ntfy/auth/auth.go

170 lines
5.5 KiB
Go
Raw Normal View History

// Package auth deals with authentication and authorization against topics
2022-01-23 05:02:16 +00:00
package auth
2022-01-31 16:44:58 +00:00
import (
"errors"
"regexp"
)
2022-01-23 05:02:16 +00:00
2022-12-15 04:11:22 +00:00
// Manager is a generic interface to implement password and token based authentication and authorization
type Manager interface {
2022-01-26 03:30:53 +00:00
// Authenticate checks username and password and returns a user if correct. The method
// returns in constant-ish time, regardless of whether the user exists or the password is
// correct or incorrect.
Authenticate(username, password string) (*User, error)
2022-12-03 20:20:59 +00:00
AuthenticateToken(token string) (*User, error)
2022-12-08 01:44:20 +00:00
CreateToken(user *User) (string, error)
RemoveToken(user *User) error
2022-12-08 02:26:18 +00:00
ChangeSettings(user *User) error
EnqueueUpdateStats(user *User)
2022-12-03 20:20:59 +00:00
2022-01-26 03:30:53 +00:00
// Authorize returns nil if the given user has access to the given topic using the desired
// permission. The user param may be nil to signal an anonymous user.
2022-01-23 05:02:16 +00:00
Authorize(user *User, topic string, perm Permission) error
2022-01-26 03:30:53 +00:00
// AddUser adds a user with the given username, password and role. The password should be hashed
// before it is stored in a persistence layer.
2022-01-23 05:54:18 +00:00
AddUser(username, password string, role Role) error
2022-01-26 03:30:53 +00:00
// RemoveUser deletes the user with the given username. The function returns nil on success, even
// if the user did not exist in the first place.
2022-01-23 05:54:18 +00:00
RemoveUser(username string) error
2022-01-26 03:30:53 +00:00
// Users returns a list of users. It always also returns the Everyone user ("*").
2022-01-24 04:02:39 +00:00
Users() ([]*User, error)
2022-01-26 03:30:53 +00:00
// User returns the user with the given username if it exists, or ErrNotFound otherwise.
// You may also pass Everyone to retrieve the anonymous user and its Grant list.
2022-01-24 04:02:39 +00:00
User(username string) (*User, error)
2022-01-26 03:30:53 +00:00
// ChangePassword changes a user's password
2022-01-23 05:54:18 +00:00
ChangePassword(username, password string) error
2022-01-26 03:30:53 +00:00
// ChangeRole changes a user's role. When a role is changed from RoleUser to RoleAdmin,
// all existing access control entries (Grant) are removed, since they are no longer needed.
2022-01-23 20:30:30 +00:00
ChangeRole(username string, role Role) error
2022-01-26 03:30:53 +00:00
// AllowAccess adds or updates an entry in th access control list for a specific user. It controls
2022-01-31 16:44:58 +00:00
// read/write access to a topic. The parameter topicPattern may include wildcards (*).
AllowAccess(username string, topicPattern string, read bool, write bool) error
2022-01-26 03:30:53 +00:00
// ResetAccess removes an access control list entry for a specific username/topic, or (if topic is
2022-01-31 16:44:58 +00:00
// empty) for an entire user. The parameter topicPattern may include wildcards (*).
ResetAccess(username string, topicPattern string) error
2022-01-26 03:30:53 +00:00
// DefaultAccess returns the default read/write access if no access control entry matches
DefaultAccess() (read bool, write bool)
2022-01-23 05:54:18 +00:00
}
2022-01-26 03:30:53 +00:00
// User is a struct that represents a user
2022-01-23 05:02:16 +00:00
type User struct {
2022-12-08 02:26:18 +00:00
Name string
Hash string // password hash (bcrypt)
Token string // Only set if token was used to log in
Role Role
Grants []Grant
Prefs *UserPrefs
2022-12-18 19:35:05 +00:00
Plan *Plan
Stats *Stats
2022-12-08 02:26:18 +00:00
}
type UserPrefs struct {
Language string `json:"language,omitempty"`
Notification *UserNotificationPrefs `json:"notification,omitempty"`
Subscriptions []*UserSubscription `json:"subscriptions,omitempty"`
}
2022-12-18 19:35:05 +00:00
type PlanCode string
const (
PlanUnlimited = PlanCode("unlimited")
PlanDefault = PlanCode("default")
PlanNone = PlanCode("none")
)
type Plan struct {
2022-12-19 14:59:32 +00:00
Code string `json:"name"`
Upgradable bool `json:"upgradable"`
2022-12-19 21:22:13 +00:00
MessagesLimit int64 `json:"messages_limit"`
2022-12-19 14:59:32 +00:00
EmailsLimit int64 `json:"emails_limit"`
AttachmentFileSizeLimit int64 `json:"attachment_file_size_limit"`
AttachmentTotalSizeLimit int64 `json:"attachment_total_size_limit"`
2022-12-17 20:17:52 +00:00
}
2022-12-08 02:26:18 +00:00
type UserSubscription struct {
2022-12-09 01:50:48 +00:00
ID string `json:"id"`
2022-12-08 02:26:18 +00:00
BaseURL string `json:"base_url"`
Topic string `json:"topic"`
}
type UserNotificationPrefs struct {
2022-12-09 01:50:48 +00:00
Sound string `json:"sound,omitempty"`
MinPriority int `json:"min_priority,omitempty"`
DeleteAfter int `json:"delete_after,omitempty"`
2022-01-24 04:02:39 +00:00
}
type Stats struct {
Messages int64
Emails int64
}
2022-01-26 03:30:53 +00:00
// Grant is a struct that represents an access control entry to a topic
2022-01-24 04:02:39 +00:00
type Grant struct {
2022-01-31 16:47:30 +00:00
TopicPattern string // May include wildcard (*)
AllowRead bool
AllowWrite bool
2022-01-23 05:02:16 +00:00
}
2022-01-26 03:30:53 +00:00
// Permission represents a read or write permission to a topic
2022-01-23 05:02:16 +00:00
type Permission int
2022-01-26 03:30:53 +00:00
// Permissions to a topic
2022-01-23 05:02:16 +00:00
const (
PermissionRead = Permission(1)
PermissionWrite = Permission(2)
)
2022-01-26 03:30:53 +00:00
// Role represents a user's role, either admin or regular user
2022-01-23 05:02:16 +00:00
type Role string
2022-01-26 03:30:53 +00:00
// User roles
2022-01-23 05:02:16 +00:00
const (
2022-01-24 05:54:28 +00:00
RoleAdmin = Role("admin")
RoleUser = Role("user")
RoleAnonymous = Role("anonymous")
2022-01-23 05:02:16 +00:00
)
2022-01-26 03:30:53 +00:00
// Everyone is a special username representing anonymous users
2022-01-24 05:54:28 +00:00
const (
Everyone = "*"
)
2022-01-23 20:30:30 +00:00
2022-01-31 16:44:58 +00:00
var (
allowedUsernameRegex = regexp.MustCompile(`^[-_.@a-zA-Z0-9]+$`) // Does not include Everyone (*)
allowedTopicPatternRegex = regexp.MustCompile(`^[-_*A-Za-z0-9]{1,64}$`) // Adds '*' for wildcards!
)
2022-01-26 03:30:53 +00:00
// AllowedRole returns true if the given role can be used for new users
2022-01-23 20:30:30 +00:00
func AllowedRole(role Role) bool {
return role == RoleUser || role == RoleAdmin
}
2022-01-31 16:44:58 +00:00
// AllowedUsername returns true if the given username is valid
func AllowedUsername(username string) bool {
return allowedUsernameRegex.MatchString(username)
}
// AllowedTopicPattern returns true if the given topic pattern is valid; this includes the wildcard character (*)
func AllowedTopicPattern(username string) bool {
return allowedTopicPatternRegex.MatchString(username)
}
2022-01-26 03:30:53 +00:00
// Error constants used by the package
2022-01-24 04:02:39 +00:00
var (
2022-01-26 02:57:28 +00:00
ErrUnauthenticated = errors.New("unauthenticated")
ErrUnauthorized = errors.New("unauthorized")
ErrInvalidArgument = errors.New("invalid argument")
ErrNotFound = errors.New("not found")
2022-01-24 04:02:39 +00:00
)