types.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Package user deals with authentication and authorization against topics
  2. package user
  3. import (
  4. "errors"
  5. "regexp"
  6. )
  7. // Manager is a generic interface to implement password and token based authentication and authorization
  8. type Manager interface {
  9. // Authenticate checks username and password and returns a user if correct. The method
  10. // returns in constant-ish time, regardless of whether the user exists or the password is
  11. // correct or incorrect.
  12. Authenticate(username, password string) (*User, error)
  13. AuthenticateToken(token string) (*User, error)
  14. CreateToken(user *User) (*Token, error)
  15. ExtendToken(user *User) (*Token, error)
  16. RemoveToken(user *User) error
  17. RemoveExpiredTokens() error
  18. ChangeSettings(user *User) error
  19. EnqueueStats(user *User)
  20. // Authorize returns nil if the given user has access to the given topic using the desired
  21. // permission. The user param may be nil to signal an anonymous user.
  22. Authorize(user *User, topic string, perm Permission) error
  23. // AddUser adds a user with the given username, password and role. The password should be hashed
  24. // before it is stored in a persistence layer.
  25. AddUser(username, password string, role Role) error
  26. // RemoveUser deletes the user with the given username. The function returns nil on success, even
  27. // if the user did not exist in the first place.
  28. RemoveUser(username string) error
  29. // Users returns a list of users. It always also returns the Everyone user ("*").
  30. Users() ([]*User, error)
  31. // User returns the user with the given username if it exists, or ErrNotFound otherwise.
  32. // You may also pass Everyone to retrieve the anonymous user and its Grant list.
  33. User(username string) (*User, error)
  34. // ChangePassword changes a user's password
  35. ChangePassword(username, password string) error
  36. // ChangeRole changes a user's role. When a role is changed from RoleUser to RoleAdmin,
  37. // all existing access control entries (Grant) are removed, since they are no longer needed.
  38. ChangeRole(username string, role Role) error
  39. // AllowAccess adds or updates an entry in th access control list for a specific user. It controls
  40. // read/write access to a topic. The parameter topicPattern may include wildcards (*).
  41. AllowAccess(username string, topicPattern string, read bool, write bool) error
  42. // ResetAccess removes an access control list entry for a specific username/topic, or (if topic is
  43. // empty) for an entire user. The parameter topicPattern may include wildcards (*).
  44. ResetAccess(username string, topicPattern string) error
  45. // DefaultAccess returns the default read/write access if no access control entry matches
  46. DefaultAccess() (read bool, write bool)
  47. }
  48. // User is a struct that represents a user
  49. type User struct {
  50. Name string
  51. Hash string // password hash (bcrypt)
  52. Token string // Only set if token was used to log in
  53. Role Role
  54. Grants []Grant
  55. Prefs *Prefs
  56. Plan *Plan
  57. Stats *Stats
  58. }
  59. type Token struct {
  60. Value string
  61. Expires int64
  62. }
  63. type Prefs struct {
  64. Language string `json:"language,omitempty"`
  65. Notification *NotificationPrefs `json:"notification,omitempty"`
  66. Subscriptions []*Subscription `json:"subscriptions,omitempty"`
  67. }
  68. type PlanCode string
  69. const (
  70. PlanUnlimited = PlanCode("unlimited")
  71. PlanDefault = PlanCode("default")
  72. PlanNone = PlanCode("none")
  73. )
  74. type Plan struct {
  75. Code string `json:"name"`
  76. Upgradable bool `json:"upgradable"`
  77. MessagesLimit int64 `json:"messages_limit"`
  78. EmailsLimit int64 `json:"emails_limit"`
  79. AttachmentFileSizeLimit int64 `json:"attachment_file_size_limit"`
  80. AttachmentTotalSizeLimit int64 `json:"attachment_total_size_limit"`
  81. }
  82. type Subscription struct {
  83. ID string `json:"id"`
  84. BaseURL string `json:"base_url"`
  85. Topic string `json:"topic"`
  86. DisplayName string `json:"display_name"`
  87. }
  88. type NotificationPrefs struct {
  89. Sound string `json:"sound,omitempty"`
  90. MinPriority int `json:"min_priority,omitempty"`
  91. DeleteAfter int `json:"delete_after,omitempty"`
  92. }
  93. type Stats struct {
  94. Messages int64
  95. Emails int64
  96. }
  97. // Grant is a struct that represents an access control entry to a topic
  98. type Grant struct {
  99. TopicPattern string // May include wildcard (*)
  100. AllowRead bool
  101. AllowWrite bool
  102. }
  103. // Permission represents a read or write permission to a topic
  104. type Permission int
  105. // Permissions to a topic
  106. const (
  107. PermissionRead = Permission(1)
  108. PermissionWrite = Permission(2)
  109. )
  110. // Role represents a user's role, either admin or regular user
  111. type Role string
  112. // User roles
  113. const (
  114. RoleAdmin = Role("admin")
  115. RoleUser = Role("user")
  116. RoleAnonymous = Role("anonymous")
  117. )
  118. // Everyone is a special username representing anonymous users
  119. const (
  120. Everyone = "*"
  121. )
  122. var (
  123. allowedUsernameRegex = regexp.MustCompile(`^[-_.@a-zA-Z0-9]+$`) // Does not include Everyone (*)
  124. allowedTopicPatternRegex = regexp.MustCompile(`^[-_*A-Za-z0-9]{1,64}$`) // Adds '*' for wildcards!
  125. )
  126. // AllowedRole returns true if the given role can be used for new users
  127. func AllowedRole(role Role) bool {
  128. return role == RoleUser || role == RoleAdmin
  129. }
  130. // AllowedUsername returns true if the given username is valid
  131. func AllowedUsername(username string) bool {
  132. return allowedUsernameRegex.MatchString(username)
  133. }
  134. // AllowedTopicPattern returns true if the given topic pattern is valid; this includes the wildcard character (*)
  135. func AllowedTopicPattern(username string) bool {
  136. return allowedTopicPatternRegex.MatchString(username)
  137. }
  138. // Error constants used by the package
  139. var (
  140. ErrUnauthenticated = errors.New("unauthenticated")
  141. ErrUnauthorized = errors.New("unauthorized")
  142. ErrInvalidArgument = errors.New("invalid argument")
  143. ErrNotFound = errors.New("not found")
  144. )