acl.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package acl
  2. import (
  3. "context"
  4. config "github.com/OliveTin/OliveTin/internal/config"
  5. log "github.com/sirupsen/logrus"
  6. "golang.org/x/exp/slices"
  7. "google.golang.org/grpc/metadata"
  8. )
  9. // User respresents a person.
  10. type AuthenticatedUser struct {
  11. Username string
  12. Usergroup string
  13. acls []string
  14. }
  15. // IsAllowedExec checks if a AuthenticatedUser is allowed to execute an Action
  16. func IsAllowedExec(cfg *config.Config, user *AuthenticatedUser, action *config.Action) bool {
  17. for _, acl := range getRelevantAcls(cfg, action.Acls, user) {
  18. if acl.Permissions.Exec {
  19. log.WithFields(log.Fields{
  20. "User": user.Username,
  21. "Action": action.Title,
  22. "ACL": acl.Name,
  23. }).Debug("isAllowedExec - Matched ACL")
  24. return true
  25. }
  26. }
  27. log.WithFields(log.Fields{
  28. "User": user.Username,
  29. "Action": action.Title,
  30. }).Debug("isAllowedExec - No ACLs matched")
  31. return cfg.DefaultPermissions.Exec
  32. }
  33. // IsAllowedView checks if a User is allowed to view an Action
  34. func IsAllowedView(cfg *config.Config, user *AuthenticatedUser, action *config.Action) bool {
  35. for _, acl := range getRelevantAcls(cfg, action.Acls, user) {
  36. if acl.Permissions.View {
  37. log.WithFields(log.Fields{
  38. "User": user.Username,
  39. "Action": action.Title,
  40. "ACL": acl.Name,
  41. }).Debug("isAllowedView - Matched ACL")
  42. return true
  43. }
  44. }
  45. log.WithFields(log.Fields{
  46. "User": user.Username,
  47. "Action": action.Title,
  48. }).Debug("isAllowedView - No ACLs matched")
  49. return cfg.DefaultPermissions.View
  50. }
  51. func getMetdataKeyOrEmpty(md metadata.MD, key string) string {
  52. mdValues := md.Get(key)
  53. if len(mdValues) > 0 {
  54. return mdValues[0]
  55. }
  56. return ""
  57. }
  58. // UserFromContext tries to find a user from a grpc context
  59. func UserFromContext(ctx context.Context, cfg *config.Config) *AuthenticatedUser {
  60. md, ok := metadata.FromIncomingContext(ctx)
  61. ret := &AuthenticatedUser{
  62. Username: "guest",
  63. Usergroup: "guest",
  64. }
  65. if ok {
  66. ret.Username = getMetdataKeyOrEmpty(md, "username")
  67. ret.Usergroup = getMetdataKeyOrEmpty(md, "usergroup")
  68. }
  69. buildUserAcls(cfg, ret)
  70. log.WithFields(log.Fields{
  71. "username": ret.Username,
  72. "usergroup": ret.Usergroup,
  73. }).Debugf("UserFromContext")
  74. return ret
  75. }
  76. func buildUserAcls(cfg *config.Config, user *AuthenticatedUser) {
  77. for _, acl := range cfg.AccessControlLists {
  78. if slices.Contains(acl.MatchUsernames, user.Username) {
  79. user.acls = append(user.acls, acl.Name)
  80. continue
  81. }
  82. if slices.Contains(acl.MatchUsergroups, user.Usergroup) {
  83. user.acls = append(user.acls, acl.Name)
  84. continue
  85. }
  86. }
  87. }
  88. func isACLRelevant(cfg *config.Config, actionAcls []string, acl config.AccessControlList, user *AuthenticatedUser) bool {
  89. if !slices.Contains(user.acls, acl.Name) {
  90. return false
  91. }
  92. if acl.AddToEveryAction {
  93. return true
  94. }
  95. if slices.Contains(actionAcls, acl.Name) {
  96. return true
  97. }
  98. return false
  99. }
  100. func getRelevantAcls(cfg *config.Config, actionAcls []string, user *AuthenticatedUser) []*config.AccessControlList {
  101. var ret []*config.AccessControlList
  102. for _, acl := range cfg.AccessControlLists {
  103. if isACLRelevant(cfg, actionAcls, acl, user) {
  104. ret = append(ret, &acl)
  105. }
  106. }
  107. return ret
  108. }