ntfy/user/types.go

153 lines
4.5 KiB
Go
Raw Normal View History

2022-12-26 04:29:55 +01:00
// Package user deals with authentication and authorization against topics
package user
import (
"errors"
"regexp"
2022-12-28 19:46:18 +01:00
"time"
2022-12-26 04:29:55 +01:00
)
// User is a struct that represents a user
type User struct {
Name string
Hash string // password hash (bcrypt)
Token string // Only set if token was used to log in
Role Role
Grants []Grant
Prefs *Prefs
Plan *Plan
Stats *Stats
}
2022-12-29 04:16:11 +01:00
// Auther is an interface for authentication and authorization
type Auther interface {
// 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)
// 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.
Authorize(user *User, topic string, perm Permission) error
}
// Token represents a user token, including expiry date
2022-12-26 04:29:55 +01:00
type Token struct {
Value string
2022-12-28 19:46:18 +01:00
Expires time.Time
2022-12-26 04:29:55 +01:00
}
2022-12-29 04:16:11 +01:00
// Prefs represents a user's configuration settings
2022-12-26 04:29:55 +01:00
type Prefs struct {
Language string `json:"language,omitempty"`
Notification *NotificationPrefs `json:"notification,omitempty"`
Subscriptions []*Subscription `json:"subscriptions,omitempty"`
}
2022-12-29 04:16:11 +01:00
// PlanCode is code identifying a user's plan
2022-12-26 04:29:55 +01:00
type PlanCode string
2022-12-29 04:16:11 +01:00
// Default plan codes
2022-12-26 04:29:55 +01:00
const (
PlanUnlimited = PlanCode("unlimited")
PlanDefault = PlanCode("default")
PlanNone = PlanCode("none")
)
2022-12-29 04:16:11 +01:00
// Plan represents a user's account type, including its account limits
2022-12-26 04:29:55 +01:00
type Plan struct {
Code string `json:"name"`
Upgradable bool `json:"upgradable"`
MessagesLimit int64 `json:"messages_limit"`
EmailsLimit int64 `json:"emails_limit"`
AttachmentFileSizeLimit int64 `json:"attachment_file_size_limit"`
AttachmentTotalSizeLimit int64 `json:"attachment_total_size_limit"`
}
2022-12-29 04:16:11 +01:00
// Subscription represents a user's topic subscription
2022-12-26 04:29:55 +01:00
type Subscription struct {
ID string `json:"id"`
BaseURL string `json:"base_url"`
Topic string `json:"topic"`
DisplayName string `json:"display_name"`
}
2022-12-29 04:16:11 +01:00
// NotificationPrefs represents the user's notification settings
2022-12-26 04:29:55 +01:00
type NotificationPrefs struct {
Sound string `json:"sound,omitempty"`
MinPriority int `json:"min_priority,omitempty"`
DeleteAfter int `json:"delete_after,omitempty"`
}
2022-12-29 04:16:11 +01:00
// Stats is a struct holding daily user statistics
2022-12-26 04:29:55 +01:00
type Stats struct {
Messages int64
Emails int64
}
// Grant is a struct that represents an access control entry to a topic
type Grant struct {
TopicPattern string // May include wildcard (*)
AllowRead bool
AllowWrite bool
2023-01-01 21:21:43 +01:00
Owner bool // This user owns this ACL entry
2022-12-26 04:29:55 +01:00
}
// Permission represents a read or write permission to a topic
type Permission int
// Permissions to a topic
const (
PermissionRead = Permission(1)
PermissionWrite = Permission(2)
)
// Role represents a user's role, either admin or regular user
type Role string
// User roles
const (
2022-12-28 19:28:28 +01:00
RoleAdmin = Role("admin") // Some queries have these values hardcoded!
2022-12-26 04:29:55 +01:00
RoleUser = Role("user")
RoleAnonymous = Role("anonymous")
)
// Everyone is a special username representing anonymous users
const (
Everyone = "*"
)
var (
allowedUsernameRegex = regexp.MustCompile(`^[-_.@a-zA-Z0-9]+$`) // Does not include Everyone (*)
2023-01-01 21:21:43 +01:00
allowedTopicRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`) // No '*'
2022-12-26 04:29:55 +01:00
allowedTopicPatternRegex = regexp.MustCompile(`^[-_*A-Za-z0-9]{1,64}$`) // Adds '*' for wildcards!
)
// AllowedRole returns true if the given role can be used for new users
func AllowedRole(role Role) bool {
return role == RoleUser || role == RoleAdmin
}
// AllowedUsername returns true if the given username is valid
func AllowedUsername(username string) bool {
return allowedUsernameRegex.MatchString(username)
}
2023-01-01 21:21:43 +01:00
// AllowedTopic returns true if the given topic name is valid
func AllowedTopic(username string) bool {
return allowedTopicRegex.MatchString(username)
}
2022-12-26 04:29:55 +01:00
// AllowedTopicPattern returns true if the given topic pattern is valid; this includes the wildcard character (*)
func AllowedTopicPattern(username string) bool {
return allowedTopicPatternRegex.MatchString(username)
}
// Error constants used by the package
var (
ErrUnauthenticated = errors.New("unauthenticated")
ErrUnauthorized = errors.New("unauthorized")
ErrInvalidArgument = errors.New("invalid argument")
ErrNotFound = errors.New("not found")
)