diff options
-rw-r--r-- | api4/user.go | 18 | ||||
-rw-r--r-- | api4/user_test.go | 17 | ||||
-rw-r--r-- | app/session.go | 13 | ||||
-rw-r--r-- | i18n/en.json | 4 | ||||
-rw-r--r-- | model/client4.go | 25 | ||||
-rw-r--r-- | store/sqlstore/user_access_token_store.go | 12 | ||||
-rw-r--r-- | store/store.go | 1 | ||||
-rw-r--r-- | store/storetest/mocks/UserAccessTokenStore.go | 16 | ||||
-rw-r--r-- | store/storetest/user_access_token_store.go | 6 |
9 files changed, 107 insertions, 5 deletions
diff --git a/api4/user.go b/api4/user.go index 0b07f8dc7..cd26b00e3 100644 --- a/api4/user.go +++ b/api4/user.go @@ -58,7 +58,8 @@ func (api *API) InitUser() { api.BaseRoutes.User.Handle("/audits", api.ApiSessionRequired(getUserAudits)).Methods("GET") api.BaseRoutes.User.Handle("/tokens", api.ApiSessionRequired(createUserAccessToken)).Methods("POST") - api.BaseRoutes.User.Handle("/tokens", api.ApiSessionRequired(getUserAccessTokens)).Methods("GET") + api.BaseRoutes.User.Handle("/tokens", api.ApiSessionRequired(getUserAccessTokensForUser)).Methods("GET") + api.BaseRoutes.Users.Handle("/tokens", api.ApiSessionRequired(getUserAccessTokens)).Methods("GET") api.BaseRoutes.Users.Handle("/tokens/{token_id:[A-Za-z0-9]+}", api.ApiSessionRequired(getUserAccessToken)).Methods("GET") api.BaseRoutes.Users.Handle("/tokens/revoke", api.ApiSessionRequired(revokeUserAccessToken)).Methods("POST") api.BaseRoutes.Users.Handle("/tokens/disable", api.ApiSessionRequired(disableUserAccessToken)).Methods("POST") @@ -1241,6 +1242,21 @@ func createUserAccessToken(c *Context, w http.ResponseWriter, r *http.Request) { } func getUserAccessTokens(c *Context, w http.ResponseWriter, r *http.Request) { + if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { + c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) + return + } + + accessTokens, err := c.App.GetUserAccessTokens(c.Params.Page, c.Params.PerPage) + if err != nil { + c.Err = err + return + } + + w.Write([]byte(model.UserAccessTokenListToJson(accessTokens))) +} + +func getUserAccessTokensForUser(c *Context, w http.ResponseWriter, r *http.Request) { c.RequireUserId() if c.Err != nil { return diff --git a/api4/user_test.go b/api4/user_test.go index fb9222d8f..7b103d23b 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -2450,6 +2450,23 @@ func TestGetUserAccessToken(t *testing.T) { if len(rtokens) != 2 { t.Fatal("should have 2 tokens") } + + _, resp = Client.GetUserAccessTokens(0,100) + CheckForbiddenStatus(t, resp) + + rtokens, resp = AdminClient.GetUserAccessTokens(1,1) + CheckNoError(t, resp) + + if len(rtokens) != 1 { + t.Fatal("should have 1 token") + } + + rtokens, resp = AdminClient.GetUserAccessTokens(0,2) + CheckNoError(t, resp) + + if len(rtokens) != 2 { + t.Fatal("should have 2 tokens") + } } func TestRevokeUserAccessToken(t *testing.T) { diff --git a/app/session.go b/app/session.go index bf5f68fa3..6a07380e4 100644 --- a/app/session.go +++ b/app/session.go @@ -356,6 +356,19 @@ func (a *App) EnableUserAccessToken(token *model.UserAccessToken) *model.AppErro return nil } +func (a *App) GetUserAccessTokens(page, perPage int) ([]*model.UserAccessToken, *model.AppError) { + if result := <-a.Srv.Store.UserAccessToken().GetAll(page*perPage, perPage); result.Err != nil { + return nil, result.Err + } else { + tokens := result.Data.([]*model.UserAccessToken) + for _, token := range tokens { + token.Token = "" + } + + return tokens, nil + } +} + func (a *App) GetUserAccessTokensForUser(userId string, page, perPage int) ([]*model.UserAccessToken, *model.AppError) { if result := <-a.Srv.Store.UserAccessToken().GetByUser(userId, page*perPage, perPage); result.Err != nil { return nil, result.Err diff --git a/i18n/en.json b/i18n/en.json index 405ee49f1..5b69732e6 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -6783,6 +6783,10 @@ "translation": "We couldn't get the personal access token by token" }, { + "id": "store.sql_user_access_token.get_all.app_error", + "translation": "We couldn't get all personal access tokens" + }, + { "id": "store.sql_user_access_token.get_by_user.app_error", "translation": "We couldn't get the personal access tokens by user" }, diff --git a/model/client4.go b/model/client4.go index e84a23e5f..3f3439ebe 100644 --- a/model/client4.go +++ b/model/client4.go @@ -82,6 +82,10 @@ func (c *Client4) GetUserRoute(userId string) string { return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId) } +func (c *Client4) GetUserAccessTokensRoute() string { + return fmt.Sprintf(c.GetUsersRoute() + "/tokens") +} + func (c *Client4) GetUserAccessTokenRoute(tokenId string) string { return fmt.Sprintf(c.GetUsersRoute()+"/tokens/%v", tokenId) } @@ -1035,10 +1039,23 @@ func (c *Client4) CreateUserAccessToken(userId, description string) (*UserAccess } } -// GetUserAccessToken will get a user access token's id, description and the user_id -// of the user it is for. The actual token will not be returned. Must have the -// 'read_user_access_token' permission and if getting for another user, must have the -// 'edit_other_users' permission. +// GetUserAccessTokens will get a page of access tokens' id, description, is_active +// and the user_id in the system. The actual token will not be returned. Must have +// the 'manage_system' permission. +func (c *Client4) GetUserAccessTokens(page int, perPage int) ([]*UserAccessToken, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetUserAccessTokensRoute()+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserAccessTokenListFromJson(r.Body), BuildResponse(r) + } +} + +// GetUserAccessToken will get a user access tokens' id, description, is_active +// and the user_id of the user it is for. The actual token will not be returned. +// Must have the 'read_user_access_token' permission and if getting for another +// user, must have the 'edit_other_users' permission. func (c *Client4) GetUserAccessToken(tokenId string) (*UserAccessToken, *Response) { if r, err := c.DoApiGet(c.GetUserAccessTokenRoute(tokenId), ""); err != nil { return nil, BuildErrorResponse(r, err) diff --git a/store/sqlstore/user_access_token_store.go b/store/sqlstore/user_access_token_store.go index 530ba8d16..deba9f7ea 100644 --- a/store/sqlstore/user_access_token_store.go +++ b/store/sqlstore/user_access_token_store.go @@ -171,6 +171,18 @@ func (s SqlUserAccessTokenStore) Get(tokenId string) store.StoreChannel { }) } +func (s SqlUserAccessTokenStore) GetAll(offset, limit int) store.StoreChannel { + return store.Do(func(result *store.StoreResult) { + tokens := []*model.UserAccessToken{} + + if _, err := s.GetReplica().Select(&tokens, "SELECT * FROM UserAccessTokens LIMIT :Limit OFFSET :Offset", map[string]interface{}{"Offset": offset, "Limit": limit}); err != nil { + result.Err = model.NewAppError("SqlUserAccessTokenStore.GetAll", "store.sql_user_access_token.get_all.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + result.Data = tokens + }) +} + func (s SqlUserAccessTokenStore) GetByToken(tokenString string) store.StoreChannel { return store.Do(func(result *store.StoreResult) { token := model.UserAccessToken{} diff --git a/store/store.go b/store/store.go index 6ca07c294..de8bd4635 100644 --- a/store/store.go +++ b/store/store.go @@ -446,6 +446,7 @@ type UserAccessTokenStore interface { Delete(tokenId string) StoreChannel DeleteAllForUser(userId string) StoreChannel Get(tokenId string) StoreChannel + GetAll(offset int, limit int) StoreChannel GetByToken(tokenString string) StoreChannel GetByUser(userId string, page, perPage int) StoreChannel UpdateTokenEnable(tokenId string) StoreChannel diff --git a/store/storetest/mocks/UserAccessTokenStore.go b/store/storetest/mocks/UserAccessTokenStore.go index 87541fd76..60e08076c 100644 --- a/store/storetest/mocks/UserAccessTokenStore.go +++ b/store/storetest/mocks/UserAccessTokenStore.go @@ -61,6 +61,22 @@ func (_m *UserAccessTokenStore) Get(tokenId string) store.StoreChannel { return r0 } +// GetAll provides a mock function with given fields: +func (_m *UserAccessTokenStore) GetAll(page int, perPage int) store.StoreChannel { + ret := _m.Called(page, perPage) + + var r0 store.StoreChannel + if rf, ok := ret.Get(0).(func(int, int) store.StoreChannel); ok { + r0 = rf(page, perPage) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(store.StoreChannel) + } + } + + return r0 +} + // GetByToken provides a mock function with given fields: tokenString func (_m *UserAccessTokenStore) GetByToken(tokenString string) store.StoreChannel { ret := _m.Called(tokenString) diff --git a/store/storetest/user_access_token_store.go b/store/storetest/user_access_token_store.go index 661c969da..c32023d30 100644 --- a/store/storetest/user_access_token_store.go +++ b/store/storetest/user_access_token_store.go @@ -54,6 +54,12 @@ func testUserAccessTokenSaveGetDelete(t *testing.T, ss store.Store) { t.Fatal("received incorrect number of tokens after save") } + if result := <-ss.UserAccessToken().GetAll(0, 100); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.([]*model.UserAccessToken); len(received) != 1 { + t.Fatal("received incorrect number of tokens after save") + } + if result := <-ss.UserAccessToken().Delete(uat.Id); result.Err != nil { t.Fatal(result.Err) } |