Files
core/iam/identity/user.go
2023-09-18 15:18:21 +02:00

262 lines
5.8 KiB
Go

package identity
import (
"fmt"
"strings"
"time"
"github.com/datarhei/core/v16/slices"
)
type User struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Name string `json:"name"`
Alias string `json:"alias"`
Superuser bool `json:"superuser"`
Auth UserAuth `json:"auth"`
}
type UserAuth struct {
API UserAuthAPI `json:"api"`
Services UserAuthServices `json:"services"`
}
type UserAuthAPI struct {
Password string `json:"password"`
Auth0 UserAuthAPIAuth0 `json:"auth0"`
}
type UserAuthAPIAuth0 struct {
User string `json:"user"`
Tenant Auth0Tenant `json:"tenant"`
}
type UserAuthServices struct {
Basic []string `json:"basic"` // Passwords for BasicAuth
Token []string `json:"token"` // Tokens/Streamkey for RTMP and SRT
Session []string `json:"session"` // Secrets for session JWT
}
func (u *User) Validate() error {
if len(u.Name) == 0 {
return fmt.Errorf("a name is required")
}
if strings.HasPrefix(u.Name, "$") {
return fmt.Errorf("name is not allowed to start with $")
}
if len(u.Alias) != 0 {
if strings.HasPrefix(u.Alias, "$") {
return fmt.Errorf("alias is not allowed to start with $")
}
}
if len(u.Auth.API.Auth0.User) != 0 {
t, err := newAuth0Tenant(u.Auth.API.Auth0.Tenant)
if err != nil {
return fmt.Errorf("auth0: %w", err)
}
t.Cancel()
}
return nil
}
func (u *User) marshalIdentity() *identity {
i := &identity{
user: u.clone(),
}
return i
}
func (u *User) clone() User {
user := *u
user.Auth.Services.Basic = slices.Copy(u.Auth.Services.Basic)
user.Auth.Services.Token = slices.Copy(u.Auth.Services.Token)
user.Auth.Services.Session = slices.Copy(u.Auth.Services.Session)
return user
}
type UserList interface {
Add(u User) error
Get(nameOrAlias string) (User, error)
Update(nameOrAlias string, u User) error
Delete(nameorAlias string)
List() []User
}
type userlist struct {
namesUserMap map[string]string
auth0UserMap map[string]string
user map[string]User
}
func NewUserList() UserList {
return &userlist{
namesUserMap: map[string]string{},
auth0UserMap: map[string]string{},
user: map[string]User{},
}
}
// Add implements UserList.
func (ul *userlist) Add(u User) error {
if err := u.Validate(); err != nil {
return fmt.Errorf("invalid user: %w", err)
}
if _, ok := ul.namesUserMap[u.Name]; ok {
return fmt.Errorf("the name '%s' is already in use", u.Name)
}
if len(u.Alias) != 0 {
if _, ok := ul.namesUserMap[u.Alias]; ok {
return fmt.Errorf("the alias '%s' is already in use", u.Alias)
}
}
if len(u.Auth.API.Auth0.User) != 0 {
if name, ok := ul.auth0UserMap[u.Auth.API.Auth0.User]; ok {
return fmt.Errorf("the Auth0 user has already an identity (%s)", name)
}
}
u = u.clone()
ul.namesUserMap[u.Name] = u.Name
if len(u.Alias) != 0 {
ul.namesUserMap[u.Alias] = u.Name
}
if len(u.Auth.API.Auth0.User) != 0 {
ul.auth0UserMap[u.Auth.API.Auth0.User] = u.Name
}
ul.user[u.Name] = u
return nil
}
func (ul *userlist) Get(nameOrAlias string) (User, error) {
name, ok := ul.namesUserMap[nameOrAlias]
if !ok {
return User{}, fmt.Errorf("user not found")
}
u, ok := ul.user[name]
if !ok {
return User{}, fmt.Errorf("user not found")
}
return u.clone(), nil
}
// Delete implements UserList.
func (ul *userlist) Delete(nameOrAlias string) {
name, ok := ul.namesUserMap[nameOrAlias]
if !ok {
return
}
u, ok := ul.user[name]
if !ok {
delete(ul.namesUserMap, nameOrAlias)
return
}
delete(ul.namesUserMap, u.Name)
delete(ul.namesUserMap, u.Alias)
delete(ul.auth0UserMap, u.Auth.API.Auth0.User)
delete(ul.user, u.Name)
}
// List implements UserList.
func (ul *userlist) List() []User {
user := []User{}
for _, u := range ul.user {
user = append(user, u.clone())
}
return user
}
// Update implements UserList.
func (ul *userlist) Update(nameOrAlias string, u User) error {
if err := u.Validate(); err != nil {
return fmt.Errorf("invalid user: %w", err)
}
name, ok := ul.namesUserMap[nameOrAlias]
if !ok {
return fmt.Errorf("user with the name or alias '%s' not found", nameOrAlias)
}
oldUser, ok := ul.user[name]
if !ok {
return fmt.Errorf("user with the name '%s' not found", name)
}
delete(ul.namesUserMap, oldUser.Name)
delete(ul.namesUserMap, oldUser.Alias)
delete(ul.auth0UserMap, oldUser.Auth.API.Auth0.User)
if _, ok := ul.namesUserMap[u.Name]; ok {
ul.namesUserMap[oldUser.Name] = oldUser.Name
if len(oldUser.Alias) != 0 {
ul.namesUserMap[oldUser.Alias] = oldUser.Name
}
if len(oldUser.Auth.API.Auth0.User) != 0 {
ul.auth0UserMap[oldUser.Auth.API.Auth0.User] = oldUser.Name
}
return fmt.Errorf("the name '%s' is already in use", u.Name)
}
if len(u.Alias) != 0 {
if _, ok := ul.namesUserMap[u.Alias]; ok {
ul.namesUserMap[oldUser.Name] = oldUser.Name
if len(oldUser.Alias) != 0 {
ul.namesUserMap[oldUser.Alias] = oldUser.Name
}
if len(oldUser.Auth.API.Auth0.User) != 0 {
ul.auth0UserMap[oldUser.Auth.API.Auth0.User] = oldUser.Name
}
return fmt.Errorf("the alias '%s' is already in use", u.Alias)
}
}
if len(u.Auth.API.Auth0.User) != 0 {
if _, ok := ul.auth0UserMap[u.Auth.API.Auth0.User]; ok {
ul.namesUserMap[oldUser.Name] = oldUser.Name
if len(oldUser.Alias) != 0 {
ul.namesUserMap[oldUser.Alias] = oldUser.Name
}
if len(oldUser.Auth.API.Auth0.User) != 0 {
ul.auth0UserMap[oldUser.Auth.API.Auth0.User] = oldUser.Name
}
return fmt.Errorf("the Auth0 user has already an identity (%s)", u.Auth.API.Auth0.User)
}
}
delete(ul.user, oldUser.Name)
u = u.clone()
ul.namesUserMap[u.Name] = u.Name
if len(u.Alias) != 0 {
ul.namesUserMap[u.Alias] = u.Name
}
if len(u.Auth.API.Auth0.User) != 0 {
ul.auth0UserMap[u.Auth.API.Auth0.User] = u.Name
}
ul.user[u.Name] = u
return nil
}