Implement user token delete

This commit is contained in:
Simone Gotti 2019-04-05 15:01:57 +02:00
parent 3a41e66f5b
commit c4310be7de
9 changed files with 188 additions and 3 deletions

View File

@ -17,9 +17,9 @@ package cmd
import ( import (
"context" "context"
"github.com/pkg/errors"
"github.com/sorintlab/agola/internal/services/gateway/api" "github.com/sorintlab/agola/internal/services/gateway/api"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -58,13 +58,13 @@ func userLADelete(cmd *cobra.Command, args []string) error {
userName := userLADeleteOpts.userName userName := userLADeleteOpts.userName
laID := userLADeleteOpts.laID laID := userLADeleteOpts.laID
log.Infof("deleting linked account %s for user %q", userName) log.Infof("deleting linked account %q for user %q", laID, userName)
_, err := gwclient.DeleteUserLA(context.TODO(), userName, laID) _, err := gwclient.DeleteUserLA(context.TODO(), userName, laID)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to delete linked account") return errors.Wrapf(err, "failed to delete linked account")
} }
log.Infof("linked account %q for user %q deleted", userName, laID) log.Infof("linked account %q for user %q deleted", laID, userName)
return nil return nil
} }

View File

@ -0,0 +1,70 @@
// Copyright 2019 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"context"
"github.com/sorintlab/agola/internal/services/gateway/api"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var cmdUserTokenDelete = &cobra.Command{
Use: "delete",
Short: "delete a user token",
Run: func(cmd *cobra.Command, args []string) {
if err := userTokenDelete(cmd, args); err != nil {
log.Fatalf("err: %v", err)
}
},
}
type userTokenDeleteOptions struct {
userName string
tokenName string
}
var userTokenDeleteOpts userTokenDeleteOptions
func init() {
flags := cmdUserTokenDelete.Flags()
flags.StringVarP(&userTokenDeleteOpts.userName, "username", "n", "", "user name")
flags.StringVarP(&userTokenDeleteOpts.tokenName, "tokenname", "t", "", "token name")
cmdUserTokenDelete.MarkFlagRequired("username")
cmdUserTokenDelete.MarkFlagRequired("tokenname")
cmdUserToken.AddCommand(cmdUserTokenDelete)
}
func userTokenDelete(cmd *cobra.Command, args []string) error {
gwclient := api.NewClient(gatewayURL, token)
userName := userTokenDeleteOpts.userName
tokenName := userTokenDeleteOpts.tokenName
log.Infof("deleting token %q for user %q", tokenName, userName)
_, err := gwclient.DeleteUserToken(context.TODO(), userName, tokenName)
if err != nil {
return errors.Wrapf(err, "failed to delete user token")
}
log.Infof("token %q for user %q deleted", tokenName, userName)
return nil
}

View File

@ -383,6 +383,10 @@ func (c *Client) CreateUserToken(ctx context.Context, userName string, req *Crea
return tresp, resp, err return tresp, resp, err
} }
func (c *Client) DeleteUserToken(ctx context.Context, userName, tokenName string) (*http.Response, error) {
return c.getResponse(ctx, "DELETE", fmt.Sprintf("/users/%s/tokens/%s", userName, tokenName), nil, jsonContent, nil)
}
func (c *Client) GetRemoteSource(ctx context.Context, rsID string) (*types.RemoteSource, *http.Response, error) { func (c *Client) GetRemoteSource(ctx context.Context, rsID string) (*types.RemoteSource, *http.Response, error) {
rs := new(types.RemoteSource) rs := new(types.RemoteSource)
resp, err := c.getParsedResponse(ctx, "GET", fmt.Sprintf("/remotesource/%s", rsID), nil, jsonContent, nil, rs) resp, err := c.getParsedResponse(ctx, "GET", fmt.Sprintf("/remotesource/%s", rsID), nil, jsonContent, nil, rs)

View File

@ -471,3 +471,24 @@ func (h *CreateUserTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
return return
} }
} }
type DeleteUserTokenHandler struct {
log *zap.SugaredLogger
ch *command.CommandHandler
}
func NewDeleteUserTokenHandler(logger *zap.Logger, ch *command.CommandHandler) *DeleteUserTokenHandler {
return &DeleteUserTokenHandler{log: logger.Sugar(), ch: ch}
}
func (h *DeleteUserTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
vars := mux.Vars(r)
userName := vars["username"]
tokenName := vars["tokenname"]
err := h.ch.DeleteUserToken(ctx, userName, tokenName)
if httpError(w, err) {
h.log.Errorf("err: %+v", err)
}
}

View File

@ -686,6 +686,65 @@ func (s *CommandHandler) CreateUserToken(ctx context.Context, userName, tokenNam
return token, err return token, err
} }
func (s *CommandHandler) DeleteUserToken(ctx context.Context, userName, tokenName string) error {
if userName == "" {
return util.NewErrBadRequest(errors.Errorf("user name required"))
}
if tokenName == "" {
return util.NewErrBadRequest(errors.Errorf("token name required"))
}
var user *types.User
var cgt *wal.ChangeGroupsUpdateToken
// must do all the check in a single transaction to avoid concurrent changes
err := s.readDB.Do(func(tx *db.Tx) error {
var err error
user, err = s.readDB.GetUserByName(tx, userName)
if err != nil {
return err
}
if user == nil {
return util.NewErrBadRequest(errors.Errorf("user %q doesn't exist", userName))
}
cgNames := []string{user.ID}
cgt, err = s.readDB.GetChangeGroupsUpdateTokens(tx, cgNames)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
_, ok := user.Tokens[tokenName]
if !ok {
return util.NewErrBadRequest(errors.Errorf("token %q for user %q doesn't exist", tokenName, userName))
}
delete(user.Tokens, tokenName)
userj, err := json.Marshal(user)
if err != nil {
return errors.Wrapf(err, "failed to marshal user")
}
actions := []*wal.Action{
{
ActionType: wal.ActionTypePut,
DataType: string(types.ConfigTypeUser),
ID: user.ID,
Data: userj,
},
}
_, err = s.wal.WriteWal(ctx, actions, cgt)
return err
}
func (s *CommandHandler) CreateRemoteSource(ctx context.Context, remoteSource *types.RemoteSource) (*types.RemoteSource, error) { func (s *CommandHandler) CreateRemoteSource(ctx context.Context, remoteSource *types.RemoteSource) (*types.RemoteSource, error) {
if remoteSource.Name == "" { if remoteSource.Name == "" {
return nil, util.NewErrBadRequest(errors.Errorf("remotesource name required")) return nil, util.NewErrBadRequest(errors.Errorf("remotesource name required"))

View File

@ -144,6 +144,7 @@ func (s *ConfigStore) Run(ctx context.Context) error {
updateUserLAHandler := api.NewUpdateUserLAHandler(logger, s.ch) updateUserLAHandler := api.NewUpdateUserLAHandler(logger, s.ch)
createUserTokenHandler := api.NewCreateUserTokenHandler(logger, s.ch) createUserTokenHandler := api.NewCreateUserTokenHandler(logger, s.ch)
deleteUserTokenHandler := api.NewDeleteUserTokenHandler(logger, s.ch)
orgHandler := api.NewOrgHandler(logger, s.readDB) orgHandler := api.NewOrgHandler(logger, s.readDB)
orgsHandler := api.NewOrgsHandler(logger, s.readDB) orgsHandler := api.NewOrgsHandler(logger, s.readDB)
@ -193,6 +194,7 @@ func (s *ConfigStore) Run(ctx context.Context) error {
apirouter.Handle("/users/{username}/linkedaccounts/{laid}", deleteUserLAHandler).Methods("DELETE") apirouter.Handle("/users/{username}/linkedaccounts/{laid}", deleteUserLAHandler).Methods("DELETE")
apirouter.Handle("/users/{username}/linkedaccounts/{laid}", updateUserLAHandler).Methods("PUT") apirouter.Handle("/users/{username}/linkedaccounts/{laid}", updateUserLAHandler).Methods("PUT")
apirouter.Handle("/users/{username}/tokens", createUserTokenHandler).Methods("PUT") apirouter.Handle("/users/{username}/tokens", createUserTokenHandler).Methods("PUT")
apirouter.Handle("/users/{username}/tokens/{tokenname}", deleteUserTokenHandler).Methods("DELETE")
apirouter.Handle("/org/{orgid}", orgHandler).Methods("GET") apirouter.Handle("/org/{orgid}", orgHandler).Methods("GET")
apirouter.Handle("/orgs", orgsHandler).Methods("GET") apirouter.Handle("/orgs", orgsHandler).Methods("GET")

View File

@ -306,6 +306,10 @@ func (c *Client) CreateUserToken(ctx context.Context, userName string, req *Crea
return tresp, resp, err return tresp, resp, err
} }
func (c *Client) DeleteUserToken(ctx context.Context, userName, tokenName string) (*http.Response, error) {
return c.getResponse(ctx, "DELETE", fmt.Sprintf("/users/%s/tokens/%s", userName, tokenName), nil, jsonContent, nil)
}
func (c *Client) GetRun(ctx context.Context, runID string) (*RunResponse, *http.Response, error) { func (c *Client) GetRun(ctx context.Context, runID string) (*RunResponse, *http.Response, error) {
run := new(RunResponse) run := new(RunResponse)
resp, err := c.getParsedResponse(ctx, "GET", fmt.Sprintf("/run/%s", runID), nil, jsonContent, nil, run) resp, err := c.getParsedResponse(ctx, "GET", fmt.Sprintf("/run/%s", runID), nil, jsonContent, nil, run)

View File

@ -416,6 +416,29 @@ func (h *CreateUserTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
} }
} }
type DeleteUserTokenHandler struct {
log *zap.SugaredLogger
configstoreClient *csapi.Client
}
func NewDeleteUserTokenHandler(logger *zap.Logger, configstoreClient *csapi.Client) *DeleteUserTokenHandler {
return &DeleteUserTokenHandler{log: logger.Sugar(), configstoreClient: configstoreClient}
}
func (h *DeleteUserTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
vars := mux.Vars(r)
userName := vars["username"]
tokenName := vars["tokenname"]
h.log.Infof("deleting user %q token %q", userName, tokenName)
_, err := h.configstoreClient.DeleteUserToken(ctx, userName, tokenName)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
type RegisterUserRequest struct { type RegisterUserRequest struct {
CreateUserRequest CreateUserRequest
CreateUserLARequest CreateUserLARequest

View File

@ -175,6 +175,7 @@ func (g *Gateway) Run(ctx context.Context) error {
createUserLAHandler := api.NewCreateUserLAHandler(logger, g.ch) createUserLAHandler := api.NewCreateUserLAHandler(logger, g.ch)
deleteUserLAHandler := api.NewDeleteUserLAHandler(logger, g.configstoreClient) deleteUserLAHandler := api.NewDeleteUserLAHandler(logger, g.configstoreClient)
createUserTokenHandler := api.NewCreateUserTokenHandler(logger, g.configstoreClient) createUserTokenHandler := api.NewCreateUserTokenHandler(logger, g.configstoreClient)
deleteUserTokenHandler := api.NewDeleteUserTokenHandler(logger, g.configstoreClient)
remoteSourceHandler := api.NewRemoteSourceHandler(logger, g.configstoreClient) remoteSourceHandler := api.NewRemoteSourceHandler(logger, g.configstoreClient)
createRemoteSourceHandler := api.NewCreateRemoteSourceHandler(logger, g.configstoreClient) createRemoteSourceHandler := api.NewCreateRemoteSourceHandler(logger, g.configstoreClient)
@ -247,6 +248,7 @@ func (g *Gateway) Run(ctx context.Context) error {
apirouter.Handle("/users/{username}/linkedaccounts", authForcedHandler(createUserLAHandler)).Methods("PUT") apirouter.Handle("/users/{username}/linkedaccounts", authForcedHandler(createUserLAHandler)).Methods("PUT")
apirouter.Handle("/users/{username}/linkedaccounts/{laid}", authForcedHandler(deleteUserLAHandler)).Methods("DELETE") apirouter.Handle("/users/{username}/linkedaccounts/{laid}", authForcedHandler(deleteUserLAHandler)).Methods("DELETE")
apirouter.Handle("/users/{username}/tokens", authForcedHandler(createUserTokenHandler)).Methods("PUT") apirouter.Handle("/users/{username}/tokens", authForcedHandler(createUserTokenHandler)).Methods("PUT")
apirouter.Handle("/users/{username}/tokens/{tokenname}", authForcedHandler(deleteUserTokenHandler)).Methods("DELETE")
apirouter.Handle("/remotesource/{id}", authForcedHandler(remoteSourceHandler)).Methods("GET") apirouter.Handle("/remotesource/{id}", authForcedHandler(remoteSourceHandler)).Methods("GET")
apirouter.Handle("/remotesources", authForcedHandler(createRemoteSourceHandler)).Methods("PUT") apirouter.Handle("/remotesources", authForcedHandler(createRemoteSourceHandler)).Methods("PUT")