acl.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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. if ok {
  63. ret.Username = getMetdataKeyOrEmpty(md, "username")
  64. ret.Usergroup = getMetdataKeyOrEmpty(md, "usergroup")
  65. }
  66. buildUserAcls(cfg, ret)
  67. log.WithFields(log.Fields{
  68. "username": ret.Username,
  69. "usergroup": ret.Usergroup,
  70. }).Infof("UserFromContext")
  71. return ret
  72. }
  73. func buildUserAcls(cfg *config.Config, user *AuthenticatedUser) {
  74. for _, acl := range cfg.AccessControlLists {
  75. if slices.Contains(acl.MatchUsernames, user.Username) {
  76. user.acls = append(user.acls, acl.Name)
  77. continue
  78. }
  79. if slices.Contains(acl.MatchUsergroups, user.Usergroup) {
  80. user.acls = append(user.acls, acl.Name)
  81. continue
  82. }
  83. }
  84. }
  85. func isACLRelevant(cfg *config.Config, actionAcls []string, acl config.AccessControlList, user *AuthenticatedUser) bool {
  86. if !slices.Contains(user.acls, acl.Name) {
  87. return false
  88. }
  89. if acl.AddToEveryAction {
  90. return true
  91. }
  92. if slices.Contains(actionAcls, acl.Name) {
  93. return true
  94. }
  95. return false
  96. }
  97. func getRelevantAcls(cfg *config.Config, actionAcls []string, user *AuthenticatedUser) []*config.AccessControlList {
  98. var ret []*config.AccessControlList
  99. for _, acl := range cfg.AccessControlLists {
  100. if isACLRelevant(cfg, actionAcls, acl, user) {
  101. ret = append(ret, &acl)
  102. }
  103. }
  104. return ret
  105. }