[PLT-7793] Added /users/tokens endpoint (#8038)

* Added /users/tokens/all endpoint

- UserAccessStore now has getAll method
- Added tests
- Added route
- Added handler

* Remove space fix check-style

* Remove blank space check-style

* Fixes for make check-style

* Remove extra code that is un-needed in user_test.go

* Rename endpoint + grammar

- Renamed /users/tokens/all to /users/tokens
- Renamed getUserAccessTokens to getUserAccessTokensForUser
- Renamed getAllUserAccessTokens to getUserAccessTokens
- Minor Grammar changes

* Add localization for sql_user_access_token.get_all

* Fix minor plural spelling
Этот коммит содержится в:
Adrian Carolli
2018-01-05 14:46:48 -05:00
коммит произвёл Joram Wilander
родитель 66bdf830b5
Коммит fd3fa8f8dc
9 изменённых файлов: 107 добавлений и 5 удалений

Просмотреть файл

@@ -58,7 +58,8 @@ func (api *API) InitUser() {
api.BaseRoutes.User.Handle("/audits", api.ApiSessionRequired(getUserAudits)).Methods("GET") 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(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/{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/revoke", api.ApiSessionRequired(revokeUserAccessToken)).Methods("POST")
api.BaseRoutes.Users.Handle("/tokens/disable", api.ApiSessionRequired(disableUserAccessToken)).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) { 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() c.RequireUserId()
if c.Err != nil { if c.Err != nil {
return return

Просмотреть файл

@@ -2450,6 +2450,23 @@ func TestGetUserAccessToken(t *testing.T) {
if len(rtokens) != 2 { if len(rtokens) != 2 {
t.Fatal("should have 2 tokens") 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) { func TestRevokeUserAccessToken(t *testing.T) {

Просмотреть файл

@@ -356,6 +356,19 @@ func (a *App) EnableUserAccessToken(token *model.UserAccessToken) *model.AppErro
return nil 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) { 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 { if result := <-a.Srv.Store.UserAccessToken().GetByUser(userId, page*perPage, perPage); result.Err != nil {
return nil, result.Err return nil, result.Err

Просмотреть файл

@@ -6782,6 +6782,10 @@
"id": "store.sql_user_access_token.get_by_token.app_error", "id": "store.sql_user_access_token.get_by_token.app_error",
"translation": "We couldn't get the personal access token by token" "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", "id": "store.sql_user_access_token.get_by_user.app_error",
"translation": "We couldn't get the personal access tokens by user" "translation": "We couldn't get the personal access tokens by user"

Просмотреть файл

@@ -82,6 +82,10 @@ func (c *Client4) GetUserRoute(userId string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId) 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 { func (c *Client4) GetUserAccessTokenRoute(tokenId string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/tokens/%v", tokenId) 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 // GetUserAccessTokens will get a page of access tokens' id, description, is_active
// of the user it is for. The actual token will not be returned. Must have the // and the user_id in the system. The actual token will not be returned. Must have
// 'read_user_access_token' permission and if getting for another user, must have the // the 'manage_system' permission.
// 'edit_other_users' 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) { func (c *Client4) GetUserAccessToken(tokenId string) (*UserAccessToken, *Response) {
if r, err := c.DoApiGet(c.GetUserAccessTokenRoute(tokenId), ""); err != nil { if r, err := c.DoApiGet(c.GetUserAccessTokenRoute(tokenId), ""); err != nil {
return nil, BuildErrorResponse(r, err) return nil, BuildErrorResponse(r, err)

Просмотреть файл

@@ -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 { func (s SqlUserAccessTokenStore) GetByToken(tokenString string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
token := model.UserAccessToken{} token := model.UserAccessToken{}

Просмотреть файл

@@ -446,6 +446,7 @@ type UserAccessTokenStore interface {
Delete(tokenId string) StoreChannel Delete(tokenId string) StoreChannel
DeleteAllForUser(userId string) StoreChannel DeleteAllForUser(userId string) StoreChannel
Get(tokenId string) StoreChannel Get(tokenId string) StoreChannel
GetAll(offset int, limit int) StoreChannel
GetByToken(tokenString string) StoreChannel GetByToken(tokenString string) StoreChannel
GetByUser(userId string, page, perPage int) StoreChannel GetByUser(userId string, page, perPage int) StoreChannel
UpdateTokenEnable(tokenId string) StoreChannel UpdateTokenEnable(tokenId string) StoreChannel

Просмотреть файл

@@ -61,6 +61,22 @@ func (_m *UserAccessTokenStore) Get(tokenId string) store.StoreChannel {
return r0 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 // GetByToken provides a mock function with given fields: tokenString
func (_m *UserAccessTokenStore) GetByToken(tokenString string) store.StoreChannel { func (_m *UserAccessTokenStore) GetByToken(tokenString string) store.StoreChannel {
ret := _m.Called(tokenString) ret := _m.Called(tokenString)

Просмотреть файл

@@ -54,6 +54,12 @@ func testUserAccessTokenSaveGetDelete(t *testing.T, ss store.Store) {
t.Fatal("received incorrect number of tokens after save") 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 { if result := <-ss.UserAccessToken().Delete(uat.Id); result.Err != nil {
t.Fatal(result.Err) t.Fatal(result.Err)
} }