mirror of
https://github.com/netbirdio/netbird.git
synced 2025-06-20 01:38:41 +02:00
Add auto-assign groups to the User API (#467)
This commit is contained in:
parent
c75ffd0f4b
commit
518a2561a2
@ -39,6 +39,7 @@ type AccountManager interface {
|
|||||||
autoGroups []string,
|
autoGroups []string,
|
||||||
) (*SetupKey, error)
|
) (*SetupKey, error)
|
||||||
SaveSetupKey(accountID string, key *SetupKey) (*SetupKey, error)
|
SaveSetupKey(accountID string, key *SetupKey) (*SetupKey, error)
|
||||||
|
SaveUser(accountID string, key *User) (*UserInfo, error)
|
||||||
GetSetupKey(accountID, keyID string) (*SetupKey, error)
|
GetSetupKey(accountID, keyID string) (*SetupKey, error)
|
||||||
GetAccountById(accountId string) (*Account, error)
|
GetAccountById(accountId string) (*Account, error)
|
||||||
GetAccountByUserOrAccountId(userId, accountId, domain string) (*Account, error)
|
GetAccountByUserOrAccountId(userId, accountId, domain string) (*Account, error)
|
||||||
@ -107,10 +108,11 @@ type Account struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
|
AutoGroups []string `json:"auto_groups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Account) Copy() *Account {
|
func (a *Account) Copy() *Account {
|
||||||
@ -300,19 +302,25 @@ func (am *DefaultAccountManager) updateIDPMetadata(userId, accountID string) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeLocalAndQueryUser(queried idp.UserData, local User) *UserInfo {
|
|
||||||
return &UserInfo{
|
|
||||||
ID: local.Id,
|
|
||||||
Email: queried.Email,
|
|
||||||
Name: queried.Name,
|
|
||||||
Role: string(local.Role),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (am *DefaultAccountManager) loadFromCache(_ context.Context, accountID interface{}) (interface{}, error) {
|
func (am *DefaultAccountManager) loadFromCache(_ context.Context, accountID interface{}) (interface{}, error) {
|
||||||
return am.idpManager.GetAccount(fmt.Sprintf("%v", accountID))
|
return am.idpManager.GetAccount(fmt.Sprintf("%v", accountID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (am *DefaultAccountManager) lookupUserInCache(user *User, accountID string) (*idp.UserData, error) {
|
||||||
|
userData, err := am.lookupCache(map[string]*User{user.Id: user}, accountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, datum := range userData {
|
||||||
|
if datum.ID == user.Id {
|
||||||
|
return datum, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, status.Errorf(codes.NotFound, "user %s not found in the IdP", user.Id)
|
||||||
|
}
|
||||||
|
|
||||||
func (am *DefaultAccountManager) lookupCache(accountUsers map[string]*User, accountID string) ([]*idp.UserData, error) {
|
func (am *DefaultAccountManager) lookupCache(accountUsers map[string]*User, accountID string) ([]*idp.UserData, error) {
|
||||||
data, err := am.cacheManager.Get(am.ctx, accountID)
|
data, err := am.cacheManager.Get(am.ctx, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -352,46 +360,6 @@ func (am *DefaultAccountManager) lookupCache(accountUsers map[string]*User, acco
|
|||||||
return userData, err
|
return userData, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUsersFromAccount performs a batched request for users from IDP by account id
|
|
||||||
func (am *DefaultAccountManager) GetUsersFromAccount(accountID string) ([]*UserInfo, error) {
|
|
||||||
account, err := am.GetAccountById(accountID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
queriedUsers := make([]*idp.UserData, 0)
|
|
||||||
if !isNil(am.idpManager) {
|
|
||||||
queriedUsers, err = am.lookupCache(account.Users, accountID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
userInfo := make([]*UserInfo, 0)
|
|
||||||
|
|
||||||
// in case of self-hosted, or IDP doesn't return anything, we will return the locally stored userInfo
|
|
||||||
if len(queriedUsers) == 0 {
|
|
||||||
for _, user := range account.Users {
|
|
||||||
userInfo = append(userInfo, &UserInfo{
|
|
||||||
ID: user.Id,
|
|
||||||
Email: "",
|
|
||||||
Name: "",
|
|
||||||
Role: string(user.Role),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return userInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, queriedUser := range queriedUsers {
|
|
||||||
if localUser, contains := account.Users[queriedUser.ID]; contains {
|
|
||||||
userInfo = append(userInfo, mergeLocalAndQueryUser(*queriedUser, *localUser))
|
|
||||||
log.Debugf("Merged userinfo to send back; %v", userInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return userInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateAccountDomainAttributes updates the account domain attributes and then, saves the account
|
// updateAccountDomainAttributes updates the account domain attributes and then, saves the account
|
||||||
func (am *DefaultAccountManager) updateAccountDomainAttributes(
|
func (am *DefaultAccountManager) updateAccountDomainAttributes(
|
||||||
account *Account,
|
account *Account,
|
||||||
|
@ -31,13 +31,33 @@ components:
|
|||||||
description: User's name from idp provider
|
description: User's name from idp provider
|
||||||
type: string
|
type: string
|
||||||
role:
|
role:
|
||||||
description: User's Netbird account role
|
description: User's NetBird account role
|
||||||
type: string
|
type: string
|
||||||
|
auto_groups:
|
||||||
|
description: Groups to auto-assign to peers registered by this user
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- email
|
- email
|
||||||
- name
|
- name
|
||||||
- role
|
- role
|
||||||
|
- auto_groups
|
||||||
|
UserRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
auto_groups:
|
||||||
|
description: Groups to auto-assign to peers registered by this user
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- type
|
||||||
|
- expires_in
|
||||||
|
- revoked
|
||||||
|
- auto_groups
|
||||||
PeerMinimum:
|
PeerMinimum:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -409,6 +429,40 @@ paths:
|
|||||||
"$ref": "#/components/responses/forbidden"
|
"$ref": "#/components/responses/forbidden"
|
||||||
'500':
|
'500':
|
||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/users/{id}:
|
||||||
|
put:
|
||||||
|
summary: Update information about a User
|
||||||
|
tags: [ Users]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The User ID
|
||||||
|
requestBody:
|
||||||
|
description: User update
|
||||||
|
content:
|
||||||
|
'application/json':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A User object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
/api/peers:
|
/api/peers:
|
||||||
get:
|
get:
|
||||||
summary: Returns a list of all peers
|
summary: Returns a list of all peers
|
||||||
|
@ -356,6 +356,9 @@ type SetupKeyRequest struct {
|
|||||||
|
|
||||||
// User defines model for User.
|
// User defines model for User.
|
||||||
type User struct {
|
type User struct {
|
||||||
|
// Groups to auto-assign to peers registered by this user
|
||||||
|
AutoGroups []string `json:"auto_groups"`
|
||||||
|
|
||||||
// User's email address
|
// User's email address
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
|
||||||
@ -365,10 +368,16 @@ type User struct {
|
|||||||
// User's name from idp provider
|
// User's name from idp provider
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
// User's Netbird account role
|
// User's NetBird account role
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserRequest defines model for UserRequest.
|
||||||
|
type UserRequest struct {
|
||||||
|
// Groups to auto-assign to peers registered by this user
|
||||||
|
AutoGroups []string `json:"auto_groups"`
|
||||||
|
}
|
||||||
|
|
||||||
// PostApiGroupsJSONBody defines parameters for PostApiGroups.
|
// PostApiGroupsJSONBody defines parameters for PostApiGroups.
|
||||||
type PostApiGroupsJSONBody struct {
|
type PostApiGroupsJSONBody struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@ -442,6 +451,9 @@ type PostApiSetupKeysJSONBody = SetupKeyRequest
|
|||||||
// PutApiSetupKeysIdJSONBody defines parameters for PutApiSetupKeysId.
|
// PutApiSetupKeysIdJSONBody defines parameters for PutApiSetupKeysId.
|
||||||
type PutApiSetupKeysIdJSONBody = SetupKeyRequest
|
type PutApiSetupKeysIdJSONBody = SetupKeyRequest
|
||||||
|
|
||||||
|
// PutApiUsersIdJSONBody defines parameters for PutApiUsersId.
|
||||||
|
type PutApiUsersIdJSONBody = UserRequest
|
||||||
|
|
||||||
// PostApiGroupsJSONRequestBody defines body for PostApiGroups for application/json ContentType.
|
// PostApiGroupsJSONRequestBody defines body for PostApiGroups for application/json ContentType.
|
||||||
type PostApiGroupsJSONRequestBody PostApiGroupsJSONBody
|
type PostApiGroupsJSONRequestBody PostApiGroupsJSONBody
|
||||||
|
|
||||||
@ -477,3 +489,6 @@ type PostApiSetupKeysJSONRequestBody = PostApiSetupKeysJSONBody
|
|||||||
|
|
||||||
// PutApiSetupKeysIdJSONRequestBody defines body for PutApiSetupKeysId for application/json ContentType.
|
// PutApiSetupKeysIdJSONRequestBody defines body for PutApiSetupKeysId for application/json ContentType.
|
||||||
type PutApiSetupKeysIdJSONRequestBody = PutApiSetupKeysIdJSONBody
|
type PutApiSetupKeysIdJSONRequestBody = PutApiSetupKeysIdJSONBody
|
||||||
|
|
||||||
|
// PutApiUsersIdJSONRequestBody defines body for PutApiUsersId for application/json ContentType.
|
||||||
|
type PutApiUsersIdJSONRequestBody = PutApiUsersIdJSONBody
|
||||||
|
@ -39,6 +39,7 @@ func APIHandler(accountManager s.AccountManager, authIssuer string, authAudience
|
|||||||
apiHandler.HandleFunc("/api/peers/{id}", peersHandler.HandlePeer).
|
apiHandler.HandleFunc("/api/peers/{id}", peersHandler.HandlePeer).
|
||||||
Methods("GET", "PUT", "DELETE", "OPTIONS")
|
Methods("GET", "PUT", "DELETE", "OPTIONS")
|
||||||
apiHandler.HandleFunc("/api/users", userHandler.GetUsers).Methods("GET", "OPTIONS")
|
apiHandler.HandleFunc("/api/users", userHandler.GetUsers).Methods("GET", "OPTIONS")
|
||||||
|
apiHandler.HandleFunc("/api/users/{id}", userHandler.UpdateUser).Methods("PUT", "OPTIONS")
|
||||||
|
|
||||||
apiHandler.HandleFunc("/api/setup-keys", keysHandler.GetAllSetupKeysHandler).Methods("GET", "OPTIONS")
|
apiHandler.HandleFunc("/api/setup-keys", keysHandler.GetAllSetupKeysHandler).Methods("GET", "OPTIONS")
|
||||||
apiHandler.HandleFunc("/api/setup-keys", keysHandler.CreateSetupKeyHandler).Methods("POST", "OPTIONS")
|
apiHandler.HandleFunc("/api/setup-keys", keysHandler.CreateSetupKeyHandler).Methods("POST", "OPTIONS")
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -24,6 +29,52 @@ func NewUserHandler(accountManager server.AccountManager, authAudience string) *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUser is a PUT requests to update User data
|
||||||
|
func (h *UserHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPut {
|
||||||
|
http.Error(w, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
http.Redirect(w, r, "/", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
userID := vars["id"]
|
||||||
|
if len(userID) == 0 {
|
||||||
|
http.Error(w, "invalid key Id", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &api.PutApiUsersIdJSONRequestBody{}
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newUser, err := h.accountManager.SaveUser(account.Id, &server.User{
|
||||||
|
Id: userID,
|
||||||
|
AutoGroups: req.AutoGroups,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if e, ok := status.FromError(err); ok {
|
||||||
|
switch e.Code() {
|
||||||
|
case codes.NotFound:
|
||||||
|
http.Error(w, fmt.Sprintf("couldn't find a user for ID %s", userID), http.StatusNotFound)
|
||||||
|
default:
|
||||||
|
http.Error(w, "failed to update user", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeJSONObject(w, toUserResponse(newUser))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// GetUsers returns a list of users of the account this user belongs to.
|
// GetUsers returns a list of users of the account this user belongs to.
|
||||||
// It also gathers additional user data (like email and name) from the IDP manager.
|
// It also gathers additional user data (like email and name) from the IDP manager.
|
||||||
func (h *UserHandler) GetUsers(w http.ResponseWriter, r *http.Request) {
|
func (h *UserHandler) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -52,10 +103,17 @@ func (h *UserHandler) GetUsers(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toUserResponse(user *server.UserInfo) *api.User {
|
func toUserResponse(user *server.UserInfo) *api.User {
|
||||||
|
|
||||||
|
autoGroups := user.AutoGroups
|
||||||
|
if autoGroups == nil {
|
||||||
|
autoGroups = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
return &api.User{
|
return &api.User{
|
||||||
Id: user.ID,
|
Id: user.ID,
|
||||||
Name: user.Name,
|
Name: user.Name,
|
||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
Role: user.Role,
|
Role: user.Role,
|
||||||
|
AutoGroups: autoGroups,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ type MockAccountManager struct {
|
|||||||
ListRoutesFunc func(accountID string) ([]*route.Route, error)
|
ListRoutesFunc func(accountID string) ([]*route.Route, error)
|
||||||
SaveSetupKeyFunc func(accountID string, key *server.SetupKey) (*server.SetupKey, error)
|
SaveSetupKeyFunc func(accountID string, key *server.SetupKey) (*server.SetupKey, error)
|
||||||
ListSetupKeysFunc func(accountID string) ([]*server.SetupKey, error)
|
ListSetupKeysFunc func(accountID string) ([]*server.SetupKey, error)
|
||||||
|
SaveUserFunc func(accountID string, user *server.User) (*server.UserInfo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUsersFromAccount mock implementation of GetUsersFromAccount from server.AccountManager interface
|
// GetUsersFromAccount mock implementation of GetUsersFromAccount from server.AccountManager interface
|
||||||
@ -421,3 +422,11 @@ func (am *MockAccountManager) ListSetupKeys(accountID string) ([]*server.SetupKe
|
|||||||
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ListSetupKeys is not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method ListSetupKeys is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveUser mocks SaveUser of the AccountManager interface
|
||||||
|
func (am *MockAccountManager) SaveUser(accountID string, user *server.User) (*server.UserInfo, error) {
|
||||||
|
if am.SaveUserFunc != nil {
|
||||||
|
return am.SaveUserFunc(accountID, user)
|
||||||
|
}
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SaveUser is not implemented")
|
||||||
|
}
|
||||||
|
@ -330,6 +330,13 @@ func (am *DefaultAccountManager) AddPeer(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.NotFound, "unable to register peer, unknown user with ID: %s", userID)
|
return nil, status.Errorf(codes.NotFound, "unable to register peer, unknown user with ID: %s", userID)
|
||||||
}
|
}
|
||||||
|
user, ok := account.Users[userID]
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "unable to register peer, unknown user with ID: %s", userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
groupsToAdd = user.AutoGroups
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Empty setup key and jwt fail
|
// Empty setup key and jwt fail
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "no setup key or user id provided")
|
return nil, status.Errorf(codes.InvalidArgument, "no setup key or user id provided")
|
||||||
|
@ -2,10 +2,10 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"github.com/netbirdio/netbird/management/server/idp"
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
)
|
)
|
||||||
@ -22,20 +22,56 @@ type UserRole string
|
|||||||
type User struct {
|
type User struct {
|
||||||
Id string
|
Id string
|
||||||
Role UserRole
|
Role UserRole
|
||||||
|
// AutoGroups is a list of Group IDs to auto-assign to peers registered by this user
|
||||||
|
AutoGroups []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// toUserInfo converts a User object to a UserInfo object.
|
||||||
|
func (u *User) toUserInfo(userData *idp.UserData) (*UserInfo, error) {
|
||||||
|
autoGroups := u.AutoGroups
|
||||||
|
if autoGroups == nil {
|
||||||
|
autoGroups = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if userData == nil {
|
||||||
|
return &UserInfo{
|
||||||
|
ID: u.Id,
|
||||||
|
Email: "",
|
||||||
|
Name: "",
|
||||||
|
Role: string(u.Role),
|
||||||
|
AutoGroups: u.AutoGroups,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if userData.ID != u.Id {
|
||||||
|
return nil, fmt.Errorf("wrong UserData provided for user %s", u.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &UserInfo{
|
||||||
|
ID: u.Id,
|
||||||
|
Email: userData.Email,
|
||||||
|
Name: userData.Name,
|
||||||
|
Role: string(u.Role),
|
||||||
|
AutoGroups: autoGroups,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the user
|
||||||
func (u *User) Copy() *User {
|
func (u *User) Copy() *User {
|
||||||
|
autoGroups := []string{}
|
||||||
|
autoGroups = append(autoGroups, u.AutoGroups...)
|
||||||
return &User{
|
return &User{
|
||||||
Id: u.Id,
|
Id: u.Id,
|
||||||
Role: u.Role,
|
Role: u.Role,
|
||||||
|
AutoGroups: autoGroups,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUser creates a new user
|
// NewUser creates a new user
|
||||||
func NewUser(id string, role UserRole) *User {
|
func NewUser(id string, role UserRole) *User {
|
||||||
return &User{
|
return &User{
|
||||||
Id: id,
|
Id: id,
|
||||||
Role: role,
|
Role: role,
|
||||||
|
AutoGroups: []string{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +85,54 @@ func NewAdminUser(id string) *User {
|
|||||||
return NewUser(id, UserRoleAdmin)
|
return NewUser(id, UserRoleAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveUser saves updates a given user. If the user doesn't exit it will throw status.NotFound error.
|
||||||
|
// Only User.AutoGroups field is allowed to be updated for now.
|
||||||
|
func (am *DefaultAccountManager) SaveUser(accountID string, update *User) (*UserInfo, error) {
|
||||||
|
am.mux.Lock()
|
||||||
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
|
if update == nil {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "provided user update is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := am.Store.GetAccount(accountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, newGroupID := range update.AutoGroups {
|
||||||
|
if _, ok := account.Groups[newGroupID]; !ok {
|
||||||
|
return nil,
|
||||||
|
status.Errorf(codes.InvalidArgument, "provided group ID %s in the user %s update doesn't exist",
|
||||||
|
newGroupID, update.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
oldUser := account.Users[update.Id]
|
||||||
|
if oldUser == nil {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "update not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// only auto groups, revoked status, and name can be updated for now
|
||||||
|
newUser := oldUser.Copy()
|
||||||
|
newUser.AutoGroups = update.AutoGroups
|
||||||
|
|
||||||
|
account.Users[newUser.Id] = newUser
|
||||||
|
|
||||||
|
if err = am.Store.SaveAccount(account); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isNil(am.idpManager) {
|
||||||
|
userData, err := am.lookupUserInCache(newUser, accountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newUser.toUserInfo(userData)
|
||||||
|
}
|
||||||
|
return newUser.toUserInfo(nil)
|
||||||
|
}
|
||||||
|
|
||||||
// GetOrCreateAccountByUser returns an existing account for a given user id or creates a new one if doesn't exist
|
// GetOrCreateAccountByUser returns an existing account for a given user id or creates a new one if doesn't exist
|
||||||
func (am *DefaultAccountManager) GetOrCreateAccountByUser(userId, domain string) (*Account, error) {
|
func (am *DefaultAccountManager) GetOrCreateAccountByUser(userId, domain string) (*Account, error) {
|
||||||
am.mux.Lock()
|
am.mux.Lock()
|
||||||
@ -108,3 +192,46 @@ func (am *DefaultAccountManager) IsUserAdmin(claims jwtclaims.AuthorizationClaim
|
|||||||
|
|
||||||
return user.Role == UserRoleAdmin, nil
|
return user.Role == UserRoleAdmin, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUsersFromAccount performs a batched request for users from IDP by account ID
|
||||||
|
func (am *DefaultAccountManager) GetUsersFromAccount(accountID string) ([]*UserInfo, error) {
|
||||||
|
account, err := am.GetAccountById(accountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
queriedUsers := make([]*idp.UserData, 0)
|
||||||
|
if !isNil(am.idpManager) {
|
||||||
|
queriedUsers, err = am.lookupCache(account.Users, accountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfos := make([]*UserInfo, 0)
|
||||||
|
|
||||||
|
// in case of self-hosted, or IDP doesn't return anything, we will return the locally stored userInfo
|
||||||
|
if len(queriedUsers) == 0 {
|
||||||
|
for _, user := range account.Users {
|
||||||
|
info, err := user.toUserInfo(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userInfos = append(userInfos, info)
|
||||||
|
}
|
||||||
|
return userInfos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, queriedUser := range queriedUsers {
|
||||||
|
if localUser, contains := account.Users[queriedUser.ID]; contains {
|
||||||
|
|
||||||
|
info, err := localUser.toUserInfo(queriedUser)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userInfos = append(userInfos, info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return userInfos, nil
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user