Skip to content

Commit

Permalink
Extends management user and group structure (#1268)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
bcmmbaga authored Nov 1, 2023
1 parent 6d4240a commit c38d65e
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 61 deletions.
21 changes: 12 additions & 9 deletions management/server/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const (
UnknownCategory = "unknown"
GroupIssuedAPI = "api"
GroupIssuedJWT = "jwt"
GroupIssuedIntegration = "integration"
CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days
CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days
DefaultPeerLoginExpiration = 24 * time.Hour
Expand Down Expand Up @@ -195,15 +196,17 @@ type Account struct {
}

type UserInfo struct {
ID string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
Role string `json:"role"`
AutoGroups []string `json:"auto_groups"`
Status string `json:"-"`
IsServiceUser bool `json:"is_service_user"`
IsBlocked bool `json:"is_blocked"`
LastLogin time.Time `json:"last_login"`
ID string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
Role string `json:"role"`
AutoGroups []string `json:"auto_groups"`
Status string `json:"-"`
IsServiceUser bool `json:"is_service_user"`
IsBlocked bool `json:"is_blocked"`
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
Expand Down
5 changes: 5 additions & 0 deletions management/server/file_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ func restore(file string) (*FileStore, error) {
}
for _, user := range account.Users {
store.UserID2AccountID[user.Id] = accountID
if user.Issued == "" {
user.Issued = UserIssuedAPI
account.Users[user.Id] = user
}

for _, pat := range user.PATs {
store.TokenID2UserID[pat.ID] = user.Id
store.HashedPAT2TokenID[pat.HashedToken] = pat.ID
Expand Down
7 changes: 7 additions & 0 deletions management/server/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type Group struct {

// Peers list of the group
Peers []string `gorm:"serializer:json"`

IntegrationReference IntegrationReference `gorm:"embedded;embeddedPrefix:integration_ref_"`
}

// EventMeta returns activity event meta related to the group
Expand Down Expand Up @@ -160,6 +162,11 @@ func (am *DefaultAccountManager) DeleteGroup(accountId, userId, groupID string)
return nil
}

// check integration link
if g.Issued == GroupIssuedIntegration {
return &GroupLinkError{GroupIssuedIntegration, g.IntegrationReference.String()}
}

// check route links
for _, r := range account.Routes {
for _, g := range r.Groups {
Expand Down
64 changes: 39 additions & 25 deletions management/server/group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ func TestDefaultAccountManager_DeleteGroup(t *testing.T) {
"grp-for-users",
"user",
},
{
"integration",
"grp-for-integration",
"integration",
},
}

for _, testCase := range testCases {
Expand Down Expand Up @@ -79,43 +84,51 @@ func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
domain := "example.com"

groupForRoute := &Group{
"grp-for-route",
"account-id",
"Group for route",
GroupIssuedAPI,
make([]string, 0),
ID: "grp-for-route",
AccountID: "account-id",
Name: "Group for route",
Issued: GroupIssuedAPI,
Peers: make([]string, 0),
}

groupForNameServerGroups := &Group{
"grp-for-name-server-grp",
"account-id",
"Group for name server groups",
GroupIssuedAPI,
make([]string, 0),
ID: "grp-for-name-server-grp",
AccountID: "account-id",
Name: "Group for name server groups",
Issued: GroupIssuedAPI,
Peers: make([]string, 0),
}

groupForPolicies := &Group{
"grp-for-policies",
"account-id",
"Group for policies",
GroupIssuedAPI,
make([]string, 0),
ID: "grp-for-policies",
AccountID: "account-id",
Name: "Group for policies",
Issued: GroupIssuedAPI,
Peers: make([]string, 0),
}

groupForSetupKeys := &Group{
"grp-for-keys",
"account-id",
"Group for setup keys",
GroupIssuedAPI,
make([]string, 0),
ID: "grp-for-keys",
AccountID: "account-id",
Name: "Group for setup keys",
Issued: GroupIssuedAPI,
Peers: make([]string, 0),
}

groupForUsers := &Group{
"grp-for-users",
"account-id",
"Group for users",
GroupIssuedAPI,
make([]string, 0),
ID: "grp-for-users",
AccountID: "account-id",
Name: "Group for users",
Issued: GroupIssuedAPI,
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{
Expand Down Expand Up @@ -164,6 +177,7 @@ func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
_ = am.SaveGroup(accountID, groupAdminUserID, groupForPolicies)
_ = am.SaveGroup(accountID, groupAdminUserID, groupForSetupKeys)
_ = am.SaveGroup(accountID, groupAdminUserID, groupForUsers)
_ = am.SaveGroup(accountID, groupAdminUserID, groupForIntegration)

return am.Store.GetAccount(account.Id)
}
4 changes: 4 additions & 0 deletions management/server/http/api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ components:
description: Is true if this user is blocked. Blocked users can't use the system
type: boolean
example: false
issued:
description: How user was issued by API or Integration
type: string
example: api
required:
- id
- email
Expand Down
3 changes: 3 additions & 0 deletions management/server/http/api/types.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions management/server/http/groups_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,11 @@ func (h *GroupsHandler) UpdateGroup(w http.ResponseWriter, r *http.Request) {
peers = *req.Peers
}
group := server.Group{
ID: groupID,
Name: req.Name,
Peers: peers,
Issued: eg.Issued,
ID: groupID,
Name: req.Name,
Peers: peers,
Issued: eg.Issued,
IntegrationReference: eg.IntegrationReference,
}

if err := h.accountManager.SaveGroup(account.Id, user.Id, &group); err != nil {
Expand Down
18 changes: 14 additions & 4 deletions management/server/http/users_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ func (h *UsersHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
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{}
err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
Expand All @@ -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{
Id: userID,
Role: userRole,
AutoGroups: req.AutoGroups,
Blocked: req.IsBlocked,
Id: userID,
Role: userRole,
AutoGroups: req.AutoGroups,
Blocked: req.IsBlocked,
Issued: existingUser.Issued,
IntegrationReference: existingUser.IntegrationReference,
})

if err != nil {
Expand Down Expand Up @@ -153,6 +161,7 @@ func (h *UsersHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
Role: req.Role,
AutoGroups: req.AutoGroups,
IsServiceUser: req.IsServiceUser,
Issued: server.UserIssuedAPI,
})
if err != nil {
util.WriteError(err, w)
Expand Down Expand Up @@ -271,5 +280,6 @@ func toUserResponse(user *server.UserInfo, currenUserID string) *api.User {
IsServiceUser: &user.IsServiceUser,
IsBlocked: user.IsBlocked,
LastLogin: &user.LastLogin,
Issued: &user.Issued,
}
}
5 changes: 5 additions & 0 deletions management/server/http/users_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,21 @@ var usersTestAccount = &server.Account{
Role: "admin",
IsServiceUser: false,
AutoGroups: []string{"group_1"},
Issued: server.UserIssuedAPI,
},
regularUserID: {
Id: regularUserID,
Role: "user",
IsServiceUser: false,
AutoGroups: []string{"group_1"},
Issued: server.UserIssuedAPI,
},
serviceUserID: {
Id: serviceUserID,
Role: "user",
IsServiceUser: true,
AutoGroups: []string{"group_1"},
Issued: server.UserIssuedAPI,
},
},
}
Expand All @@ -64,6 +67,7 @@ func initUsersTestData() *UsersHandler {
Name: "",
Email: "",
IsServiceUser: v.IsServiceUser,
Issued: v.Issued,
})
}
return users, nil
Expand Down Expand Up @@ -170,6 +174,7 @@ func TestGetUsers(t *testing.T) {
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.IsServiceUser, usersTestAccount.Users[v.ID].IsServiceUser)
assert.Equal(t, v.Issued, usersTestAccount.Users[v.ID].Issued)
}
})
}
Expand Down
Loading

0 comments on commit c38d65e

Please sign in to comment.