diff --git a/authorizer/src/asapo_authorizer/authorization/authorization.go b/authorizer/src/asapo_authorizer/authorization/authorization.go new file mode 100644 index 0000000000000000000000000000000000000000..768350e943f75b429406effb5b3a53ea1d551af6 --- /dev/null +++ b/authorizer/src/asapo_authorizer/authorization/authorization.go @@ -0,0 +1,75 @@ +package authorization + +import ( + "asapo_common/utils" + "encoding/json" + "github.com/rs/xid" + "time" +) + +type Auth struct { + authHMAC utils.Auth + authHMACAdmin utils.Auth + authJWT utils.Auth +} + +func NewAuth(authHMAC,authHMACAdmin,authJWT utils.Auth) *Auth { + return &Auth{authHMAC,authHMACAdmin,authJWT} +} + +func (auth *Auth) AdminAuth() utils.Auth { + return auth.authHMACAdmin +} + +func (auth *Auth) HmacAuth() utils.Auth { + return auth.authHMAC +} + +func (auth *Auth) JWTAuth() utils.Auth { + return auth.authJWT +} + +func subjectFromRequest(request TokenRequest) string { + for key,value := range request.Subject { + switch key { + case "beamline": + return "bl_" + value + case "beamtimeId": + return "bt_" + value + default: + return value + } + } + return "" +} + +func (auth *Auth) PrepareUserJWTToken(request TokenRequest) (string, error) { + var claims utils.CustomClaims + var extraClaim utils.AccessTokenExtraClaim + + claims.Subject = subjectFromRequest(request) + + extraClaim.AccessType = request.AccessType + claims.ExtraClaims = &extraClaim + claims.SetExpiration(time.Duration(request.DaysValid*24) * time.Hour) + uid := xid.New() + claims.Id = uid.String() + + return auth.authJWT.GenerateToken(&claims) + +} + +func UserTokenResponce(request TokenRequest, token string) []byte { + expires := "" + if request.DaysValid>0 { + expires = time.Now().Add(time.Duration(request.DaysValid*24) * time.Hour).UTC().Format(time.RFC3339) + } + answer := TokenResponce{ + Token: token, + AccessType: request.AccessType, + Sub: subjectFromRequest(request), + Expires: expires, + } + res, _ := json.Marshal(answer) + return res +} diff --git a/authorizer/src/asapo_authorizer/authorization/structs.go b/authorizer/src/asapo_authorizer/authorization/structs.go new file mode 100644 index 0000000000000000000000000000000000000000..aeec3c550b2a5ef11694c94cc15358675f151604 --- /dev/null +++ b/authorizer/src/asapo_authorizer/authorization/structs.go @@ -0,0 +1,14 @@ +package authorization + +type TokenRequest struct { + Subject map[string]string + DaysValid int + AccessType string +} + +type TokenResponce struct { + Token string + Sub string + AccessType string + Expires string +} diff --git a/authorizer/src/asapo_authorizer/cli/cli.go b/authorizer/src/asapo_authorizer/cli/cli.go new file mode 100644 index 0000000000000000000000000000000000000000..0851d568c782b2d97f80894b3afd280f0c5a1b64 --- /dev/null +++ b/authorizer/src/asapo_authorizer/cli/cli.go @@ -0,0 +1,60 @@ +// Package contains asapo commands that can be executed from command line. +// Every CommandXxxx function that is a member of a cmd struct processes asapo xxxx command +package cli + +import ( + "errors" + "flag" + "fmt" + "io" + "os" + "reflect" + "strings" +) + +var flHelp bool + +var outBuf io.Writer = os.Stdout + +func printHelp(f *flag.FlagSet) bool { + if flHelp { + f.Usage() + return true + } else { + return false + } +} + +// DoCommand takes command name as a parameter and executes corresponding to this name cmd method +func DoCommand(name string, args []string) error { + commandName := "Command" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:]) + cmd := new(command) + commandName = strings.ReplaceAll(commandName,"-","_") + methodVal := reflect.ValueOf(cmd).MethodByName(commandName) + if !methodVal.IsValid() { + return errors.New("wrong "+ProgramName+" command: " + name + "\nType '"+os.Args[0]+" -help'") + } + cmd.name = strings.ReplaceAll(name,"-","_") + cmd.args = args + + method := methodVal.Interface().(func() error) + + return method() +} + +// PrintAllCommands prints all available commands (found wihtin methods of cmd) +func PrintAllCommands() { + fmt.Fprintln(outBuf, "\nCommands:") + cmd := new(command) + CmdType := reflect.TypeOf(cmd) + for i := 0; i < CmdType.NumMethod(); i++ { + methodVal := CmdType.Method(i) + if strings.HasPrefix(methodVal.Name, "Command") { + method := methodVal.Func.Interface().(func(*command) error) + cmd.name = strings.ToLower(methodVal.Name)[7:] + cmd.name = strings.ReplaceAll(cmd.name,"_","-") + cmd.args = []string{"description"} + method(cmd) + } + } +} diff --git a/authorizer/src/asapo_authorizer/cli/command.go b/authorizer/src/asapo_authorizer/cli/command.go new file mode 100644 index 0000000000000000000000000000000000000000..6f8fc4f556ff1e420de679d953eab89a3c194654 --- /dev/null +++ b/authorizer/src/asapo_authorizer/cli/command.go @@ -0,0 +1,43 @@ +package cli + +import ( + "errors" + "flag" + "fmt" + "os" +) + +var ProgramName string + +// A command consists of a command name and arguments, passed to this command (all after program name ...) +type command struct { + name string + args []string +} + +// description prints description line and returns true if first command argument is "description". +func (cmd *command) description(d string) bool { + if len(cmd.args) == 1 && cmd.args[0] == "description" { + fmt.Fprintf(outBuf, " %-10s %s\n", cmd.name, d) + return true + } + return false +} + +func (cmd *command) errBadOptions(err string) error { + return errors.New(ProgramName + " " + cmd.name + ": " + err + "\nType '" +os.Args[0] +" " + cmd.name + " -help'") +} + +// createDefaultFlagset creates new flagset and adds default help behaviour. +func (cmd *command) createDefaultFlagset(description, args string) *flag.FlagSet { + + flags := flag.NewFlagSet(cmd.name, flag.ContinueOnError) + flags.BoolVar(&flHelp, "help", false, "Print usage") + flags.Usage = func() { + fmt.Fprintf(outBuf, "Usage:\t\n"+ProgramName+" %s "+args, cmd.name) + fmt.Fprintf(outBuf, "\n\n%s\n", description) + flags.PrintDefaults() + } + + return flags +} diff --git a/authorizer/src/asapo_authorizer/cli/command_test.go b/authorizer/src/asapo_authorizer/cli/command_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b62788adea6f9f9ce0a3334ec8b2ea7ac3e4856e --- /dev/null +++ b/authorizer/src/asapo_authorizer/cli/command_test.go @@ -0,0 +1,41 @@ +package cli + +import ( + "asapo_authorizer/authorization" + "asapo_authorizer/server" + "asapo_common/utils" + "bytes" + "testing" + "github.com/stretchr/testify/assert" +) + +var CommandTests = []struct { + cmd command + ok bool + msg string +}{ + {command{"create-token", []string{"-type", "create-user-token", "-beamtime","123","-access-type","read","-duration-days","1"}}, true,"ok"}, + {command{"dummy", []string{"description"}}, false,"wrong command"}, +} + +func TestCommand(t *testing.T) { + outBuf = new(bytes.Buffer) + server.Auth = authorization.NewAuth(utils.NewHMACAuth("secret"),utils.NewHMACAuth("secret"),utils.NewJWTAuth("secret")) + + for _, test := range CommandTests { + outBuf.(*bytes.Buffer).Reset() + err := DoCommand(test.cmd.name, test.cmd.args) + if !test.ok { + assert.NotNil(t, err, "Should be error",test.msg) + } else { + assert.Nil(t, err, "Should be ok",test.msg) + } + } + +} + +func TestPrintAllCommands(t *testing.T) { + outBuf = new(bytes.Buffer) + PrintAllCommands() + assert.Contains(t, outBuf.(*bytes.Buffer).String(), "token", "all commands must have token") +} diff --git a/authorizer/src/asapo_authorizer/cli/create_token.go b/authorizer/src/asapo_authorizer/cli/create_token.go new file mode 100644 index 0000000000000000000000000000000000000000..6481f22f2b012cff02b183fbcbf7a3873cfa356d --- /dev/null +++ b/authorizer/src/asapo_authorizer/cli/create_token.go @@ -0,0 +1,146 @@ +package cli + +import ( + "asapo_authorizer/authorization" + "asapo_authorizer/server" + "errors" + "os" + "fmt" + "asapo_common/utils" +) + +type tokenFlags struct { + Type string + AccessType string + Beamtime string + Beamline string + DaysValid int +} + +func generateToken(id string,secret string) string { + hmac := utils.NewHMACAuth(secret) + token,err := hmac.GenerateToken(&id) + + if (err!=nil) { + fmt.Println(err.Error()) + } + return token +} + +func isUserRequest(reqType string) bool { + return reqType == "user-token" +} + +func isAdminRequest(reqType string) bool { + return reqType == "admin-token" +} + + +func userTokenRequest(flags tokenFlags) (request authorization.TokenRequest, err error) { + if (flags.Beamline=="" && flags.Beamtime=="") || (flags.Beamline!="" && flags.Beamtime!="") { + return request,errors.New("beamtime or beamline must be set") + } + if flags.AccessType!="read" && flags.AccessType!="write" { + return request,errors.New("access type must be read of write") + } + + if flags.DaysValid<=0 { + return request,errors.New("expiration period must be set") + } + + request.Subject = make(map[string]string,1) + if (flags.Beamline!="") { + request.Subject["beamline"]=flags.Beamline + } else { + request.Subject["beamtimeId"]=flags.Beamtime + } + request.AccessType = flags.AccessType + request.DaysValid = flags.DaysValid + + return +} + + +func adminTokenRequest(flags tokenFlags) (request authorization.TokenRequest, err error) { + if flags.Beamline+flags.Beamtime!="" { + return request,errors.New("beamtime and beamline must not be set for admin token") + } + if flags.AccessType!="create" && flags.AccessType!="revoke" && flags.AccessType!="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.AccessType = flags.AccessType + request.DaysValid = flags.DaysValid + + return +} + +// GenerateToken generates token for consumers +func (cmd *command) CommandCreate_token() (err error) { + message_string := "Generate token" + if cmd.description(message_string) { + return nil + } + + flags, err := cmd.parseTokenFlags(message_string) + if err != nil { + return err + } + + request, err := getTokenRequest(flags) + if err != nil { + return err + } + + token, err := server.Auth.PrepareUserJWTToken(request) + if err != nil { + return err + } + + answer := authorization.UserTokenResponce(request, token) + fmt.Fprintf(outBuf, "%s\n", string(answer)) + return nil +} + +func getTokenRequest(flags tokenFlags) (request authorization.TokenRequest, err error) { + switch flags.Type { + case "user-token": + request, err = userTokenRequest(flags) + case "admin-token": + request, err = adminTokenRequest(flags) + default: + return authorization.TokenRequest{}, errors.New("wrong token type") + } + if err != nil { + return authorization.TokenRequest{}, err + } + return request, err +} + + +func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) { + + var flags tokenFlags + flagset := cmd.createDefaultFlagset(message_string, "") + flagset.StringVar(&flags.Type, "type", "", "token type") + flagset.StringVar(&flags.Beamtime, "beamtime", "", "beamtime for user token") + flagset.StringVar(&flags.Beamline, "beamline", "", "beamline for user token") + flagset.StringVar(&flags.AccessType, "access-type", "", "read/write for user token") + flagset.IntVar(&flags.DaysValid, "duration-days", 0, "token duration (in days)") + + + flagset.Parse(cmd.args) + + if printHelp(flagset) { + os.Exit(0) + } + + if flags.Type == "" { + return flags, errors.New("secret file missed ") + } + + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..68396195a23710daad4e1a8c8ed974b4eec8840d --- /dev/null +++ b/authorizer/src/asapo_authorizer/cli/create_token_test.go @@ -0,0 +1,52 @@ +package cli + +import ( + "asapo_authorizer/authorization" + "asapo_authorizer/server" + "asapo_common/utils" + "encoding/json" + "fmt" + "testing" + + "bytes" + "github.com/stretchr/testify/assert" +) + +var tokenTests = []struct { + cmd command + ok bool + tokenAccessType string + tokenSubject string + tokenExpires bool + msg string +}{ +// {command{args: []string{"-type"}}, false, "", "", "not enough parameters"}, +// {command{args: []string{"-type", "user-token", "-beamtime","123","-access-type","read","-duration-days","10"}}, +// true, "read", "bt_123", true,"user token ok"}, + {command{args: []string{"-type", "admin-token","-access-type","create"}}, + true, "create", "admin", false,"admin token ok"}, +} + +func TestGenerateToken(t *testing.T) { + outBuf = new(bytes.Buffer) + server.Auth = authorization.NewAuth(utils.NewHMACAuth("secret"),utils.NewHMACAuth("secret"),utils.NewJWTAuth("secret")) + for _, test := range tokenTests { + err := test.cmd.CommandCreate_token() + fmt.Println(err) + if !test.ok { + assert.NotNil(t, err, test.msg) + continue + } + assert.Nil(t, err, test.msg) + var token authorization.TokenResponce + json.Unmarshal(outBuf.(*bytes.Buffer).Bytes(), &token) + assert.Equal(t, test.tokenSubject, token.Sub, test.msg) + assert.Equal(t, test.tokenAccessType, token.AccessType, test.msg) + if test.tokenExpires { + assert.Equal(t, true, len(token.Expires)>0, test.msg) + } else { + assert.Empty(t, token.Expires, test.msg) + } + + } +} diff --git a/authorizer/src/asapo_authorizer/cli/daemon.go b/authorizer/src/asapo_authorizer/cli/daemon.go new file mode 100644 index 0000000000000000000000000000000000000000..46bf9a3e7bc4bdd1cb121bd21c819f21a9749921 --- /dev/null +++ b/authorizer/src/asapo_authorizer/cli/daemon.go @@ -0,0 +1,18 @@ +package cli + +import ( + "asapo_authorizer/server" +) + + +func (cmd *command) CommandDaemon() error { + + message_string := "Start daemon (default)" + if cmd.description(message_string) { + return nil + } + + server.Start() + + return nil +} diff --git a/authorizer/src/asapo_authorizer/common/structs.go b/authorizer/src/asapo_authorizer/common/structs.go new file mode 100644 index 0000000000000000000000000000000000000000..805d0c79aadd885ddb6ed76b1e678c10a63bbe45 --- /dev/null +++ b/authorizer/src/asapo_authorizer/common/structs.go @@ -0,0 +1 @@ +package common diff --git a/authorizer/src/asapo_authorizer/main/authorizer.go b/authorizer/src/asapo_authorizer/main/authorizer.go index 9f4329909333d6dac5c88966ccbc398497856941..57261418da9c80661c09aa8a5d57db7c2ea693e1 100644 --- a/authorizer/src/asapo_authorizer/main/authorizer.go +++ b/authorizer/src/asapo_authorizer/main/authorizer.go @@ -3,19 +3,22 @@ package main import ( - log "asapo_common/logger" + "asapo_authorizer/cli" "asapo_authorizer/server" + log "asapo_common/logger" "asapo_common/version" "flag" "os" ) -func PrintUsage() { - log.Fatal("Usage: " + os.Args[0] + " -config <config file>") -} +var ( + flHelp = flag.Bool("help", false, "Print usage") +) func main() { - var fname = flag.String("config", "", "config file path") + cli.ProgramName = "asapo-authorizer" + + var fname = flag.String("config", "", "config file path (mandatory)") if ret := version.ShowVersion(os.Stdout, "ASAPO Authorizer"); ret { return @@ -24,10 +27,16 @@ func main() { log.SetSoucre("authorizer") flag.Parse() - if *fname == "" { - PrintUsage() + if *flHelp { + flag.Usage() + cli.PrintAllCommands() + return } + if *fname=="" { + log.Fatal("config file path is missed") + + } logLevel, err := server.ReadConfig(*fname) if err != nil { log.Fatal(err.Error()) @@ -35,5 +44,12 @@ func main() { log.SetLevel(logLevel) - server.Start() + if len(flag.Args()) == 0 { + server.Start() + } + + if err := cli.DoCommand(flag.Arg(0), flag.Args()[1:]); err != nil { + log.Fatal(err.Error()) + } + } diff --git a/authorizer/src/asapo_authorizer/server/authorize.go b/authorizer/src/asapo_authorizer/server/authorize.go index edcf0b703767f142d746a56235155bef6b942e91..89020bf127c793d08acfb010c759a6da98d3d444 100644 --- a/authorizer/src/asapo_authorizer/server/authorize.go +++ b/authorizer/src/asapo_authorizer/server/authorize.go @@ -155,10 +155,10 @@ func needHostAuthorization(creds SourceCredentials) bool { func authorizeByToken(creds SourceCredentials) error { var token_expect string if (creds.BeamtimeId != "auto") { - token_expect, _ = authHMAC.GenerateToken(&creds.BeamtimeId) + token_expect, _ = Auth.HmacAuth().GenerateToken(&creds.BeamtimeId) } else { key := "bl_" + creds.Beamline - token_expect, _ = authHMAC.GenerateToken(&key) + token_expect, _ = Auth.HmacAuth().GenerateToken(&key) } var err_string string diff --git a/authorizer/src/asapo_authorizer/server/authorize_test.go b/authorizer/src/asapo_authorizer/server/authorize_test.go index 01983cb0034e90fdde52446947d95718be9ab37b..8fdf09e583f0ec95aeaf007e04ae23933cf02397 100644 --- a/authorizer/src/asapo_authorizer/server/authorize_test.go +++ b/authorizer/src/asapo_authorizer/server/authorize_test.go @@ -1,6 +1,7 @@ package server import ( + "asapo_authorizer/authorization" "asapo_authorizer/common" "asapo_authorizer/ldap_client" "asapo_common/utils" @@ -15,14 +16,14 @@ import ( ) func prepareToken(payload string) string{ - authHMAC = utils.NewHMACAuth("secret") - token, _ := authHMAC.GenerateToken(&payload) + Auth = authorization.NewAuth(utils.NewHMACAuth("secret"),nil,nil) + token, _ := Auth.HmacAuth().GenerateToken(&payload) return token } func prepareAdminToken(payload string) string{ - authHMACAdmin = utils.NewHMACAuth("secret_admin") - token, _ := authHMACAdmin.GenerateToken(&payload) + Auth = authorization.NewAuth(nil,utils.NewHMACAuth("secret_admin"),nil) + token, _ := Auth.AdminAuth().GenerateToken(&payload) return token } @@ -205,7 +206,7 @@ var authTests = [] struct { func TestAuthorize(t *testing.T) { ldapClient = mockClient allowBeamlines([]beamtimeMeta{}) - + Auth = authorization.NewAuth(utils.NewHMACAuth("secret"),utils.NewHMACAuth("secret"),utils.NewJWTAuth("secret")) expected_uri := "expected_uri" expected_base := "expected_base" allowed_ips := []string{"127.0.0.1"} diff --git a/authorizer/src/asapo_authorizer/server/folder_token.go b/authorizer/src/asapo_authorizer/server/folder_token.go index f693640448b5ccac87d3fc54aaddcf4bd3fe95f9..be3d6b7ec7e693f9c67b17913dd6d0ada394f5d7 100644 --- a/authorizer/src/asapo_authorizer/server/folder_token.go +++ b/authorizer/src/asapo_authorizer/server/folder_token.go @@ -30,7 +30,7 @@ func prepareJWTToken(request folderTokenRequest) (string,error) { extraClaim.RootFolder = request.Folder claims.ExtraClaims = &extraClaim claims.SetExpiration(time.Duration(settings.FolderTokenDurationMin) * time.Minute) - return authJWT.GenerateToken(&claims) + return Auth.JWTAuth().GenerateToken(&claims) } @@ -39,7 +39,7 @@ func folderTokenResponce(token string) []byte{ } func checkBeamtimeToken(request folderTokenRequest) error { - token_expect, _ := authHMAC.GenerateToken(&request.BeamtimeId) + token_expect, _ := Auth.HmacAuth().GenerateToken(&request.BeamtimeId) var err_string string if request.Token != token_expect { err_string = "wrong token for beamtime " + request.BeamtimeId diff --git a/authorizer/src/asapo_authorizer/server/folder_token_test.go b/authorizer/src/asapo_authorizer/server/folder_token_test.go index 2b45e61c25040a4b2f0816f7502556686fcea11c..61e7d8f5ae09ad8de9f5b4502f888dc8223d7557 100644 --- a/authorizer/src/asapo_authorizer/server/folder_token_test.go +++ b/authorizer/src/asapo_authorizer/server/folder_token_test.go @@ -1,6 +1,7 @@ package server import ( + "asapo_authorizer/authorization" "asapo_common/utils" "github.com/stretchr/testify/assert" "io/ioutil" @@ -31,6 +32,8 @@ func TestFolderToken(t *testing.T) { allowBeamlines([]beamtimeMeta{}) settings.RootBeamtimesFolder ="." settings.CurrentBeamlinesFolder="." + Auth = authorization.NewAuth(utils.NewHMACAuth("secret"),utils.NewHMACAuth("secret"),utils.NewJWTAuth("secret")) + os.MkdirAll(filepath.Clean("tf/gpfs/bl1/2019/data/test"), os.ModePerm) os.MkdirAll(filepath.Clean("tf/gpfs/bl1/2019/data/test_online"), os.ModePerm) @@ -41,7 +44,6 @@ func TestFolderToken(t *testing.T) { defer os.RemoveAll("bl1") for _, test := range fodlerTokenTests { - authJWT = utils.NewJWTAuth("secret") abs_path:=settings.RootBeamtimesFolder + string(filepath.Separator)+test.root_folder request := makeRequest(folderTokenRequest{abs_path,test.beamtime_id,test.token}) if test.status == http.StatusBadRequest { diff --git a/authorizer/src/asapo_authorizer/server/issue_token.go b/authorizer/src/asapo_authorizer/server/issue_token.go index 898783522ed6c76a7cec0faa794f809cd0b8bcbb..aa863eb3a85bdb00d705959b411baa293f2c7839 100644 --- a/authorizer/src/asapo_authorizer/server/issue_token.go +++ b/authorizer/src/asapo_authorizer/server/issue_token.go @@ -1,100 +1,56 @@ package server import ( + "asapo_authorizer/authorization" log "asapo_common/logger" "asapo_common/utils" - "encoding/json" "errors" - "github.com/rs/xid" "net/http" - "time" ) -type userTokenRequest struct { - BeamtimeId string - Beamtimeline string - DaysValid int - Role string -} - -type userToken struct { - Token string - AccessType string - Expires string -} - -func prepareUserJWTToken(request userTokenRequest) (string, error) { - var claims utils.CustomClaims - var extraClaim utils.AccessTokenExtraClaim - - if request.Beamtimeline != "" { - claims.Subject = "bl_" + request.Beamtimeline - } else { - claims.Subject = "bt_" + request.BeamtimeId - } - extraClaim.Role = request.Role - claims.ExtraClaims = &extraClaim - claims.SetExpiration(time.Duration(request.DaysValid*24) * time.Hour) - uid := xid.New() - claims.Id = uid.String() - - return authJWT.GenerateToken(&claims) - -} - -func extractUserTokenrequest(r *http.Request) (userTokenRequest, error) { - var request userTokenRequest - err := utils.ExtractRequest(r, &request) +func extractUserTokenrequest(r *http.Request) (request authorization.TokenRequest, err error) { + err = utils.ExtractRequest(r, &request) if err != nil { - return userTokenRequest{}, err + return request, err } - if request.BeamtimeId == "" && request.Beamtimeline == "" { - return userTokenRequest{}, errors.New("missing beamtime/beamline") + if request.Subject["beamtimeId"] == "" && request.Subject["beamline"] == "" { + return request, errors.New("missing beamtime/beamline") } - if request.BeamtimeId != "" && request.Beamtimeline != "" { - return userTokenRequest{}, errors.New("set only one of beamtime/beamline") + if request.Subject["beamtimeId"] != "" && request.Subject["beamline"] != "" { + return request, errors.New("set only one of beamtime/beamline") } - if request.Role != "read" && request.Role != "write" { - return userTokenRequest{}, errors.New("wrong role " + request.Role) + if request.AccessType != "read" && request.AccessType != "write" { + return request, errors.New("wrong access type " + request.AccessType) } return request, nil } -func userTokenResponce(request userTokenRequest, token string) []byte { - answer := userToken{ - Token: token, - AccessType: request.Role, - Expires: time.Now().Add(time.Duration(request.DaysValid*24) * time.Hour).UTC().Format(time.RFC3339), - } - res, _ := json.Marshal(answer) - return res -} -func authorisedUserToken(w http.ResponseWriter, r *http.Request) { - authHMACAdmin.ProcessAuth(routeUserToken, "admin")(w, r) +func routeAuthorisedTokenIssue(w http.ResponseWriter, r *http.Request) { + Auth.AdminAuth().ProcessAuth(issueUserToken, "admin")(w, r) } -func routeUserToken(w http.ResponseWriter, r *http.Request) { +func issueUserToken(w http.ResponseWriter, r *http.Request) { request, err := extractUserTokenrequest(r) if err != nil { utils.WriteServerError(w, err, http.StatusBadRequest) return } - token, err := prepareUserJWTToken(request) + token, err := Auth.PrepareUserJWTToken(request) if err != nil { utils.WriteServerError(w, err, http.StatusInternalServerError) return } - log.Debug("generated user token for beamtime " + request.BeamtimeId) + log.Debug("generated user token ") - answer := userTokenResponce(request, token) + answer := authorization.UserTokenResponce(request, token) w.WriteHeader(http.StatusOK) w.Write(answer) } diff --git a/authorizer/src/asapo_authorizer/server/issue_token_test.go b/authorizer/src/asapo_authorizer/server/issue_token_test.go index f85e92b35c4d8138154681d117e955d6c2394462..26de51a2e4397b4b0e44827252d83da00e6b3555 100644 --- a/authorizer/src/asapo_authorizer/server/issue_token_test.go +++ b/authorizer/src/asapo_authorizer/server/issue_token_test.go @@ -1,6 +1,7 @@ package server import ( + "asapo_authorizer/authorization" "asapo_common/utils" "encoding/json" "fmt" @@ -12,9 +13,8 @@ import ( ) var IssueTokenTests = [] struct { - beamtimeId string - beamline string - subject string + requestSubject map[string]string + tokenSubject string role string validDays int adminToken string @@ -22,33 +22,33 @@ var IssueTokenTests = [] struct { status int message string }{ - {"test", "","bt_test","read",180,prepareAdminToken("admin"),"aaa",http.StatusOK,"read for beamtime"}, - {"test", "","bt_test","read",90,prepareAdminToken("admin"),"aaa",http.StatusOK,"write for beamtime"}, - {"", "test","bl_test","read",180,prepareAdminToken("admin"),"aaa",http.StatusOK,"read for beamline"}, - {"test", "test","","read",180,prepareAdminToken("admin"),"",http.StatusBadRequest,"both beamline/beamtime given"}, - {"", "","","read",180,prepareAdminToken("admin"),"",http.StatusBadRequest,"beamline or beamtime not given"}, - {"test", "","","bla",180,prepareAdminToken("admin"),"",http.StatusBadRequest,"wrong role"}, - {"test", "","","read",180,prepareAdminToken("bla"),"",http.StatusUnauthorized,"wrong admin token"}, + {map[string]string{"beamtimeId":"test"},"bt_test","read",180,prepareAdminToken("admin"),"aaa",http.StatusOK,"read for beamtime"}, + {map[string]string{"beamtimeId":"test"},"bt_test","read",90,prepareAdminToken("admin"),"aaa",http.StatusOK,"write for beamtime"}, + {map[string]string{"beamline":"test"},"bl_test","read",180,prepareAdminToken("admin"),"aaa",http.StatusOK,"read for beamline"}, + {map[string]string{"blabla":"test"},"","read",180,prepareAdminToken("admin"),"",http.StatusBadRequest,"beamline or beamtime not given"}, + {map[string]string{"beamtimeId":"test"},"","bla",180,prepareAdminToken("admin"),"",http.StatusBadRequest,"wrong role"}, + {map[string]string{"beamtimeId":"test"},"","read",180,prepareAdminToken("bla"),"",http.StatusUnauthorized,"wrong admin token"}, } func TestIssueToken(t *testing.T) { - authJWT = utils.NewJWTAuth("secret") - authHMACAdmin = utils.NewHMACAuth("secret_admin") + authJWT := utils.NewJWTAuth("secret") + authHMACAdmin := utils.NewHMACAuth("secret_admin") + Auth = authorization.NewAuth(nil,authHMACAdmin,authJWT) for _, test := range IssueTokenTests { - request := makeRequest(userTokenRequest{test.beamtimeId,test.beamline,test.validDays,test.role}) + request := makeRequest(authorization.TokenRequest{test.requestSubject,test.validDays,test.role}) w := doPostRequest("/admin/issue",request,authHMACAdmin.Name()+" "+test.adminToken) if w.Code == http.StatusOK { body, _ := ioutil.ReadAll(w.Body) - var token userToken + var token authorization.TokenResponce json.Unmarshal(body,&token) claims,_ := utils.CheckJWTToken(token.Token,"secret") cclaims,_:= claims.(*utils.CustomClaims) var extra_claim utils.AccessTokenExtraClaim utils.MapToStruct(claims.(*utils.CustomClaims).ExtraClaims.(map[string]interface{}), &extra_claim) - assert.Equal(t, cclaims.Subject , test.subject, test.message) + assert.Equal(t, cclaims.Subject , test.tokenSubject, test.message) assert.True(t, cclaims.ExpiresAt-time.Now().Unix()>int64(test.validDays)*24*60*60-10, test.message) assert.True(t, cclaims.ExpiresAt-time.Now().Unix()<int64(test.validDays)*24*60*60+10, test.message) - assert.Equal(t, extra_claim.Role , test.role, test.message) + assert.Equal(t, extra_claim.AccessType, test.role, test.message) assert.NotEmpty(t, cclaims.Id , test.message) } else { body, _ := ioutil.ReadAll(w.Body) diff --git a/authorizer/src/asapo_authorizer/server/listroutes.go b/authorizer/src/asapo_authorizer/server/listroutes.go index 7282f7a7498c9a82892317953fa32d89c872a9af..e640c8a8acb799afc725cea2a0993d7155e45cf9 100644 --- a/authorizer/src/asapo_authorizer/server/listroutes.go +++ b/authorizer/src/asapo_authorizer/server/listroutes.go @@ -27,6 +27,6 @@ var listRoutes = utils.Routes{ "User Token", "POST", "/admin/issue", - authorisedUserToken, + routeAuthorisedTokenIssue, }, } diff --git a/authorizer/src/asapo_authorizer/server/server.go b/authorizer/src/asapo_authorizer/server/server.go index 6e5c6d8a6f7721cc8e4bf663939e39dc15c4af9e..0efe071ad23d66922020a4c4cc785c53416f1536 100644 --- a/authorizer/src/asapo_authorizer/server/server.go +++ b/authorizer/src/asapo_authorizer/server/server.go @@ -1,8 +1,8 @@ package server import ( + "asapo_authorizer/authorization" "asapo_authorizer/ldap_client" - "asapo_common/utils" ) type beamtimeMeta struct { @@ -32,7 +32,5 @@ type serverSettings struct { var settings serverSettings var ldapClient ldap_client.LdapClient -var authHMAC utils.Auth -var authHMACAdmin utils.Auth -var authJWT utils.Auth +var Auth *authorization.Auth diff --git a/authorizer/src/asapo_authorizer/server/server_nottested.go b/authorizer/src/asapo_authorizer/server/server_nottested.go index c9f2667bcd5d9e93699e6053e41b96de04db420d..01de58ec8778d6aecd74413a38057a64f0cf5156 100644 --- a/authorizer/src/asapo_authorizer/server/server_nottested.go +++ b/authorizer/src/asapo_authorizer/server/server_nottested.go @@ -3,6 +3,7 @@ package server import ( + "asapo_authorizer/authorization" "asapo_authorizer/ldap_client" log "asapo_common/logger" "asapo_common/utils" @@ -20,17 +21,17 @@ func Start() { log.Fatal(http.ListenAndServe(":"+strconv.Itoa(settings.Port), http.HandlerFunc(mux.ServeHTTP))) } -func createAuth() (utils.Auth, utils.Auth, utils.Auth, error) { +func createAuth() (*authorization.Auth,error) { secret, err := utils.ReadFirstStringFromFile(settings.UserSecretFile) if err != nil { - return nil, nil, nil, err + return nil, err } adminSecret, err := utils.ReadFirstStringFromFile(settings.AdminSecretFile) if err != nil { - return nil, nil, nil, err + return nil, err } - return utils.NewHMACAuth(adminSecret), utils.NewHMACAuth(secret), utils.NewJWTAuth(secret), nil + return authorization.NewAuth(utils.NewHMACAuth(adminSecret), utils.NewHMACAuth(secret), utils.NewJWTAuth(secret)),nil } func ReadConfig(fname string) (log.Level, error) { @@ -51,7 +52,7 @@ func ReadConfig(fname string) (log.Level, error) { } var err error - authHMACAdmin, authHMAC, authJWT, err = createAuth() + Auth, err = createAuth() if err != nil { return log.FatalLevel, err } diff --git a/common/go/src/asapo_common/utils/structs.go b/common/go/src/asapo_common/utils/structs.go index e758e6aede6f57affa937ee0bb25240df7beeff0..2280212e1079483cdc27da3b838be3514f2a965a 100644 --- a/common/go/src/asapo_common/utils/structs.go +++ b/common/go/src/asapo_common/utils/structs.go @@ -5,5 +5,5 @@ type FolderTokenTokenExtraClaim struct { } type AccessTokenExtraClaim struct { - Role string + AccessType string }