From 2d305ba54ab597bb14956f997199def1f0f75580 Mon Sep 17 00:00:00 2001 From: Sergey Yakubov <sergey.yakubov@desy.de> Date: Tue, 28 Sep 2021 14:02:06 +0200 Subject: [PATCH] token to db --- .../authorization/authorization.go | 20 +++--- authorizer/src/asapo_authorizer/cli/cli.go | 9 +++ .../src/asapo_authorizer/cli/create_token.go | 61 +++++++++++-------- .../asapo_authorizer/cli/create_token_test.go | 21 +++++++ .../src/asapo_authorizer/database/database.go | 13 +++- .../database/mock_database.go | 5 +- .../src/asapo_authorizer/database/mongodb.go | 23 ++++++- .../server/db_request_test.go | 5 +- .../asapo_authorizer/server/issue_token.go | 2 +- .../server/server_nottested.go | 6 +- 10 files changed, 118 insertions(+), 47 deletions(-) diff --git a/authorizer/src/asapo_authorizer/authorization/authorization.go b/authorizer/src/asapo_authorizer/authorization/authorization.go index 1a0e85b25..59b613c6f 100644 --- a/authorizer/src/asapo_authorizer/authorization/authorization.go +++ b/authorizer/src/asapo_authorizer/authorization/authorization.go @@ -14,8 +14,8 @@ type Auth struct { authJWT utils.Auth } -func NewAuth(authUser,authAdmin,authJWT utils.Auth) *Auth { - return &Auth{authUser,authAdmin,authJWT} +func NewAuth(authUser, authAdmin, authJWT utils.Auth) *Auth { + return &Auth{authUser, authAdmin, authJWT} } func (auth *Auth) AdminAuth() utils.Auth { @@ -31,7 +31,7 @@ func (auth *Auth) JWTAuth() utils.Auth { } func subjectFromRequest(request structs.IssueTokenRequest) string { - for key,value := range request.Subject { + for key, value := range request.Subject { switch key { case "beamline": return utils.SubjectFromBeamline(value) @@ -44,10 +44,10 @@ func subjectFromRequest(request structs.IssueTokenRequest) string { return "" } -func (auth *Auth) PrepareAccessToken(request structs.IssueTokenRequest, userToken bool) (string, error) { - var claims utils.CustomClaims +func (auth *Auth) PrepareAccessToken(request structs.IssueTokenRequest, userToken bool) (token string, claims *utils.CustomClaims, err error) { var extraClaim structs.AccessTokenExtraClaim + claims = new(utils.CustomClaims) claims.Subject = subjectFromRequest(request) extraClaim.AccessTypes = request.AccessTypes @@ -57,15 +57,19 @@ func (auth *Auth) PrepareAccessToken(request structs.IssueTokenRequest, userToke claims.Id = uid.String() if userToken { - return auth.UserAuth().GenerateToken(&claims) + token, err = auth.UserAuth().GenerateToken(claims) } else { - return auth.AdminAuth().GenerateToken(&claims) + token, err = auth.AdminAuth().GenerateToken(claims) } + if err != nil { + return "", nil, err + } + return } func UserTokenResponce(request structs.IssueTokenRequest, token string) []byte { expires := "" - if request.DaysValid>0 { + if request.DaysValid > 0 { expires = time.Now().Add(time.Duration(request.DaysValid*24) * time.Hour).UTC().Format(time.RFC3339) } answer := structs.IssueTokenResponse{ diff --git a/authorizer/src/asapo_authorizer/cli/cli.go b/authorizer/src/asapo_authorizer/cli/cli.go index 0851d568c..3bfde762c 100644 --- a/authorizer/src/asapo_authorizer/cli/cli.go +++ b/authorizer/src/asapo_authorizer/cli/cli.go @@ -3,6 +3,8 @@ package cli import ( + "asapo_authorizer/database" + "asapo_authorizer/server" "errors" "flag" "fmt" @@ -16,6 +18,8 @@ var flHelp bool var outBuf io.Writer = os.Stdout +var db database.Agent + func printHelp(f *flag.FlagSet) bool { if flHelp { f.Usage() @@ -39,6 +43,11 @@ func DoCommand(name string, args []string) error { method := methodVal.Interface().(func() error) + server.CreateDiscoveryService() + db = new(database.Mongodb) + server.InitDB(db) + defer db.Close() + return method() } diff --git a/authorizer/src/asapo_authorizer/cli/create_token.go b/authorizer/src/asapo_authorizer/cli/create_token.go index 33ab16ebd..5ddd51c7e 100644 --- a/authorizer/src/asapo_authorizer/cli/create_token.go +++ b/authorizer/src/asapo_authorizer/cli/create_token.go @@ -2,6 +2,7 @@ package cli import ( "asapo_authorizer/authorization" + "asapo_authorizer/database" "asapo_authorizer/server" "asapo_common/structs" "errors" @@ -19,24 +20,24 @@ type tokenFlags struct { } func userTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, err error) { - if (flags.Beamline=="" && flags.Beamtime=="") || (flags.Beamline!="" && flags.Beamtime!="") { - return request,errors.New("beamtime or beamline must be set") + if (flags.Beamline == "" && flags.Beamtime == "") || (flags.Beamline != "" && flags.Beamtime != "") { + return request, errors.New("beamtime or beamline must be set") } - request.Subject = make(map[string]string,1) - if (flags.Beamline!="") { - request.Subject["beamline"]=flags.Beamline + request.Subject = make(map[string]string, 1) + if flags.Beamline != "" { + request.Subject["beamline"] = flags.Beamline } else { - request.Subject["beamtimeId"]=flags.Beamtime + request.Subject["beamtimeId"] = flags.Beamtime } - request.AccessTypes = strings.Split(flags.AccessType,",") - for _,at:=range request.AccessTypes { - if at != "read" && at != "write" && !(at== "writeraw" && request.Subject["beamline"]!="") { - if (request.Subject["beamline"]!="") { - return request,errors.New("access type must be read, write or writeraw") + request.AccessTypes = strings.Split(flags.AccessType, ",") + for _, at := range request.AccessTypes { + if at != "read" && at != "write" && !(at == "writeraw" && request.Subject["beamline"] != "") { + if request.Subject["beamline"] != "" { + return request, errors.New("access type must be read, write or writeraw") } else { - return request,errors.New("access type must be read or write") + return request, errors.New("access type must be read or write") } } @@ -47,21 +48,20 @@ func userTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, err return } - func adminTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, err error) { - if flags.Beamline+flags.Beamtime!="" { - return request,errors.New("beamtime and beamline must not be set for admin token") + if flags.Beamline+flags.Beamtime != "" { + return request, errors.New("beamtime and beamline must not be set for admin token") } - request.AccessTypes = strings.Split(flags.AccessType,",") - for _,at:=range request.AccessTypes { - if at!="create" && at!="revoke" && at!="list" { - return request,errors.New("access type must be create,revoke of list") + request.AccessTypes = strings.Split(flags.AccessType, ",") + for _, at := range request.AccessTypes { + if at != "create" && at != "revoke" && at != "list" { + return request, errors.New("access type must be create,revoke of list") } } - request.Subject = make(map[string]string,1) - request.Subject["user"]="admin" + request.Subject = make(map[string]string, 1) + request.Subject["user"] = "admin" request.DaysValid = flags.DaysValid return @@ -83,7 +83,17 @@ func (cmd *command) CommandCreate_token() (err error) { return err } - token, err := server.Auth.PrepareAccessToken(request,userToken) + token, claims, err := server.Auth.PrepareAccessToken(request, userToken) + if err != nil { + return err + } + + record := database.TokenRecord{claims.Id, claims, token} + _, err = db.ProcessRequest(database.Request{ + DbName: database.KAdminDb, + Collection: database.KTokens, + Op: "create_record", + }, &record) if err != nil { return err } @@ -105,12 +115,11 @@ func getTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, userT return structs.IssueTokenRequest{}, false, errors.New("wrong token type") } if err != nil { - return structs.IssueTokenRequest{},false, err + return structs.IssueTokenRequest{}, false, err } return request, userToken, err } - func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) { var flags tokenFlags @@ -121,7 +130,6 @@ func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) { flagset.StringVar(&flags.AccessType, "access-types", "", "read/write/writeraw(beamline only) for user token") flagset.IntVar(&flags.DaysValid, "duration-days", 0, "token duration (in days)") - flagset.Parse(cmd.args) if printHelp(flagset) { @@ -129,10 +137,9 @@ func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) { } if flags.Type == "" { - return flags, errors.New("secret file missed ") + return flags, errors.New("access types missing") } - return flags, nil } diff --git a/authorizer/src/asapo_authorizer/cli/create_token_test.go b/authorizer/src/asapo_authorizer/cli/create_token_test.go index ca16f199e..a3e33caad 100644 --- a/authorizer/src/asapo_authorizer/cli/create_token_test.go +++ b/authorizer/src/asapo_authorizer/cli/create_token_test.go @@ -2,10 +2,12 @@ package cli import ( "asapo_authorizer/authorization" + "asapo_authorizer/database" "asapo_authorizer/server" "asapo_common/structs" "asapo_common/utils" "encoding/json" + "github.com/stretchr/testify/mock" "testing" "bytes" @@ -41,13 +43,28 @@ var tokenTests = []struct { func TestGenerateToken(t *testing.T) { server.Auth = authorization.NewAuth(utils.NewJWTAuth("secret_user"),utils.NewJWTAuth("secret_admin"),utils.NewJWTAuth("secret")) + mock_db := new(database.MockedDatabase) + db = mock_db + for _, test := range tokenTests { outBuf = new(bytes.Buffer) + + if test.ok { + req := database.Request{ + DbName: "asapo_admin", + Collection: "tokens", + Op: "create_record", + } + + mock_db.On("ProcessRequest", req, mock.Anything).Return([]byte(""), nil) + } + err := test.cmd.CommandCreate_token() if !test.ok { assert.NotNil(t, err, test.msg) continue } + assert.Nil(t, err, test.msg) var token structs.IssueTokenResponse json.Unmarshal(outBuf.(*bytes.Buffer).Bytes(), &token) @@ -63,5 +80,9 @@ func TestGenerateToken(t *testing.T) { } else { assert.Empty(t, token.Expires, test.msg) } + + mock_db.AssertExpectations(t) + mock_db.ExpectedCalls = nil + mock_db.Calls = nil } } diff --git a/authorizer/src/asapo_authorizer/database/database.go b/authorizer/src/asapo_authorizer/database/database.go index ed7ebd7db..187adeaa8 100644 --- a/authorizer/src/asapo_authorizer/database/database.go +++ b/authorizer/src/asapo_authorizer/database/database.go @@ -2,15 +2,24 @@ package database import "asapo_common/utils" + +const KAdminDb = "asapo_admin" +const KTokens = "tokens" + type Request struct { DbName string Collection string Op string - ExtraParam string +} + +type TokenRecord struct { + Id string `bson:"_id"` + *utils.CustomClaims + Token string } type Agent interface { - ProcessRequest(request Request) ([]byte, error) + ProcessRequest(request Request, extraParams ...interface{}) ([]byte, error) Ping() error Connect(string) error Close() diff --git a/authorizer/src/asapo_authorizer/database/mock_database.go b/authorizer/src/asapo_authorizer/database/mock_database.go index 150972dfb..e44cacaa7 100644 --- a/authorizer/src/asapo_authorizer/database/mock_database.go +++ b/authorizer/src/asapo_authorizer/database/mock_database.go @@ -24,8 +24,7 @@ func (db *MockedDatabase) Ping() error { return args.Error(0) } - -func (db *MockedDatabase) ProcessRequest(request Request) (answer []byte, err error) { - args := db.Called(request) +func (db *MockedDatabase) ProcessRequest(request Request, extraParams ...interface{}) (answer []byte, err error) { + args := db.Called(request,extraParams) return args.Get(0).([]byte), args.Error(1) } diff --git a/authorizer/src/asapo_authorizer/database/mongodb.go b/authorizer/src/asapo_authorizer/database/mongodb.go index 96abc4e9f..51d69165d 100644 --- a/authorizer/src/asapo_authorizer/database/mongodb.go +++ b/authorizer/src/asapo_authorizer/database/mongodb.go @@ -6,6 +6,7 @@ import ( "asapo_common/utils" "context" "errors" + "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "strings" @@ -107,7 +108,25 @@ func (db *Mongodb) checkDatabaseOperationPrerequisites(request Request) error { return nil } -func (db *Mongodb) ProcessRequest(request Request) (answer []byte, err error) { +func (db *Mongodb) createRecord(request Request, extra_params ...interface{}) ([]byte, error) { + if len(extra_params) != 1 { + return nil, errors.New("wrong number of parameters") + } + record := extra_params[0] + c := db.client.Database(request.DbName).Collection(request.Collection) + res, err := c.InsertOne(context.TODO(), record, options.InsertOne()) + if err != nil { + return nil, err + } + newId, ok := res.InsertedID.(primitive.ObjectID) + if ok { + return []byte(newId.Hex()), nil + } + return nil, nil +} + + +func (db *Mongodb) ProcessRequest(request Request,extraParams ...interface{}) (answer []byte, err error) { dbClientLock.RLock() defer dbClientLock.RUnlock() @@ -116,6 +135,8 @@ func (db *Mongodb) ProcessRequest(request Request) (answer []byte, err error) { } switch request.Op { + case "create_record": + return db.createRecord(request, extraParams...) } return nil, errors.New("Wrong db operation: " + request.Op) diff --git a/authorizer/src/asapo_authorizer/server/db_request_test.go b/authorizer/src/asapo_authorizer/server/db_request_test.go index 2f42f4627..4cd03bfb7 100644 --- a/authorizer/src/asapo_authorizer/server/db_request_test.go +++ b/authorizer/src/asapo_authorizer/server/db_request_test.go @@ -45,7 +45,7 @@ func TestProcessRequestTestSuite(t *testing.T) { func (suite *ProcessRequestTestSuite) TestProcessRequestWithConnectionError() { req := database.Request{} - suite.mock_db.On("ProcessRequest", req).Return([]byte(""), + suite.mock_db.On("ProcessRequest", req,mock.Anything).Return([]byte(""), &database.DBError{utils.StatusServiceUnavailable, ""}) ExpectReconnect(suite.mock_db) @@ -62,10 +62,9 @@ func (suite *ProcessRequestTestSuite) TestProcessRequestAddTokenToDb() { DbName: "test", Collection: "test", Op: "test", - ExtraParam: "test", } - suite.mock_db.On("ProcessRequest", req).Return([]byte("Hello"), nil) + suite.mock_db.On("ProcessRequest", req,mock.Anything).Return([]byte("Hello"), nil) _,err := ProcessRequestInDb(req) diff --git a/authorizer/src/asapo_authorizer/server/issue_token.go b/authorizer/src/asapo_authorizer/server/issue_token.go index 69fc63332..8b9bae253 100644 --- a/authorizer/src/asapo_authorizer/server/issue_token.go +++ b/authorizer/src/asapo_authorizer/server/issue_token.go @@ -62,7 +62,7 @@ func issueUserToken(w http.ResponseWriter, r *http.Request) { return } - token, err := Auth.PrepareAccessToken(request, true) + token, _, err := Auth.PrepareAccessToken(request, true) if err != nil { utils.WriteServerError(w, err, http.StatusInternalServerError) return diff --git a/authorizer/src/asapo_authorizer/server/server_nottested.go b/authorizer/src/asapo_authorizer/server/server_nottested.go index e87f62fff..a4dec9673 100644 --- a/authorizer/src/asapo_authorizer/server/server_nottested.go +++ b/authorizer/src/asapo_authorizer/server/server_nottested.go @@ -15,14 +15,16 @@ import ( "strconv" ) +func CreateDiscoveryService() { + discoveryService = discovery.CreateDiscoveryService(&http.Client{},"http://" + settings.DiscoveryServer) +} func Start() { mux := utils.NewRouter(listRoutes) ldapClient = new(ldap_client.OpenLdapClient) - discoveryService = discovery.CreateDiscoveryService(&http.Client{},"http://" + settings.DiscoveryServer) log.Info("Starting ASAPO Authorizer, version " + version.GetVersion()) - + CreateDiscoveryService() err := InitDB(new(database.Mongodb)) if err != nil { log.Error(err.Error()) -- GitLab