mirror of
https://github.com/netbirdio/netbird.git
synced 2025-02-17 02:31:06 +01:00
Extends management user and group structure (#1268)
* extends user and group structure by introducing fields for issued and integration references * Add integration checks to group management to prevent groups added by integration. * Add integration checks to user management to prevent deleting user added by integration. * Fix broken user update tests * Initialize all user fields for testing * Change a serializer option to embedded for IntegrationReference in user and group models * Add issued field to user api response * Add IntegrationReference to Group in update groups handler * Set the default issued field for users in file store
This commit is contained in:
parent
6d4240a5ae
commit
c38d65ef4c
@ -36,6 +36,7 @@ const (
|
|||||||
UnknownCategory = "unknown"
|
UnknownCategory = "unknown"
|
||||||
GroupIssuedAPI = "api"
|
GroupIssuedAPI = "api"
|
||||||
GroupIssuedJWT = "jwt"
|
GroupIssuedJWT = "jwt"
|
||||||
|
GroupIssuedIntegration = "integration"
|
||||||
CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days
|
CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days
|
||||||
CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days
|
CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days
|
||||||
DefaultPeerLoginExpiration = 24 * time.Hour
|
DefaultPeerLoginExpiration = 24 * time.Hour
|
||||||
@ -195,15 +196,17 @@ 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"`
|
AutoGroups []string `json:"auto_groups"`
|
||||||
Status string `json:"-"`
|
Status string `json:"-"`
|
||||||
IsServiceUser bool `json:"is_service_user"`
|
IsServiceUser bool `json:"is_service_user"`
|
||||||
IsBlocked bool `json:"is_blocked"`
|
IsBlocked bool `json:"is_blocked"`
|
||||||
LastLogin time.Time `json:"last_login"`
|
LastLogin time.Time `json:"last_login"`
|
||||||
|
Issued string `json:"issued"`
|
||||||
|
IntegrationReference IntegrationReference `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRoutesToSync returns the enabled routes for the peer ID and the routes
|
// getRoutesToSync returns the enabled routes for the peer ID and the routes
|
||||||
|
@ -133,6 +133,11 @@ func restore(file string) (*FileStore, error) {
|
|||||||
}
|
}
|
||||||
for _, user := range account.Users {
|
for _, user := range account.Users {
|
||||||
store.UserID2AccountID[user.Id] = accountID
|
store.UserID2AccountID[user.Id] = accountID
|
||||||
|
if user.Issued == "" {
|
||||||
|
user.Issued = UserIssuedAPI
|
||||||
|
account.Users[user.Id] = user
|
||||||
|
}
|
||||||
|
|
||||||
for _, pat := range user.PATs {
|
for _, pat := range user.PATs {
|
||||||
store.TokenID2UserID[pat.ID] = user.Id
|
store.TokenID2UserID[pat.ID] = user.Id
|
||||||
store.HashedPAT2TokenID[pat.HashedToken] = pat.ID
|
store.HashedPAT2TokenID[pat.HashedToken] = pat.ID
|
||||||
|
@ -34,6 +34,8 @@ type Group struct {
|
|||||||
|
|
||||||
// Peers list of the group
|
// Peers list of the group
|
||||||
Peers []string `gorm:"serializer:json"`
|
Peers []string `gorm:"serializer:json"`
|
||||||
|
|
||||||
|
IntegrationReference IntegrationReference `gorm:"embedded;embeddedPrefix:integration_ref_"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventMeta returns activity event meta related to the group
|
// EventMeta returns activity event meta related to the group
|
||||||
@ -160,6 +162,11 @@ func (am *DefaultAccountManager) DeleteGroup(accountId, userId, groupID string)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check integration link
|
||||||
|
if g.Issued == GroupIssuedIntegration {
|
||||||
|
return &GroupLinkError{GroupIssuedIntegration, g.IntegrationReference.String()}
|
||||||
|
}
|
||||||
|
|
||||||
// check route links
|
// check route links
|
||||||
for _, r := range account.Routes {
|
for _, r := range account.Routes {
|
||||||
for _, g := range r.Groups {
|
for _, g := range r.Groups {
|
||||||
|
@ -52,6 +52,11 @@ func TestDefaultAccountManager_DeleteGroup(t *testing.T) {
|
|||||||
"grp-for-users",
|
"grp-for-users",
|
||||||
"user",
|
"user",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"integration",
|
||||||
|
"grp-for-integration",
|
||||||
|
"integration",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
@ -79,43 +84,51 @@ func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
|
|||||||
domain := "example.com"
|
domain := "example.com"
|
||||||
|
|
||||||
groupForRoute := &Group{
|
groupForRoute := &Group{
|
||||||
"grp-for-route",
|
ID: "grp-for-route",
|
||||||
"account-id",
|
AccountID: "account-id",
|
||||||
"Group for route",
|
Name: "Group for route",
|
||||||
GroupIssuedAPI,
|
Issued: GroupIssuedAPI,
|
||||||
make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForNameServerGroups := &Group{
|
groupForNameServerGroups := &Group{
|
||||||
"grp-for-name-server-grp",
|
ID: "grp-for-name-server-grp",
|
||||||
"account-id",
|
AccountID: "account-id",
|
||||||
"Group for name server groups",
|
Name: "Group for name server groups",
|
||||||
GroupIssuedAPI,
|
Issued: GroupIssuedAPI,
|
||||||
make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForPolicies := &Group{
|
groupForPolicies := &Group{
|
||||||
"grp-for-policies",
|
ID: "grp-for-policies",
|
||||||
"account-id",
|
AccountID: "account-id",
|
||||||
"Group for policies",
|
Name: "Group for policies",
|
||||||
GroupIssuedAPI,
|
Issued: GroupIssuedAPI,
|
||||||
make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForSetupKeys := &Group{
|
groupForSetupKeys := &Group{
|
||||||
"grp-for-keys",
|
ID: "grp-for-keys",
|
||||||
"account-id",
|
AccountID: "account-id",
|
||||||
"Group for setup keys",
|
Name: "Group for setup keys",
|
||||||
GroupIssuedAPI,
|
Issued: GroupIssuedAPI,
|
||||||
make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForUsers := &Group{
|
groupForUsers := &Group{
|
||||||
"grp-for-users",
|
ID: "grp-for-users",
|
||||||
"account-id",
|
AccountID: "account-id",
|
||||||
"Group for users",
|
Name: "Group for users",
|
||||||
GroupIssuedAPI,
|
Issued: GroupIssuedAPI,
|
||||||
make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
groupForIntegration := &Group{
|
||||||
|
ID: "grp-for-integration",
|
||||||
|
AccountID: "account-id",
|
||||||
|
Name: "Group for users",
|
||||||
|
Issued: GroupIssuedIntegration,
|
||||||
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
routeResource := &route.Route{
|
routeResource := &route.Route{
|
||||||
@ -164,6 +177,7 @@ func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
|
|||||||
_ = am.SaveGroup(accountID, groupAdminUserID, groupForPolicies)
|
_ = am.SaveGroup(accountID, groupAdminUserID, groupForPolicies)
|
||||||
_ = am.SaveGroup(accountID, groupAdminUserID, groupForSetupKeys)
|
_ = am.SaveGroup(accountID, groupAdminUserID, groupForSetupKeys)
|
||||||
_ = am.SaveGroup(accountID, groupAdminUserID, groupForUsers)
|
_ = am.SaveGroup(accountID, groupAdminUserID, groupForUsers)
|
||||||
|
_ = am.SaveGroup(accountID, groupAdminUserID, groupForIntegration)
|
||||||
|
|
||||||
return am.Store.GetAccount(account.Id)
|
return am.Store.GetAccount(account.Id)
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,10 @@ components:
|
|||||||
description: Is true if this user is blocked. Blocked users can't use the system
|
description: Is true if this user is blocked. Blocked users can't use the system
|
||||||
type: boolean
|
type: boolean
|
||||||
example: false
|
example: false
|
||||||
|
issued:
|
||||||
|
description: How user was issued by API or Integration
|
||||||
|
type: string
|
||||||
|
example: api
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- email
|
- email
|
||||||
|
@ -791,6 +791,9 @@ type User struct {
|
|||||||
// IsServiceUser Is true if this user is a service user
|
// IsServiceUser Is true if this user is a service user
|
||||||
IsServiceUser *bool `json:"is_service_user,omitempty"`
|
IsServiceUser *bool `json:"is_service_user,omitempty"`
|
||||||
|
|
||||||
|
// Issued How user was issued by API or Integration
|
||||||
|
Issued *string `json:"issued,omitempty"`
|
||||||
|
|
||||||
// LastLogin Last time this user performed a login to the dashboard
|
// LastLogin Last time this user performed a login to the dashboard
|
||||||
LastLogin *time.Time `json:"last_login,omitempty"`
|
LastLogin *time.Time `json:"last_login,omitempty"`
|
||||||
|
|
||||||
|
@ -107,10 +107,11 @@ func (h *GroupsHandler) UpdateGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
peers = *req.Peers
|
peers = *req.Peers
|
||||||
}
|
}
|
||||||
group := server.Group{
|
group := server.Group{
|
||||||
ID: groupID,
|
ID: groupID,
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Peers: peers,
|
Peers: peers,
|
||||||
Issued: eg.Issued,
|
Issued: eg.Issued,
|
||||||
|
IntegrationReference: eg.IntegrationReference,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.accountManager.SaveGroup(account.Id, user.Id, &group); err != nil {
|
if err := h.accountManager.SaveGroup(account.Id, user.Id, &group); err != nil {
|
||||||
|
@ -54,6 +54,12 @@ func (h *UsersHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
existingUser, ok := account.Users[userID]
|
||||||
|
if !ok {
|
||||||
|
util.WriteError(status.Errorf(status.NotFound, "couldn't find user with ID %s", userID), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
req := &api.PutApiUsersUserIdJSONRequestBody{}
|
req := &api.PutApiUsersUserIdJSONRequestBody{}
|
||||||
err = json.NewDecoder(r.Body).Decode(&req)
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -73,10 +79,12 @@ func (h *UsersHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newUser, err := h.accountManager.SaveUser(account.Id, user.Id, &server.User{
|
newUser, err := h.accountManager.SaveUser(account.Id, user.Id, &server.User{
|
||||||
Id: userID,
|
Id: userID,
|
||||||
Role: userRole,
|
Role: userRole,
|
||||||
AutoGroups: req.AutoGroups,
|
AutoGroups: req.AutoGroups,
|
||||||
Blocked: req.IsBlocked,
|
Blocked: req.IsBlocked,
|
||||||
|
Issued: existingUser.Issued,
|
||||||
|
IntegrationReference: existingUser.IntegrationReference,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -153,6 +161,7 @@ func (h *UsersHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
Role: req.Role,
|
Role: req.Role,
|
||||||
AutoGroups: req.AutoGroups,
|
AutoGroups: req.AutoGroups,
|
||||||
IsServiceUser: req.IsServiceUser,
|
IsServiceUser: req.IsServiceUser,
|
||||||
|
Issued: server.UserIssuedAPI,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(err, w)
|
util.WriteError(err, w)
|
||||||
@ -271,5 +280,6 @@ func toUserResponse(user *server.UserInfo, currenUserID string) *api.User {
|
|||||||
IsServiceUser: &user.IsServiceUser,
|
IsServiceUser: &user.IsServiceUser,
|
||||||
IsBlocked: user.IsBlocked,
|
IsBlocked: user.IsBlocked,
|
||||||
LastLogin: &user.LastLogin,
|
LastLogin: &user.LastLogin,
|
||||||
|
Issued: &user.Issued,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,18 +33,21 @@ var usersTestAccount = &server.Account{
|
|||||||
Role: "admin",
|
Role: "admin",
|
||||||
IsServiceUser: false,
|
IsServiceUser: false,
|
||||||
AutoGroups: []string{"group_1"},
|
AutoGroups: []string{"group_1"},
|
||||||
|
Issued: server.UserIssuedAPI,
|
||||||
},
|
},
|
||||||
regularUserID: {
|
regularUserID: {
|
||||||
Id: regularUserID,
|
Id: regularUserID,
|
||||||
Role: "user",
|
Role: "user",
|
||||||
IsServiceUser: false,
|
IsServiceUser: false,
|
||||||
AutoGroups: []string{"group_1"},
|
AutoGroups: []string{"group_1"},
|
||||||
|
Issued: server.UserIssuedAPI,
|
||||||
},
|
},
|
||||||
serviceUserID: {
|
serviceUserID: {
|
||||||
Id: serviceUserID,
|
Id: serviceUserID,
|
||||||
Role: "user",
|
Role: "user",
|
||||||
IsServiceUser: true,
|
IsServiceUser: true,
|
||||||
AutoGroups: []string{"group_1"},
|
AutoGroups: []string{"group_1"},
|
||||||
|
Issued: server.UserIssuedAPI,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -64,6 +67,7 @@ func initUsersTestData() *UsersHandler {
|
|||||||
Name: "",
|
Name: "",
|
||||||
Email: "",
|
Email: "",
|
||||||
IsServiceUser: v.IsServiceUser,
|
IsServiceUser: v.IsServiceUser,
|
||||||
|
Issued: v.Issued,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return users, nil
|
return users, nil
|
||||||
@ -170,6 +174,7 @@ func TestGetUsers(t *testing.T) {
|
|||||||
assert.Equal(t, v.ID, usersTestAccount.Users[v.ID].Id)
|
assert.Equal(t, v.ID, usersTestAccount.Users[v.ID].Id)
|
||||||
assert.Equal(t, v.Role, string(usersTestAccount.Users[v.ID].Role))
|
assert.Equal(t, v.Role, string(usersTestAccount.Users[v.ID].Role))
|
||||||
assert.Equal(t, v.IsServiceUser, usersTestAccount.Users[v.ID].IsServiceUser)
|
assert.Equal(t, v.IsServiceUser, usersTestAccount.Users[v.ID].IsServiceUser)
|
||||||
|
assert.Equal(t, v.Issued, usersTestAccount.Users[v.ID].Issued)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,9 @@ const (
|
|||||||
UserStatusActive UserStatus = "active"
|
UserStatusActive UserStatus = "active"
|
||||||
UserStatusDisabled UserStatus = "disabled"
|
UserStatusDisabled UserStatus = "disabled"
|
||||||
UserStatusInvited UserStatus = "invited"
|
UserStatusInvited UserStatus = "invited"
|
||||||
|
|
||||||
|
UserIssuedAPI = "api"
|
||||||
|
UserIssuedIntegration = "integration"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StrRoleToUserRole returns UserRole for a given strRole or UserRoleUnknown if the specified role is unknown
|
// StrRoleToUserRole returns UserRole for a given strRole or UserRoleUnknown if the specified role is unknown
|
||||||
@ -42,6 +45,16 @@ type UserStatus string
|
|||||||
// UserRole is the role of a User
|
// UserRole is the role of a User
|
||||||
type UserRole string
|
type UserRole string
|
||||||
|
|
||||||
|
// IntegrationReference holds the reference to a particular integration
|
||||||
|
type IntegrationReference struct {
|
||||||
|
ID int
|
||||||
|
IntegrationType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ir IntegrationReference) String() string {
|
||||||
|
return fmt.Sprintf("%d:%s", ir.ID, ir.IntegrationType)
|
||||||
|
}
|
||||||
|
|
||||||
// User represents a user of the system
|
// User represents a user of the system
|
||||||
type User struct {
|
type User struct {
|
||||||
Id string `gorm:"primaryKey"`
|
Id string `gorm:"primaryKey"`
|
||||||
@ -59,6 +72,11 @@ type User struct {
|
|||||||
Blocked bool
|
Blocked bool
|
||||||
// LastLogin is the last time the user logged in to IdP
|
// LastLogin is the last time the user logged in to IdP
|
||||||
LastLogin time.Time
|
LastLogin time.Time
|
||||||
|
|
||||||
|
// Issued of the user
|
||||||
|
Issued string `gorm:"default:api"`
|
||||||
|
|
||||||
|
IntegrationReference IntegrationReference `gorm:"embedded;embeddedPrefix:integration_ref_"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsBlocked returns true if the user is blocked, false otherwise
|
// IsBlocked returns true if the user is blocked, false otherwise
|
||||||
@ -93,6 +111,7 @@ func (u *User) ToUserInfo(userData *idp.UserData) (*UserInfo, error) {
|
|||||||
IsServiceUser: u.IsServiceUser,
|
IsServiceUser: u.IsServiceUser,
|
||||||
IsBlocked: u.Blocked,
|
IsBlocked: u.Blocked,
|
||||||
LastLogin: u.LastLogin,
|
LastLogin: u.LastLogin,
|
||||||
|
Issued: u.Issued,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if userData.ID != u.Id {
|
if userData.ID != u.Id {
|
||||||
@ -114,6 +133,7 @@ func (u *User) ToUserInfo(userData *idp.UserData) (*UserInfo, error) {
|
|||||||
IsServiceUser: u.IsServiceUser,
|
IsServiceUser: u.IsServiceUser,
|
||||||
IsBlocked: u.Blocked,
|
IsBlocked: u.Blocked,
|
||||||
LastLogin: u.LastLogin,
|
LastLogin: u.LastLogin,
|
||||||
|
Issued: u.Issued,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,37 +146,40 @@ func (u *User) Copy() *User {
|
|||||||
pats[k] = v.Copy()
|
pats[k] = v.Copy()
|
||||||
}
|
}
|
||||||
return &User{
|
return &User{
|
||||||
Id: u.Id,
|
Id: u.Id,
|
||||||
AccountID: u.AccountID,
|
AccountID: u.AccountID,
|
||||||
Role: u.Role,
|
Role: u.Role,
|
||||||
AutoGroups: autoGroups,
|
AutoGroups: autoGroups,
|
||||||
IsServiceUser: u.IsServiceUser,
|
IsServiceUser: u.IsServiceUser,
|
||||||
ServiceUserName: u.ServiceUserName,
|
ServiceUserName: u.ServiceUserName,
|
||||||
PATs: pats,
|
PATs: pats,
|
||||||
Blocked: u.Blocked,
|
Blocked: u.Blocked,
|
||||||
LastLogin: u.LastLogin,
|
LastLogin: u.LastLogin,
|
||||||
|
Issued: u.Issued,
|
||||||
|
IntegrationReference: u.IntegrationReference,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUser creates a new user
|
// NewUser creates a new user
|
||||||
func NewUser(id string, role UserRole, isServiceUser bool, serviceUserName string, autoGroups []string) *User {
|
func NewUser(id string, role UserRole, isServiceUser bool, serviceUserName string, autoGroups []string, issued string) *User {
|
||||||
return &User{
|
return &User{
|
||||||
Id: id,
|
Id: id,
|
||||||
Role: role,
|
Role: role,
|
||||||
IsServiceUser: isServiceUser,
|
IsServiceUser: isServiceUser,
|
||||||
ServiceUserName: serviceUserName,
|
ServiceUserName: serviceUserName,
|
||||||
AutoGroups: autoGroups,
|
AutoGroups: autoGroups,
|
||||||
|
Issued: issued,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRegularUser creates a new user with role UserRoleUser
|
// NewRegularUser creates a new user with role UserRoleUser
|
||||||
func NewRegularUser(id string) *User {
|
func NewRegularUser(id string) *User {
|
||||||
return NewUser(id, UserRoleUser, false, "", []string{})
|
return NewUser(id, UserRoleUser, false, "", []string{}, UserIssuedAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAdminUser creates a new user with role UserRoleAdmin
|
// NewAdminUser creates a new user with role UserRoleAdmin
|
||||||
func NewAdminUser(id string) *User {
|
func NewAdminUser(id string) *User {
|
||||||
return NewUser(id, UserRoleAdmin, false, "", []string{})
|
return NewUser(id, UserRoleAdmin, false, "", []string{}, UserIssuedAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createServiceUser creates a new service user under the given account.
|
// createServiceUser creates a new service user under the given account.
|
||||||
@ -178,7 +201,7 @@ func (am *DefaultAccountManager) createServiceUser(accountID string, initiatorUs
|
|||||||
}
|
}
|
||||||
|
|
||||||
newUserID := uuid.New().String()
|
newUserID := uuid.New().String()
|
||||||
newUser := NewUser(newUserID, role, true, serviceUserName, autoGroups)
|
newUser := NewUser(newUserID, role, true, serviceUserName, autoGroups, UserIssuedAPI)
|
||||||
log.Debugf("New User: %v", newUser)
|
log.Debugf("New User: %v", newUser)
|
||||||
account.Users[newUserID] = newUser
|
account.Users[newUserID] = newUser
|
||||||
|
|
||||||
@ -199,6 +222,7 @@ func (am *DefaultAccountManager) createServiceUser(accountID string, initiatorUs
|
|||||||
Status: string(UserStatusActive),
|
Status: string(UserStatusActive),
|
||||||
IsServiceUser: true,
|
IsServiceUser: true,
|
||||||
LastLogin: time.Time{},
|
LastLogin: time.Time{},
|
||||||
|
Issued: UserIssuedAPI,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,9 +294,11 @@ func (am *DefaultAccountManager) inviteNewUser(accountID, userID string, invite
|
|||||||
|
|
||||||
role := StrRoleToUserRole(invite.Role)
|
role := StrRoleToUserRole(invite.Role)
|
||||||
newUser := &User{
|
newUser := &User{
|
||||||
Id: idpUser.ID,
|
Id: idpUser.ID,
|
||||||
Role: role,
|
Role: role,
|
||||||
AutoGroups: invite.AutoGroups,
|
AutoGroups: invite.AutoGroups,
|
||||||
|
Issued: invite.Issued,
|
||||||
|
IntegrationReference: invite.IntegrationReference,
|
||||||
}
|
}
|
||||||
account.Users[idpUser.ID] = newUser
|
account.Users[idpUser.ID] = newUser
|
||||||
|
|
||||||
@ -361,6 +387,10 @@ func (am *DefaultAccountManager) DeleteUser(accountID, initiatorUserID string, t
|
|||||||
return status.Errorf(status.NotFound, "target user not found")
|
return status.Errorf(status.NotFound, "target user not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if targetUser.Issued == UserIssuedIntegration {
|
||||||
|
return status.Errorf(status.PermissionDenied, "only integration can delete this user")
|
||||||
|
}
|
||||||
|
|
||||||
// handle service user first and exit, no need to fetch extra data from IDP, etc
|
// handle service user first and exit, no need to fetch extra data from IDP, etc
|
||||||
if targetUser.IsServiceUser {
|
if targetUser.IsServiceUser {
|
||||||
am.deleteServiceUser(account, initiatorUserID, targetUser)
|
am.deleteServiceUser(account, initiatorUserID, targetUser)
|
||||||
|
@ -269,6 +269,11 @@ func TestUser_Copy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Blocked: false,
|
Blocked: false,
|
||||||
LastLogin: time.Now(),
|
LastLogin: time.Now(),
|
||||||
|
Issued: "test",
|
||||||
|
IntegrationReference: IntegrationReference{
|
||||||
|
ID: 0,
|
||||||
|
IntegrationType: "test",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := validateStruct(user)
|
err := validateStruct(user)
|
||||||
@ -453,12 +458,25 @@ func TestUser_DeleteUser_SelfDelete(t *testing.T) {
|
|||||||
func TestUser_DeleteUser_regularUser(t *testing.T) {
|
func TestUser_DeleteUser_regularUser(t *testing.T) {
|
||||||
store := newStore(t)
|
store := newStore(t)
|
||||||
account := newAccountWithId(mockAccountID, mockUserID, "")
|
account := newAccountWithId(mockAccountID, mockUserID, "")
|
||||||
|
|
||||||
targetId := "user2"
|
targetId := "user2"
|
||||||
account.Users[targetId] = &User{
|
account.Users[targetId] = &User{
|
||||||
Id: targetId,
|
Id: targetId,
|
||||||
IsServiceUser: true,
|
IsServiceUser: true,
|
||||||
ServiceUserName: "user2username",
|
ServiceUserName: "user2username",
|
||||||
}
|
}
|
||||||
|
targetId = "user3"
|
||||||
|
account.Users[targetId] = &User{
|
||||||
|
Id: targetId,
|
||||||
|
IsServiceUser: false,
|
||||||
|
Issued: UserIssuedAPI,
|
||||||
|
}
|
||||||
|
targetId = "user4"
|
||||||
|
account.Users[targetId] = &User{
|
||||||
|
Id: targetId,
|
||||||
|
IsServiceUser: false,
|
||||||
|
Issued: UserIssuedIntegration,
|
||||||
|
}
|
||||||
|
|
||||||
err := store.SaveAccount(account)
|
err := store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -470,10 +488,37 @@ func TestUser_DeleteUser_regularUser(t *testing.T) {
|
|||||||
eventStore: &activity.InMemoryEventStore{},
|
eventStore: &activity.InMemoryEventStore{},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = am.DeleteUser(mockAccountID, mockUserID, targetId)
|
testCases := []struct {
|
||||||
if err != nil {
|
name string
|
||||||
t.Errorf("unexpected error: %s", err)
|
userID string
|
||||||
|
assertErrFunc assert.ErrorAssertionFunc
|
||||||
|
assertErrMessage string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Delete service user successfully ",
|
||||||
|
userID: "user2",
|
||||||
|
assertErrFunc: assert.NoError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Delete regular user successfully ",
|
||||||
|
userID: "user3",
|
||||||
|
assertErrFunc: assert.NoError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Delete integration regular user permission denied ",
|
||||||
|
userID: "user4",
|
||||||
|
assertErrFunc: assert.Error,
|
||||||
|
assertErrMessage: "only integration can delete this user",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
err = am.DeleteUser(mockAccountID, mockUserID, testCase.userID)
|
||||||
|
testCase.assertErrFunc(t, err, testCase.assertErrMessage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultAccountManager_GetUser(t *testing.T) {
|
func TestDefaultAccountManager_GetUser(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user