diff --git a/CMakeModules/testing_cpp.cmake b/CMakeModules/testing_cpp.cmake index 23a4457ac3af624949eeba0f4b3c9a55900820c3..63dae68e4cf2c706b2a245cbbbd8ef8de9419126 100644 --- a/CMakeModules/testing_cpp.cmake +++ b/CMakeModules/testing_cpp.cmake @@ -4,6 +4,7 @@ endif () set (TOKENS "ASAPO_TEST_RW_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMTkyMXJqaXB0MzVja3MzYTEwZyIsInN1YiI6ImJ0X2FzYXBvX3Rlc3QiLCJFeHRyYUNsYWltcyI6eyJBY2Nlc3NUeXBlcyI6WyJyZWFkIiwid3JpdGUiXX19.3PFdG0f48yKrOyJwPErYcewpcbZgnd8rBmBphw_kdJ0") set (TOKENS "${TOKENS};ASAPO_CREATE_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMTkyYzMzaXB0Mzdkb3IzYmZjZyIsInN1YiI6ImFkbWluIiwiRXh0cmFDbGFpbXMiOnsiQWNjZXNzVHlwZXMiOlsiY3JlYXRlIl19fQ.AI41cZ7dZL0g-rrdKIQgd7ijjzuyH1Fm0xojCXwLNBo") +set (TOKENS "${TOKENS};ASAPO_REVOKE_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjNWFxOHVyaXB0MzV0aG9raDFwMCIsInN1YiI6ImFkbWluIiwiRXh0cmFDbGFpbXMiOnsiQWNjZXNzVHlwZXMiOlsicmV2b2tlIl19fQ.GNje7w6biX0-ynltRr81p5SBSWwmKdDwGfs-adb094Q") set (TOKENS "${TOKENS};C20180508_000_COM20181_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMTkyaDRiaXB0Mzd1cGo1aDdlMCIsInN1YiI6ImJ0X2MyMDE4MDUwOC0wMDAtQ09NMjAxODEiLCJFeHRyYUNsYWltcyI6eyJBY2Nlc3NUeXBlcyI6WyJyZWFkIiwid3JpdGUiXX19.yONpjW2ybZMc9E9Eu4Hmn1roVR-mxf2OQQyXfnel5C8") set (TOKENS "${TOKENS};BT11000015_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMTkyajZqaXB0MzA3aHU1amwxZyIsInN1YiI6ImJ0XzExMDAwMDE1IiwiRXh0cmFDbGFpbXMiOnsiQWNjZXNzVHlwZXMiOlsicmVhZCJdfX0.kVs669HAS4sj9VAZk8pWTLrYNQp46mOnH4id4-_qd9g") set (TOKENS "${TOKENS};BT11000016_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMTkyajQzaXB0MzA3OWxwc3Z2ZyIsInN1YiI6ImJ0XzExMDAwMDE2IiwiRXh0cmFDbGFpbXMiOnsiQWNjZXNzVHlwZXMiOlsicmVhZCJdfX0.mpTVGtcdR0l4NaeHFTf16iWrfMYaLzh2pAjN5muil6Q") diff --git a/authorizer/src/asapo_authorizer/server/authorize.go b/authorizer/src/asapo_authorizer/server/authorize.go index e8faa9caaef9e2fa14a35f58561579e23fe3157d..aaea9b0951278067534f4f6b1486dbd84e3340af 100644 --- a/authorizer/src/asapo_authorizer/server/authorize.go +++ b/authorizer/src/asapo_authorizer/server/authorize.go @@ -14,9 +14,9 @@ import ( type SourceCredentials struct { BeamtimeId string Beamline string - DataSource string + DataSource string Token string - Type string + Type string } type authorizationRequest struct { @@ -26,15 +26,14 @@ type authorizationRequest struct { func getSourceCredentials(request authorizationRequest) (SourceCredentials, error) { - vals := strings.Split(request.SourceCredentials, "%") - nvals:=len(vals) + nvals := len(vals) if nvals < 5 { return SourceCredentials{}, errors.New("cannot get source credentials from " + request.SourceCredentials) } - creds := SourceCredentials{Type:vals[0], BeamtimeId: vals[1], Beamline: vals[2], Token:vals[nvals-1]} - creds.DataSource=strings.Join(vals[3:nvals-1],"%") + creds := SourceCredentials{Type: vals[0], BeamtimeId: vals[1], Beamline: vals[2], Token: vals[nvals-1]} + creds.DataSource = strings.Join(vals[3:nvals-1], "%") if creds.DataSource == "" { creds.DataSource = "detector" } @@ -54,7 +53,6 @@ func getSourceCredentials(request authorizationRequest) (SourceCredentials, erro return creds, nil } - func splitHost(hostPort string) string { s := strings.Split(hostPort, ":") return s[0] @@ -95,7 +93,7 @@ func beamtimeMetaFromMatch(match string) (common.BeamtimeMeta, error) { return common.BeamtimeMeta{}, errors.New("skipped fodler") } - bt.OfflinePath = common.Settings.RootBeamtimesFolder+string(filepath.Separator)+match + bt.OfflinePath = common.Settings.RootBeamtimesFolder + string(filepath.Separator) + match bt.Beamline, bt.BeamtimeId = vars[2], vars[5] return bt, nil @@ -107,10 +105,10 @@ func findBeamtimeInfoFromId(beamtime_id string) (common.BeamtimeMeta, error) { matches, err := filepath.Glob(common.Settings.RootBeamtimesFolder + pattern + beamtime_id) if err != nil || len(matches) == 0 { - return common.BeamtimeMeta{}, errors.New("Cannot find beamline for "+beamtime_id) + return common.BeamtimeMeta{}, errors.New("Cannot find beamline for " + beamtime_id) } - for _, match := range (matches) { + for _, match := range matches { btInfo, err := beamtimeMetaFromMatch(match) if err != nil { continue @@ -119,12 +117,12 @@ func findBeamtimeInfoFromId(beamtime_id string) (common.BeamtimeMeta, error) { return btInfo, nil } } - return common.BeamtimeMeta{}, errors.New("Cannot find beamline for "+beamtime_id) + return common.BeamtimeMeta{}, errors.New("Cannot find beamline for " + beamtime_id) } -func findMetaFileInFolder(beamline string,iscommissioning bool) (string, string, error){ +func findMetaFileInFolder(beamline string, iscommissioning bool) (string, string, error) { sep := string(filepath.Separator) - var pattern,folder string + var pattern, folder string if !iscommissioning { pattern = "beamtime-metadata-*.json" folder = "current" @@ -135,18 +133,18 @@ func findMetaFileInFolder(beamline string,iscommissioning bool) (string, string, online_path := common.Settings.CurrentBeamlinesFolder + sep + beamline + sep + folder matches, err := filepath.Glob(online_path + sep + pattern) if err != nil { - return "","", err + return "", "", err } if len(matches) != 1 { - return "","", errors.New("should be one beamtime-metadata file in folder") + return "", "", errors.New("should be one beamtime-metadata file in folder") } - return matches[0],online_path, nil + return matches[0], online_path, nil } -func findBeamtimeMetaFromBeamline(beamline string,iscommissioning bool) (meta common.BeamtimeMeta, err error) { - fName,online_path, err := findMetaFileInFolder(beamline,iscommissioning) - if (err != nil) { +func findBeamtimeMetaFromBeamline(beamline string, iscommissioning bool) (meta common.BeamtimeMeta, err error) { + fName, online_path, err := findMetaFileInFolder(beamline, iscommissioning) + if err != nil { return common.BeamtimeMeta{}, err } @@ -155,11 +153,11 @@ func findBeamtimeMetaFromBeamline(beamline string,iscommissioning bool) (meta co } else { meta, err = beamtimeMetaFromJson(fName) } - if (err != nil) { + if err != nil { return common.BeamtimeMeta{}, err } - if meta.BeamtimeId == "" || meta.OfflinePath=="" || meta.Beamline == ""{ + if meta.BeamtimeId == "" || meta.OfflinePath == "" || meta.Beamline == "" { return common.BeamtimeMeta{}, errors.New("cannot set meta fields from beamtime file") } @@ -172,23 +170,23 @@ func alwaysAllowed(creds SourceCredentials) (common.BeamtimeMeta, bool) { if pair.BeamtimeId == creds.BeamtimeId { pair.DataSource = creds.DataSource pair.Type = creds.Type - pair.AccessTypes = []string{"read","write"} + pair.AccessTypes = []string{"read", "write"} return pair, true } } return common.BeamtimeMeta{}, false } -func authorizeByHost(host_ip, beamline string) (error) { - filter := strings.Replace(common.Settings.Ldap.FilterTemplate,"__BEAMLINE__",beamline,1) - allowed_ips, err := ldapClient.GetAllowedIpsForBeamline(common.Settings.Ldap.Uri,common.Settings.Ldap.BaseDn, filter) +func authorizeByHost(host_ip, beamline string) error { + filter := strings.Replace(common.Settings.Ldap.FilterTemplate, "__BEAMLINE__", beamline, 1) + allowed_ips, err := ldapClient.GetAllowedIpsForBeamline(common.Settings.Ldap.Uri, common.Settings.Ldap.BaseDn, filter) if err != nil { log.Error("cannot get list of allowed hosts from LDAP: " + err.Error()) return err } - if (!utils.StringInSlice(splitHost(host_ip),allowed_ips)) { - err_string := "beamine " +beamline+" not allowed for host " + host_ip + if !utils.StringInSlice(splitHost(host_ip), allowed_ips) { + err_string := "beamine " + beamline + " not allowed for host " + host_ip log.Error(err_string) return errors.New(err_string) } @@ -199,57 +197,73 @@ func canUseHostAuthorization(creds SourceCredentials) bool { return len(creds.Token) == 0 } +func checkTokenRevoked(tokenId string) (err error) { + revoked, err := store.IsTokenRevoked(tokenId) + if err != nil { + return &common.ServerError{utils.StatusServiceUnavailable, err.Error()} + } + if revoked { + return errors.New("token was revoked") + } + return nil +} + func checkToken(token string, subject_expect string) (accessTypes []string, err error) { var extra_claim structs.AccessTokenExtraClaim - claim,err := Auth.UserAuth().CheckAndGetContent(token,&extra_claim) - if err!=nil { - return nil,err + claim, err := Auth.UserAuth().CheckAndGetContent(token, &extra_claim) + if err != nil { + return nil, err + } + + err = checkTokenRevoked(claim.Id) + if err != nil { + return nil, err } - if extra_claim.AccessTypes==nil || len(extra_claim.AccessTypes)==0 { - return nil,errors.New("missing access types") + if extra_claim.AccessTypes == nil || len(extra_claim.AccessTypes) == 0 { + return nil, errors.New("missing access types") } - if claim.Subject!=subject_expect { - return nil,errors.New("wrong token for "+subject_expect) + if claim.Subject != subject_expect { + return nil, errors.New("wrong token for " + subject_expect) } - return extra_claim.AccessTypes,err + return extra_claim.AccessTypes, err } func authorizeByToken(creds SourceCredentials) (accessTypes []string, err error) { - subject_expect:="" - if (creds.BeamtimeId != "auto") { + subject_expect := "" + if creds.BeamtimeId != "auto" { subject_expect = utils.SubjectFromBeamtime(creds.BeamtimeId) } else { subject_expect = utils.SubjectFromBeamline(creds.Beamline) } - return checkToken(creds.Token,subject_expect) + return checkToken(creds.Token, subject_expect) } func iscommissioning(beamtime string) bool { - return len(beamtime)>0 && beamtime[0]=='c' + return len(beamtime) > 0 && beamtime[0] == 'c' } func findMeta(creds SourceCredentials) (common.BeamtimeMeta, error) { var err error var meta common.BeamtimeMeta - if (creds.BeamtimeId != "auto") { + if creds.BeamtimeId != "auto" { meta, err = findBeamtimeInfoFromId(creds.BeamtimeId) - if (err == nil ) { - meta_onilne, err_online := findBeamtimeMetaFromBeamline(meta.Beamline,iscommissioning(creds.BeamtimeId)) + if err == nil { + meta_onilne, err_online := findBeamtimeMetaFromBeamline(meta.Beamline, iscommissioning(creds.BeamtimeId)) if err_online == nil && meta.BeamtimeId == meta_onilne.BeamtimeId { meta.OnlinePath = meta_onilne.OnlinePath } } } else { - meta, err = findBeamtimeMetaFromBeamline(creds.Beamline,false) + meta, err = findBeamtimeMetaFromBeamline(creds.Beamline, false) } if creds.Type == "processed" { meta.OnlinePath = "" } - if (err != nil) { + if err != nil { log.Error(err.Error()) return common.BeamtimeMeta{}, err } @@ -262,32 +276,32 @@ func findMeta(creds SourceCredentials) (common.BeamtimeMeta, error) { func authorizeMeta(meta common.BeamtimeMeta, request authorizationRequest, creds SourceCredentials) (accessTypes []string, err error) { accessTypes = nil - if creds.Type=="raw" && meta.OnlinePath=="" { - err_string := "beamtime "+meta.BeamtimeId+" is not online" + if creds.Type == "raw" && meta.OnlinePath == "" { + err_string := "beamtime " + meta.BeamtimeId + " is not online" log.Error(err_string) - return nil,errors.New(err_string) + return nil, errors.New(err_string) } if creds.Beamline != "auto" && meta.Beamline != creds.Beamline { err_string := "given beamline (" + creds.Beamline + ") does not match the found one (" + meta.Beamline + ")" log.Debug(err_string) - return nil,errors.New(err_string) + return nil, errors.New(err_string) } if canUseHostAuthorization(creds) { if err := authorizeByHost(request.OriginHost, meta.Beamline); err != nil { - return nil,err + return nil, err } if creds.Type == "raw" { - accessTypes = []string{"read","write","writeraw"} + accessTypes = []string{"read", "write", "writeraw"} } else { - accessTypes = []string{"read","write"} + accessTypes = []string{"read", "write"} } } else { - accessTypes,err = authorizeByToken(creds) + accessTypes, err = authorizeByToken(creds) } - return accessTypes,err + return accessTypes, err } func authorize(request authorizationRequest, creds SourceCredentials) (common.BeamtimeMeta, error) { @@ -306,42 +320,70 @@ func authorize(request authorizationRequest, creds SourceCredentials) (common.Be } meta.AccessTypes = accessTypes - log.Debug("authorized creds bl/bt: ", creds.Beamline+"/"+creds.BeamtimeId+", beamtime " + meta.BeamtimeId + " for " + request.OriginHost + " in " + - meta.Beamline+", type "+meta.Type, "online path "+meta.OnlinePath + ", offline path "+meta.OfflinePath) + log.Debug("authorized creds bl/bt: ", creds.Beamline+"/"+creds.BeamtimeId+", beamtime "+meta.BeamtimeId+" for "+request.OriginHost+" in "+ + meta.Beamline+", type "+meta.Type, "online path "+meta.OnlinePath+", offline path "+meta.OfflinePath) return meta, nil } +func writeServerError(w http.ResponseWriter, err error) { + serr, ok := err.(*common.ServerError) + if ok { + utils.WriteServerError(w, err, serr.Code) + return + } + utils.WriteServerError(w, err, http.StatusUnauthorized) + return +} + func routeAuthorize(w http.ResponseWriter, r *http.Request) { var request authorizationRequest - err := utils.ExtractRequest(r,&request) + err := utils.ExtractRequest(r, &request) if err != nil { - utils.WriteServerError(w,err,http.StatusBadRequest) + utils.WriteServerError(w, err, http.StatusBadRequest) return } creds, err := getSourceCredentials(request) if err != nil { - utils.WriteServerError(w,err,http.StatusBadRequest) + utils.WriteServerError(w, err, http.StatusBadRequest) return } beamtimeInfo, err := authorize(request, creds) - if (err != nil) { - serr,ok:=err.(*common.ServerError) - if ok { - utils.WriteServerError(w,err,serr.Code) - return - } - utils.WriteServerError(w,err,http.StatusUnauthorized) + if err != nil { + writeServerError(w, err) return } res, err := utils.MapToJson(&beamtimeInfo) if err != nil { - utils.WriteServerError(w,err,http.StatusInternalServerError) + utils.WriteServerError(w, err, http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte(res)) } + +func checkRole(w http.ResponseWriter, r *http.Request, role string) error { + var extraClaim structs.AccessTokenExtraClaim + var claims *utils.CustomClaims + if err := utils.JobClaimFromContext(r, &claims, &extraClaim); err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return err + } + + if err := checkTokenRevoked(claims.Id); err != nil { + writeServerError(w, err) + return err + } + + if claims.Subject != "admin" || !utils.StringInSlice(role, extraClaim.AccessTypes) { + err_txt := "wrong token claims" + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte(err_txt)) + return errors.New(err_txt) + } + return nil +} diff --git a/authorizer/src/asapo_authorizer/server/authorize_test.go b/authorizer/src/asapo_authorizer/server/authorize_test.go index 7272ea101c0c1fed437ca450580779fbd016333d..3f7781e04b10f406046d5565d9f4f274b2f3aa0c 100644 --- a/authorizer/src/asapo_authorizer/server/authorize_test.go +++ b/authorizer/src/asapo_authorizer/server/authorize_test.go @@ -4,6 +4,7 @@ import ( "asapo_authorizer/authorization" "asapo_authorizer/common" "asapo_authorizer/ldap_client" + "asapo_authorizer/token_store" "asapo_common/structs" "asapo_common/utils" "github.com/stretchr/testify/assert" @@ -16,20 +17,22 @@ import ( "testing" ) +var expectedTokenId = "123" -func prepareAsapoToken(payload string, accessTypes []string) string{ - auth := authorization.NewAuth(nil,utils.NewJWTAuth("secret_user"),nil) +func prepareAsapoToken(payload string, accessTypes []string) string { + auth := authorization.NewAuth(nil, utils.NewJWTAuth("secret_user"), nil) var claims utils.CustomClaims var extraClaim structs.AccessTokenExtraClaim claims.Subject = payload + claims.Id = expectedTokenId extraClaim.AccessTypes = accessTypes claims.ExtraClaims = &extraClaim token, _ := auth.AdminAuth().GenerateToken(&claims) return token } -func prepareAdminToken(payload string) string{ - auth:= authorization.NewAuth(nil,utils.NewJWTAuth("secret_admin"),nil) +func prepareAdminToken(payload string) string { + auth := authorization.NewAuth(nil, utils.NewJWTAuth("secret_admin"), nil) var claims utils.CustomClaims var extraClaim structs.AccessTokenExtraClaim @@ -42,7 +45,6 @@ func prepareAdminToken(payload string) string{ var mockClient = new(ldap_client.MockedLdapClient) - type request struct { path string cmd string @@ -51,10 +53,9 @@ type request struct { } func allowBeamlines(beamlines []common.BeamtimeMeta) { - common.Settings.AlwaysAllowedBeamtimes=beamlines + common.Settings.AlwaysAllowedBeamtimes = beamlines } - func containsMatcher(substr string) func(str string) bool { return func(str string) bool { return strings.Contains(str, substr) } } @@ -64,56 +65,56 @@ func makeRequest(request interface{}) string { return string(buf) } -func doPostRequest(path string,buf string,authHeader string) *httptest.ResponseRecorder { +func doPostRequest(path string, buf string, authHeader string) *httptest.ResponseRecorder { mux := utils.NewRouter(listRoutes) req, _ := http.NewRequest("POST", path, strings.NewReader(buf)) - if authHeader!="" { - req.Header.Add("Authorization",authHeader) + if authHeader != "" { + req.Header.Add("Authorization", authHeader) } w := httptest.NewRecorder() mux.ServeHTTP(w, req) return w } -var credTests = [] struct { +var credTests = []struct { request string - cred SourceCredentials - ok bool + cred SourceCredentials + ok bool message string -} { - {"processed%asapo_test%auto%%", SourceCredentials{"asapo_test","auto","detector","","processed"},true,"auto beamline, source and no token"}, - {"processed%asapo_test%auto%%token", SourceCredentials{"asapo_test","auto","detector","token","processed"},true,"auto beamline, source"}, - {"processed%asapo_test%auto%source%", SourceCredentials{"asapo_test","auto","source","","processed"},true,"auto beamline, no token"}, - {"processed%asapo_test%auto%source%token", SourceCredentials{"asapo_test","auto","source","token","processed"},true,"auto beamline,source, token"}, - {"processed%asapo_test%beamline%source%token", SourceCredentials{"asapo_test","beamline","source","token","processed"},true,"all set"}, - {"processed%auto%beamline%source%token", SourceCredentials{"auto","beamline","source","token","processed"},true,"auto beamtime"}, - {"raw%auto%auto%source%token", SourceCredentials{},false,"auto beamtime and beamline"}, - {"raw%%beamline%source%token", SourceCredentials{"auto","beamline","source","token","raw"},true,"empty beamtime"}, - {"raw%asapo_test%%source%token", SourceCredentials{"asapo_test","auto","source","token","raw"},true,"empty bealine"}, - {"raw%%%source%token", SourceCredentials{},false,"both empty"}, - {"processed%asapo_test%beamline%source%blabla%token", SourceCredentials{"asapo_test","beamline","source%blabla","token","processed"},true,"% in source"}, - {"processed%asapo_test%beamline%source%blabla%", SourceCredentials{"asapo_test","beamline","source%blabla","","processed"},true,"% in source, no token"}, +}{ + {"processed%asapo_test%auto%%", SourceCredentials{"asapo_test", "auto", "detector", "", "processed"}, true, "auto beamline, source and no token"}, + {"processed%asapo_test%auto%%token", SourceCredentials{"asapo_test", "auto", "detector", "token", "processed"}, true, "auto beamline, source"}, + {"processed%asapo_test%auto%source%", SourceCredentials{"asapo_test", "auto", "source", "", "processed"}, true, "auto beamline, no token"}, + {"processed%asapo_test%auto%source%token", SourceCredentials{"asapo_test", "auto", "source", "token", "processed"}, true, "auto beamline,source, token"}, + {"processed%asapo_test%beamline%source%token", SourceCredentials{"asapo_test", "beamline", "source", "token", "processed"}, true, "all set"}, + {"processed%auto%beamline%source%token", SourceCredentials{"auto", "beamline", "source", "token", "processed"}, true, "auto beamtime"}, + {"raw%auto%auto%source%token", SourceCredentials{}, false, "auto beamtime and beamline"}, + {"raw%%beamline%source%token", SourceCredentials{"auto", "beamline", "source", "token", "raw"}, true, "empty beamtime"}, + {"raw%asapo_test%%source%token", SourceCredentials{"asapo_test", "auto", "source", "token", "raw"}, true, "empty bealine"}, + {"raw%%%source%token", SourceCredentials{}, false, "both empty"}, + {"processed%asapo_test%beamline%source%blabla%token", SourceCredentials{"asapo_test", "beamline", "source%blabla", "token", "processed"}, true, "% in source"}, + {"processed%asapo_test%beamline%source%blabla%", SourceCredentials{"asapo_test", "beamline", "source%blabla", "", "processed"}, true, "% in source, no token"}, } func TestSplitCreds(t *testing.T) { for _, test := range credTests { - request := authorizationRequest{test.request,"host"} - creds,err := getSourceCredentials(request) + request := authorizationRequest{test.request, "host"} + creds, err := getSourceCredentials(request) if test.ok { - assert.Nil(t,err) - assert.Equal(t,test.cred,creds,test.message) + assert.Nil(t, err) + assert.Equal(t, test.cred, creds, test.message) } else { - assert.NotNil(t,err,test.message) + assert.NotNil(t, err, test.message) } } } func TestAuthorizeDefaultOK(t *testing.T) { - allowBeamlines([]common.BeamtimeMeta{{"asapo_test","beamline","","2019","tf","",nil}}) - request := makeRequest(authorizationRequest{"processed%asapo_test%%%","host"}) - w := doPostRequest("/authorize",request,"") + allowBeamlines([]common.BeamtimeMeta{{"asapo_test", "beamline", "", "2019", "tf", "", nil}}) + request := makeRequest(authorizationRequest{"processed%asapo_test%%%", "host"}) + w := doPostRequest("/authorize", request, "") body, _ := ioutil.ReadAll(w.Body) @@ -125,7 +126,7 @@ func TestAuthorizeDefaultOK(t *testing.T) { assert.Equal(t, http.StatusOK, w.Code, "") } -var beamtime_meta_online =` +var beamtime_meta_online = ` { "beamline": "bl1", "beamtimeId": "test_online", @@ -133,7 +134,7 @@ var beamtime_meta_online =` } ` -var beamtime_meta =` +var beamtime_meta = ` { "applicant": { "email": "test", @@ -172,7 +173,7 @@ var beamtime_meta =` } ` -var commissioning_meta =` +var commissioning_meta = ` { "beamline": "P04", "corePath": "/asap3/petra3/gpfs/p04/2021/commissioning/c20210823_000_MAA", @@ -182,76 +183,80 @@ var commissioning_meta =` } ` - -var authTests = [] struct { +var authTests = []struct { source_type string beamtime_id string - beamline string - dataSource string - token string - originHost string - status int - message string - answer string + beamline string + dataSource string + token string + originHost string + status int + message string + answer string + mode int }{ - {"processed","test","auto","dataSource", prepareAsapoToken("bt_test",nil),"127.0.0.2",http.StatusUnauthorized,"missing access types", - ""}, - {"processed","test","auto","dataSource", prepareAsapoToken("bt_test",[]string{}),"127.0.0.2",http.StatusUnauthorized,"empty access types", - ""}, - {"processed","test","auto","dataSource", prepareAsapoToken("bt_test",[]string{"write"}),"127.0.0.2",http.StatusOK,"user source with correct token", - `{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["write"]}`}, - {"processed","test_online","auto","dataSource", prepareAsapoToken("bt_test_online",[]string{"read"}),"127.0.0.1",http.StatusOK,"with online path, processed type", - `{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"","source-type":"processed","access-types":["read"]}`}, - {"processed","test1","auto","dataSource", prepareAsapoToken("bt_test1",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"correct token, beamtime not found", - ""}, - {"processed","test","auto","dataSource", prepareAsapoToken("wrong",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"user source with wrong token", - ""}, - {"processed","test","bl1","dataSource", prepareAsapoToken("bt_test",[]string{"read"}),"127.0.0.1",http.StatusOK,"correct beamline given", - `{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["read"]}`}, - {"processed","test","bl2","dataSource", prepareAsapoToken("bt_test",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"incorrect beamline given", - ""}, - {"processed","auto","p07", "dataSource", prepareAsapoToken("bl_p07",[]string{"read"}),"127.0.0.1",http.StatusOK,"beamtime found", - `{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"","source-type":"processed","access-types":["read"]}`}, - {"processed","auto","p07", "dataSource", prepareAsapoToken("bl_p06",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"wrong token", - ""}, - {"processed","auto","p08", "dataSource", prepareAsapoToken("bl_p08",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"beamtime not found", - ""}, - {"raw","test_online","auto","dataSource", "","127.0.0.1",http.StatusOK,"raw type", - `{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"./bl1/current","source-type":"raw","access-types":["read","write","writeraw"]}`}, - {"raw","test_online","auto","dataSource", "","127.0.0.1",http.StatusOK,"raw type", - `{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"./bl1/current","source-type":"raw","access-types":["read","write","writeraw"]}`}, - {"raw","auto","p07","dataSource", "","127.0.0.1",http.StatusOK,"raw type, auto beamtime", - `{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"./p07/current","source-type":"raw","access-types":["read","write","writeraw"]}`}, - {"raw","auto","p07","noldap", "","127.0.0.1",http.StatusNotFound,"no conection to ldap", - ""}, - - {"raw","auto","p07","dataSource", prepareAsapoToken("bl_p07",[]string{"read","writeraw"}),"127.0.0.2",http.StatusOK,"raw type with token", - `{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"./p07/current","source-type":"raw","access-types":["read","writeraw"]}`}, - - {"raw","test_online","auto","dataSource", "","127.0.0.2",http.StatusUnauthorized,"raw type, wrong origin host", - ""}, - {"raw","test","auto","dataSource", prepareAsapoToken("bt_test",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"raw when not online", - ""}, - {"processed","test","auto","dataSource", "","127.0.0.1:1001",http.StatusOK,"processed without token", - `{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["read","write"]}`}, - {"processed","test","auto","dataSource", "","127.0.0.2",http.StatusUnauthorized,"processed without token, wrong host", - ""}, - {"raw","c20210823_000_MAA","auto","dataSource", "","127.0.0.1",http.StatusOK,"raw type commissioning", - `{"beamtimeId":"c20210823_000_MAA","beamline":"p04","dataSource":"dataSource","corePath":"./tf/gpfs/p04/2019/commissioning/c20210823_000_MAA","beamline-path":"./p04/commissioning","source-type":"raw","access-types":["read","write","writeraw"]}`}, - {"processed","c20210823_000_MAA","auto","dataSource", "","127.0.0.1",http.StatusOK,"processed type commissioning", - `{"beamtimeId":"c20210823_000_MAA","beamline":"p04","dataSource":"dataSource","corePath":"./tf/gpfs/p04/2019/commissioning/c20210823_000_MAA","beamline-path":"","source-type":"processed","access-types":["read","write"]}`}, + {"processed", "test", "auto", "dataSource", prepareAsapoToken("bt_test", nil), "127.0.0.2", http.StatusUnauthorized, "missing access types", + "", 0}, + {"processed", "test", "auto", "dataSource", prepareAsapoToken("bt_test", []string{}), "127.0.0.2", http.StatusUnauthorized, "empty access types", + "", 0}, + {"processed", "test", "auto", "dataSource", prepareAsapoToken("bt_test", []string{"write"}), "127.0.0.2", http.StatusOK, "user source with correct token", + `{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["write"]}`, 0}, + {"processed", "test", "auto", "dataSource", prepareAsapoToken("bt_test", []string{"write"}), "127.0.0.2", http.StatusUnauthorized, "token was revoked", + "", 2}, + {"processed", "test_online", "auto", "dataSource", prepareAsapoToken("bt_test_online", []string{"read"}), "127.0.0.1", http.StatusOK, "with online path, processed type", + `{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"","source-type":"processed","access-types":["read"]}`, 0}, + {"processed", "test1", "auto", "dataSource", prepareAsapoToken("bt_test1", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "correct token, beamtime not found", + "", 1}, + {"processed", "test", "auto", "dataSource", prepareAsapoToken("wrong", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "user source with wrong token", + "", 0}, + {"processed", "test", "bl1", "dataSource", prepareAsapoToken("bt_test", []string{"read"}), "127.0.0.1", http.StatusOK, "correct beamline given", + `{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["read"]}`, 0}, + {"processed", "test", "bl2", "dataSource", prepareAsapoToken("bt_test", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "incorrect beamline given", + "", 1}, + {"processed", "auto", "p07", "dataSource", prepareAsapoToken("bl_p07", []string{"read"}), "127.0.0.1", http.StatusOK, "beamtime found", + `{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"","source-type":"processed","access-types":["read"]}`, 0}, + {"processed", "auto", "p07", "dataSource", prepareAsapoToken("bl_p06", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "wrong token", + "", 0}, + {"processed", "auto", "p08", "dataSource", prepareAsapoToken("bl_p08", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "beamtime not found", + "", 1}, + {"raw", "test_online", "auto", "dataSource", "", "127.0.0.1", http.StatusOK, "raw type", + `{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"./bl1/current","source-type":"raw","access-types":["read","write","writeraw"]}`, 0}, + {"raw", "test_online", "auto", "dataSource", "", "127.0.0.1", http.StatusOK, "raw type", + `{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"./bl1/current","source-type":"raw","access-types":["read","write","writeraw"]}`, 0}, + {"raw", "auto", "p07", "dataSource", "", "127.0.0.1", http.StatusOK, "raw type, auto beamtime", + `{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"./p07/current","source-type":"raw","access-types":["read","write","writeraw"]}`, 0}, + {"raw", "auto", "p07", "noldap", "", "127.0.0.1", http.StatusNotFound, "no conection to ldap", + "", 0}, + + {"raw", "auto", "p07", "dataSource", prepareAsapoToken("bl_p07", []string{"read", "writeraw"}), "127.0.0.2", http.StatusOK, "raw type with token", + `{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"./p07/current","source-type":"raw","access-types":["read","writeraw"]}`, 0}, + + {"raw", "test_online", "auto", "dataSource", "", "127.0.0.2", http.StatusUnauthorized, "raw type, wrong origin host", + "", 0}, + {"raw", "test", "auto", "dataSource", prepareAsapoToken("bt_test", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "raw when not online", + "", 1}, + {"processed", "test", "auto", "dataSource", "", "127.0.0.1:1001", http.StatusOK, "processed without token", + `{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["read","write"]}`, 0}, + {"processed", "test", "auto", "dataSource", "", "127.0.0.2", http.StatusUnauthorized, "processed without token, wrong host", + "", 0}, + {"raw", "c20210823_000_MAA", "auto", "dataSource", "", "127.0.0.1", http.StatusOK, "raw type commissioning", + `{"beamtimeId":"c20210823_000_MAA","beamline":"p04","dataSource":"dataSource","corePath":"./tf/gpfs/p04/2019/commissioning/c20210823_000_MAA","beamline-path":"./p04/commissioning","source-type":"raw","access-types":["read","write","writeraw"]}`, 0}, + {"processed", "c20210823_000_MAA", "auto", "dataSource", "", "127.0.0.1", http.StatusOK, "processed type commissioning", + `{"beamtimeId":"c20210823_000_MAA","beamline":"p04","dataSource":"dataSource","corePath":"./tf/gpfs/p04/2019/commissioning/c20210823_000_MAA","beamline-path":"","source-type":"processed","access-types":["read","write"]}`, 0}, } func TestAuthorize(t *testing.T) { ldapClient = mockClient allowBeamlines([]common.BeamtimeMeta{}) - Auth = authorization.NewAuth(utils.NewJWTAuth("secret_user"),utils.NewJWTAuth("secret_admin"),utils.NewJWTAuth("secret")) + Auth = authorization.NewAuth(utils.NewJWTAuth("secret_user"), utils.NewJWTAuth("secret_admin"), utils.NewJWTAuth("secret")) + mock_store := new(token_store.MockedStore) + store = mock_store expected_uri := "expected_uri" expected_base := "expected_base" allowed_ips := []string{"127.0.0.1"} - common.Settings.RootBeamtimesFolder ="." - common.Settings.CurrentBeamlinesFolder="." - common.Settings.Ldap.FilterTemplate="a3__BEAMLINE__-hosts" + common.Settings.RootBeamtimesFolder = "." + common.Settings.CurrentBeamlinesFolder = "." + common.Settings.Ldap.FilterTemplate = "a3__BEAMLINE__-hosts" common.Settings.Ldap.Uri = expected_uri common.Settings.Ldap.BaseDn = expected_base @@ -266,109 +271,118 @@ func TestAuthorize(t *testing.T) { ioutil.WriteFile(filepath.Clean("bl1/current/beamtime-metadata-test_online.json"), []byte(beamtime_meta_online), 0644) ioutil.WriteFile(filepath.Clean("p04/commissioning/commissioning-metadata-c20210823_000_MAA.json"), []byte(commissioning_meta), 0644) - defer os.RemoveAll("p07") - defer os.RemoveAll("p04") - defer os.RemoveAll("tf") - defer os.RemoveAll("bl1") + defer os.RemoveAll("p07") + defer os.RemoveAll("p04") + defer os.RemoveAll("tf") + defer os.RemoveAll("bl1") for _, test := range authTests { - if test.source_type == "raw" || test.token == "" {bl := test.beamline + if test.token != "" && test.mode != 1 { + if test.mode == 2 { + mock_store.On("IsTokenRevoked", expectedTokenId).Return(true, nil) + } else { + mock_store.On("IsTokenRevoked", expectedTokenId).Return(false, nil) + } + } + + if test.source_type == "raw" || test.token == "" { + bl := test.beamline if test.beamline == "auto" { bl = "bl1" } - if iscommissioning(test.beamtime_id) && test.beamline == "auto" { + if iscommissioning(test.beamtime_id) && test.beamline == "auto" { bl = "p04" } - expected_filter:="a3"+bl+"-hosts" + expected_filter := "a3" + bl + "-hosts" if test.dataSource == "noldap" { - err := &common.ServerError{utils.StatusServiceUnavailable,""} - mockClient.On("GetAllowedIpsForBeamline", expected_uri, expected_base,expected_filter).Return([]string{}, err) + err := &common.ServerError{utils.StatusServiceUnavailable, ""} + mockClient.On("GetAllowedIpsForBeamline", expected_uri, expected_base, expected_filter).Return([]string{}, err) } else { - mockClient.On("GetAllowedIpsForBeamline", expected_uri, expected_base,expected_filter).Return(allowed_ips, nil) + mockClient.On("GetAllowedIpsForBeamline", expected_uri, expected_base, expected_filter).Return(allowed_ips, nil) } } - request := makeRequest(authorizationRequest{test.source_type+"%"+test.beamtime_id+"%"+test.beamline+"%"+test.dataSource+"%"+test.token,test.originHost}) - w := doPostRequest("/authorize",request,"") + request := makeRequest(authorizationRequest{test.source_type + "%" + test.beamtime_id + "%" + test.beamline + "%" + test.dataSource + "%" + test.token, test.originHost}) + w := doPostRequest("/authorize", request, "") body, _ := ioutil.ReadAll(w.Body) - if test.status==http.StatusOK { - body_str:=string(body) - body_str = strings.Replace(body_str,string(os.PathSeparator),"/",-1) - body_str = strings.Replace(body_str,"//","/",-1) - assert.Equal(t, test.answer,body_str,test.message) + if test.status == http.StatusOK { + body_str := string(body) + body_str = strings.Replace(body_str, string(os.PathSeparator), "/", -1) + body_str = strings.Replace(body_str, "//", "/", -1) + assert.Equal(t, test.answer, body_str, test.message) } - assert.Equal(t, test.status,w.Code, test.message) + assert.Equal(t, test.status, w.Code, test.message) mockClient.AssertExpectations(t) - mockClient.ExpectedCalls=nil + mockClient.ExpectedCalls = nil + mock_store.AssertExpectations(t) + mock_store.ExpectedCalls = nil + mock_store.Calls = nil } } func TestNotAuthorized(t *testing.T) { - request := makeRequest(authorizationRequest{"raw%any_id%%%","host"}) - w := doPostRequest("/authorize",request,"") + request := makeRequest(authorizationRequest{"raw%any_id%%%", "host"}) + w := doPostRequest("/authorize", request, "") assert.Equal(t, http.StatusUnauthorized, w.Code, "") } - func TestAuthorizeWrongRequest(t *testing.T) { - w := doPostRequest("/authorize","babla","") + w := doPostRequest("/authorize", "babla", "") assert.Equal(t, http.StatusBadRequest, w.Code, "") } - func TestAuthorizeWrongPath(t *testing.T) { - w := doPostRequest("/authorized","","") + w := doPostRequest("/authorized", "", "") assert.Equal(t, http.StatusNotFound, w.Code, "") } func TestDoNotAuthorizeIfNotInAllowed(t *testing.T) { - allowBeamlines([]common.BeamtimeMeta{{"test","beamline","","2019","tf","",nil}}) + allowBeamlines([]common.BeamtimeMeta{{"test", "beamline", "", "2019", "tf", "", nil}}) - request := authorizationRequest{"asapo_test%%","host"} - creds,_ := getSourceCredentials(request) - _,err := authorize(request,creds) - assert.Error(t,err, "") + request := authorizationRequest{"asapo_test%%", "host"} + creds, _ := getSourceCredentials(request) + _, err := authorize(request, creds) + assert.Error(t, err, "") } func TestSplitHost(t *testing.T) { host := splitHost("127.0.0.1:112") - assert.Equal(t,"127.0.0.1", host, "") + assert.Equal(t, "127.0.0.1", host, "") } - func TestSplitHostNoPort(t *testing.T) { host := splitHost("127.0.0.1") - assert.Equal(t,"127.0.0.1", host, "") + assert.Equal(t, "127.0.0.1", host, "") } -var extractBtinfoTests = [] struct { - root string - fname string +var extractBtinfoTests = []struct { + root string + fname string beamline string - id string - ok bool + id string + ok bool }{ - {".",filepath.Clean("tf/gpfs/bl1.01/2019/data/123"),"bl1.01","123",true}, - {filepath.Clean("/blabla/tratartra"),filepath.Clean("tf/gpfs/bl1.01/2019/data/123"), "bl1.01","123",true}, - {".",filepath.Clean("tf/gpfs/common/2019/data/123"), "bl1.01","123",false}, - {".",filepath.Clean("tf/gpfs/BeamtimeUsers/2019/data/123"), "bl1.01","123",false}, - {".",filepath.Clean("tf/gpfs/state/2019/data/123"), "bl1.01","123",false}, - {".",filepath.Clean("tf/gpfs/support/2019/data/123"), "bl1.01","123",false}, - {".",filepath.Clean("petra3/gpfs/p01/2019/commissioning/c20180508-000-COM20181"), "p01","c20180508-000-COM20181",true}, - + {".", filepath.Clean("tf/gpfs/bl1.01/2019/data/123"), "bl1.01", "123", true}, + {filepath.Clean("/blabla/tratartra"), filepath.Clean("tf/gpfs/bl1.01/2019/data/123"), "bl1.01", "123", true}, + {".", filepath.Clean("tf/gpfs/common/2019/data/123"), "bl1.01", "123", false}, + {".", filepath.Clean("tf/gpfs/BeamtimeUsers/2019/data/123"), "bl1.01", "123", false}, + {".", filepath.Clean("tf/gpfs/state/2019/data/123"), "bl1.01", "123", false}, + {".", filepath.Clean("tf/gpfs/support/2019/data/123"), "bl1.01", "123", false}, + {".", filepath.Clean("petra3/gpfs/p01/2019/commissioning/c20180508-000-COM20181"), "p01", "c20180508-000-COM20181", true}, } + func TestGetBeamtimeInfo(t *testing.T) { for _, test := range extractBtinfoTests { - common.Settings.RootBeamtimesFolder=test.root - bt,err:= beamtimeMetaFromMatch(test.root+string(filepath.Separator)+test.fname) + common.Settings.RootBeamtimesFolder = test.root + bt, err := beamtimeMetaFromMatch(test.root + string(filepath.Separator) + test.fname) if test.ok { - assert.Equal(t,bt.OfflinePath,test.root+string(filepath.Separator)+test.fname) - assert.Equal(t,bt.Beamline,test.beamline) - assert.Equal(t,bt.BeamtimeId,test.id) - assert.Nil(t,err,"should not be error") + assert.Equal(t, bt.OfflinePath, test.root+string(filepath.Separator)+test.fname) + assert.Equal(t, bt.Beamline, test.beamline) + assert.Equal(t, bt.BeamtimeId, test.id) + assert.Nil(t, err, "should not be error") } else { - assert.NotNil(t,err,"should be error") + assert.NotNil(t, err, "should be error") } } diff --git a/authorizer/src/asapo_authorizer/server/folder_token_test.go b/authorizer/src/asapo_authorizer/server/folder_token_test.go index 88c02cba5f960ab017e1508157ff30ce3d6b355f..ae39cee67b223f2d4d04da60cd749d240af877f7 100644 --- a/authorizer/src/asapo_authorizer/server/folder_token_test.go +++ b/authorizer/src/asapo_authorizer/server/folder_token_test.go @@ -3,11 +3,13 @@ package server import ( "asapo_authorizer/authorization" "asapo_authorizer/common" + "asapo_authorizer/token_store" "asapo_common/structs" "asapo_common/utils" "asapo_common/version" "fmt" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "io/ioutil" "net/http" "os" @@ -39,6 +41,9 @@ var fodlerTokenTests = [] struct { func TestFolderToken(t *testing.T) { allowBeamlines([]common.BeamtimeMeta{}) + mock_store := new(token_store.MockedStore) + store = mock_store + common.Settings.RootBeamtimesFolder ="." common.Settings.CurrentBeamlinesFolder="." Auth = authorization.NewAuth(utils.NewJWTAuth("secret_user"),utils.NewJWTAuth("secret_admin"),utils.NewJWTAuth("secret_folder")) @@ -65,6 +70,8 @@ func TestFolderToken(t *testing.T) { request := makeRequest(folderTokenRequest{path_in_token,test.beamtime_id,test.token}) if test.status == http.StatusBadRequest { request =makeRequest(authorizationRequest{}) + } else { + mock_store.On("IsTokenRevoked", mock.Anything).Return(false, nil) } w := doPostRequest("/"+version.GetAuthorizerApiVersion()+"/folder",request,"") if w.Code == http.StatusOK { @@ -78,7 +85,9 @@ func TestFolderToken(t *testing.T) { body, _ := ioutil.ReadAll(w.Body) fmt.Println(string(body)) } - + mock_store.AssertExpectations(t) + mock_store.ExpectedCalls = nil + mock_store.Calls = nil assert.Equal(t, test.status, w.Code, test.message) } } diff --git a/authorizer/src/asapo_authorizer/server/introspect.go b/authorizer/src/asapo_authorizer/server/introspect.go index b846e395dcb677175d0e02aaaacb85395280a287..5dd591a2c37f274e552cba63aab7e60829b5431b 100644 --- a/authorizer/src/asapo_authorizer/server/introspect.go +++ b/authorizer/src/asapo_authorizer/server/introspect.go @@ -23,6 +23,11 @@ func verifyUserToken(token string) (response structs.IntrospectTokenResponse, er if err!=nil { return } + err = checkTokenRevoked(claim.Id) + if err != nil { + return + } + response.Sub = claim.Subject response.AccessTypes = extra_claim.AccessTypes return diff --git a/authorizer/src/asapo_authorizer/server/introspect_test.go b/authorizer/src/asapo_authorizer/server/introspect_test.go index 9a42bc066a09c5bf2db9da2f65563aff5a0004b2..40a99b52fe531d720cb902feccc577202b472709 100644 --- a/authorizer/src/asapo_authorizer/server/introspect_test.go +++ b/authorizer/src/asapo_authorizer/server/introspect_test.go @@ -2,6 +2,7 @@ package server import ( "asapo_authorizer/authorization" + "asapo_authorizer/token_store" "asapo_common/structs" "asapo_common/utils" "encoding/json" @@ -27,11 +28,16 @@ func TestIntrospect(t *testing.T) { authJWT := utils.NewJWTAuth("secret") authAdmin := utils.NewJWTAuth("secret_admin") authUser := utils.NewJWTAuth("secret_user") + mock_store := new(token_store.MockedStore) + store = mock_store + Auth = authorization.NewAuth(authUser,authAdmin,authJWT) for _, test := range IntrospectTests { token := prepareAsapoToken(test.tokenSubject,test.roles) if test.status==http.StatusUnauthorized { token = "blabla" + } else { + mock_store.On("IsTokenRevoked", expectedTokenId).Return(false, nil) } request := makeRequest(structs.IntrospectTokenRequest{token}) w := doPostRequest("/introspect",request,"") @@ -46,6 +52,9 @@ func TestIntrospect(t *testing.T) { body, _ := ioutil.ReadAll(w.Body) fmt.Println(string(body)) } + mock_store.AssertExpectations(t) + mock_store.ExpectedCalls = nil + mock_store.Calls = nil } } diff --git a/authorizer/src/asapo_authorizer/server/issue_token.go b/authorizer/src/asapo_authorizer/server/issue_token.go index 8b9bae25305b864af8d00f434ea3e5d47c40fb25..bf2524abf9317081b2dd22f063cc1cf05d1774a9 100644 --- a/authorizer/src/asapo_authorizer/server/issue_token.go +++ b/authorizer/src/asapo_authorizer/server/issue_token.go @@ -2,11 +2,13 @@ package server import ( "asapo_authorizer/authorization" + "asapo_authorizer/token_store" log "asapo_common/logger" "asapo_common/structs" "asapo_common/utils" "errors" "net/http" + "time" ) func extractUserTokenrequest(r *http.Request) (request structs.IssueTokenRequest, err error) { @@ -37,19 +39,12 @@ func extractUserTokenrequest(r *http.Request) (request structs.IssueTokenRequest } func routeAuthorisedTokenIssue(w http.ResponseWriter, r *http.Request) { - Auth.AdminAuth().ProcessAuth(checkAccessToken, "admin")(w, r) + Auth.AdminAuth().ProcessAuth(checkAccessToken, "")(w, r) } + func checkAccessToken(w http.ResponseWriter, r *http.Request) { - var extraClaim structs.AccessTokenExtraClaim - var claims *utils.CustomClaims - if err := utils.JobClaimFromContext(r, &claims, &extraClaim); err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - } - if claims.Subject != "admin" || !utils.StringInSlice("create",extraClaim.AccessTypes) { - err_txt := "wrong token claims" - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(err_txt)) + if checkRole(w, r, "create") != nil { + return } issueUserToken(w, r) @@ -62,12 +57,22 @@ func issueUserToken(w http.ResponseWriter, r *http.Request) { return } - token, _, err := Auth.PrepareAccessToken(request, true) + token, claims, err := Auth.PrepareAccessToken(request, true) if err != nil { utils.WriteServerError(w, err, http.StatusInternalServerError) return } + claims.StandardClaims.Issuer = "asapo-auth" + claims.StandardClaims.IssuedAt = time.Now().Unix() + record := token_store.TokenRecord{claims.Id, claims, token, false} + err = store.AddToken(record) + if err != nil { + utils.WriteServerError(w, err, http.StatusInternalServerError) + return + } + + log.Debug("generated user token ") answer := authorization.UserTokenResponce(request, token) diff --git a/authorizer/src/asapo_authorizer/server/issue_token_test.go b/authorizer/src/asapo_authorizer/server/issue_token_test.go index ee267ca90e6b0e349a7eb033bbfa096bcdf7f2bf..a36552063691c66572e98f276d1fd6550e2d7e00 100644 --- a/authorizer/src/asapo_authorizer/server/issue_token_test.go +++ b/authorizer/src/asapo_authorizer/server/issue_token_test.go @@ -2,11 +2,13 @@ package server import ( "asapo_authorizer/authorization" + "asapo_authorizer/token_store" "asapo_common/structs" "asapo_common/utils" "encoding/json" "fmt" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "io/ioutil" "net/http" "testing" @@ -40,8 +42,15 @@ func TestIssueToken(t *testing.T) { authAdmin := utils.NewJWTAuth("secret_admin") authUser := utils.NewJWTAuth("secret_user") Auth = authorization.NewAuth(authUser,authAdmin,authJWT) + mock_store := new(token_store.MockedStore) + store = mock_store + for _, test := range IssueTokenTests { request := makeRequest(structs.IssueTokenRequest{test.requestSubject,test.validDays,test.roles}) + mock_store.On("IsTokenRevoked", mock.Anything).Return(false,nil) + if test.status == http.StatusOK { + mock_store.On("AddToken", mock.Anything).Return(nil) + } w := doPostRequest("/admin/issue",request,authAdmin.Name()+" "+test.adminToken) if w.Code == http.StatusOK { body, _ := ioutil.ReadAll(w.Body) @@ -60,6 +69,9 @@ func TestIssueToken(t *testing.T) { body, _ := ioutil.ReadAll(w.Body) fmt.Println(string(body)) } + mock_store.AssertExpectations(t) + mock_store.ExpectedCalls = nil + mock_store.Calls = nil assert.Equal(t, test.status, w.Code, test.message) } diff --git a/authorizer/src/asapo_authorizer/server/listroutes.go b/authorizer/src/asapo_authorizer/server/listroutes.go index 09b695091ffc4ecf686715a5b7a4f10434280cbb..d1f027f0aa048b2a0a3e41c9340694e99a11effb 100644 --- a/authorizer/src/asapo_authorizer/server/listroutes.go +++ b/authorizer/src/asapo_authorizer/server/listroutes.go @@ -17,6 +17,12 @@ var listRoutes = utils.Routes{ "/introspect", routeIntrospect, }, + utils.Route{ + "Authorize", + "POST", + "/admin/revoke", + routeRevoke, + }, utils.Route{ "HealthCheck", "Get", diff --git a/authorizer/src/asapo_authorizer/server/revoke_token.go b/authorizer/src/asapo_authorizer/server/revoke_token.go new file mode 100644 index 0000000000000000000000000000000000000000..c1c98c6f2adf4c3a3fe05fd94b5773352861b75c --- /dev/null +++ b/authorizer/src/asapo_authorizer/server/revoke_token.go @@ -0,0 +1,39 @@ +package server + +import ( + log "asapo_common/logger" + "asapo_common/utils" + "encoding/json" + "net/http" +) + +func routeRevoke(w http.ResponseWriter, r *http.Request) { + Auth.AdminAuth().ProcessAuth(processRevoke, "")(w, r) +} + +func processRevoke(w http.ResponseWriter, r *http.Request) { + if checkRole(w, r, "revoke") != nil { + return + } + revokeToken(w, r) +} + +func revokeToken(w http.ResponseWriter, r *http.Request) { + token, err := extractToken(r) + if err != nil { + utils.WriteServerError(w, err, http.StatusBadRequest) + return + } + + rec, err := store.RevokeToken(token, "") + if err != nil { + log.Error("could not revoke token "+ token+": "+ err.Error()) + utils.WriteServerError(w, err, http.StatusBadRequest) + return + } + + log.Debug("revoked token " + rec.Token) + answer, _ := json.Marshal(&rec) + w.WriteHeader(http.StatusOK) + w.Write(answer) +} diff --git a/authorizer/src/asapo_authorizer/token_store/mock_store.go b/authorizer/src/asapo_authorizer/token_store/mock_store.go index b2db86bcbbf8bf0363310e0c75487631a2ff76d4..7d0a35923d098701ad1fb329de1c2d85db944b70 100644 --- a/authorizer/src/asapo_authorizer/token_store/mock_store.go +++ b/authorizer/src/asapo_authorizer/token_store/mock_store.go @@ -30,9 +30,9 @@ func (store *MockedStore) GetTokenList() ([]TokenRecord,error) { return args.Get(0).([]TokenRecord), args.Error(1) } -func (store *MockedStore) GetRevokedTokenIds() ([]string,error) { - args := store.Called() - return args.Get(0).([]string), args.Error(1) +func (store *MockedStore) IsTokenRevoked(tokenId string) (bool, error) { + args := store.Called(tokenId) + return args.Get(0).(bool), args.Error(1) } func (store *MockedStore) Close() { diff --git a/authorizer/src/asapo_authorizer/token_store/token_store.go b/authorizer/src/asapo_authorizer/token_store/token_store.go index 0de0d1c45c61a240079f478673ff3b829ce25774..756d7cc2d970292697c0b2fd4ecd0fcebcd909a9 100644 --- a/authorizer/src/asapo_authorizer/token_store/token_store.go +++ b/authorizer/src/asapo_authorizer/token_store/token_store.go @@ -16,7 +16,7 @@ type Store interface { AddToken(token TokenRecord) error RevokeToken(token string, id string) (TokenRecord, error) GetTokenList() ([]TokenRecord, error) - GetRevokedTokenIds() ([]string, error) + IsTokenRevoked(tokenId string) (bool, error) Close() } @@ -133,16 +133,6 @@ func (store *TokenStore) updateTokenStatus(token *TokenRecord) error { Collection: KTokens, Op: "update_record", }, token.Id, map[string]interface{}{"revoked": true}, false, token) - if err != nil { - return err - } - - idRec := IdRecord{token.Id} - _, err = store.db.ProcessRequest(Request{ - DbName: KAdminDb, - Collection: KRevokedTokens, - Op: "create_record", - }, &idRec) return err } @@ -172,6 +162,9 @@ func (store *TokenStore) RevokeToken(token string, id string) (TokenRecord, erro return TokenRecord{}, store.processError(err) } + store.revokedTokenList.lock.Lock() + defer store.revokedTokenList.lock.Unlock() + store.revokedTokenList.tokens = append(store.revokedTokenList.tokens, tokenRecord.Id) return tokenRecord, nil } @@ -197,6 +190,7 @@ func (store *TokenStore) loopGetRevokedTokens() { if err != nil { store.revokedTokenList.lock.Lock() store.revokedTokenList.lastError = err + store.revokedTokenList.tokens = nil store.processError(err) store.revokedTokenList.lock.Unlock() next_update = 1 @@ -209,6 +203,7 @@ func (store *TokenStore) loopGetRevokedTokens() { tokens[i] = token.Id } store.revokedTokenList.lock.Lock() + store.revokedTokenList.lastError = nil store.revokedTokenList.tokens = tokens store.revokedTokenList.lock.Unlock() } @@ -221,15 +216,23 @@ func (store *TokenStore) loopGetRevokedTokens() { } } -func (store *TokenStore) GetRevokedTokenIds() ([]string, error) { +func (store *TokenStore) IsTokenRevoked(tokenId string) (bool, error) { + tokens, err := store.getRevokedTokenIds() + if err != nil { + return true, err + } + return utils.StringInSlice(tokenId, tokens), nil +} + +func (store *TokenStore) getRevokedTokenIds() ([]string, error) { store.revokedTokenList.lock.RLock() defer store.revokedTokenList.lock.RUnlock() if store.revokedTokenList.lastError != nil { return []string{}, store.revokedTokenList.lastError } -// res := make([]string, len(store.revokedTokenList.tokens)) -// copy(res, store.revokedTokenList.tokens) -// return res,nil + // res := make([]string, len(store.revokedTokenList.tokens)) + // copy(res, store.revokedTokenList.tokens) + // return res,nil return store.revokedTokenList.tokens, nil } diff --git a/authorizer/src/asapo_authorizer/token_store/token_store_test.go b/authorizer/src/asapo_authorizer/token_store/token_store_test.go index 550784d6364b95e3369a5a2de3f75661e3059e71..d648f0ef3afbcf9d259237f425a3ca7ccc85e449 100644 --- a/authorizer/src/asapo_authorizer/token_store/token_store_test.go +++ b/authorizer/src/asapo_authorizer/token_store/token_store_test.go @@ -127,7 +127,7 @@ func (suite *TokenStoreTestSuite) TestProcessRequestRevokeToken() { suite.Equal(token, expectedRevokedToken, "ok") } -func (suite *TokenStoreTestSuite) TestProcessRequestGetRevokedTokens() { +func (suite *TokenStoreTestSuite) TestProcessRequestCheckRevokedToken() { suite.mock_db.On("Close") suite.store.Close() common.Settings.UpdateRevokedTokensIntervalSec = 5 @@ -141,6 +141,7 @@ func (suite *TokenStoreTestSuite) TestProcessRequestGetRevokedTokens() { logger.MockLog.On("Debug", mock.MatchedBy(containsMatcher("list"))) time.Sleep(time.Second*1) - _,err := suite.store.GetRevokedTokenIds() + res,err := suite.store.IsTokenRevoked("123") suite.Equal(err, nil, "ok") + suite.Equal(false, res, "ok") } \ No newline at end of file diff --git a/deploy/asapo_helm_chart/asapo/configs/asapo-authorizer.json b/deploy/asapo_helm_chart/asapo/configs/asapo-authorizer.json index b6c3d5534036a419d0ba4ae7462bb569c00c7030..db3745ac6dfe47362541d0413508dd13714ef655 100644 --- a/deploy/asapo_helm_chart/asapo/configs/asapo-authorizer.json +++ b/deploy/asapo_helm_chart/asapo/configs/asapo-authorizer.json @@ -14,5 +14,6 @@ "Uri" : "ldap://localhost:389", "BaseDn" : "ou=rgy,o=desy,c=de", "FilterTemplate" : "(cn=a3__BEAMLINE__-hosts)" - } + }, + "UpdateRevokedTokensIntervalSec": 60 } diff --git a/deploy/asapo_services/scripts/authorizer.json.tpl b/deploy/asapo_services/scripts/authorizer.json.tpl index d5c24a2bb6c55fae2f5defe21a5d0577c7d08810..1d77de613c0a03883370bb4812b92b83f22d89b9 100644 --- a/deploy/asapo_services/scripts/authorizer.json.tpl +++ b/deploy/asapo_services/scripts/authorizer.json.tpl @@ -16,5 +16,6 @@ "FilterTemplate" : "(cn=a3__BEAMLINE__-hosts)" }, "DatabaseServer":"auto", - "DiscoveryServer": "localhost:8400/asapo-discovery" + "DiscoveryServer": "localhost:8400/asapo-discovery", + "UpdateRevokedTokensIntervalSec": 60 } diff --git a/tests/automatic/authorizer/check_authorize/check_linux.sh b/tests/automatic/authorizer/check_authorize/check_linux.sh index d881c72aac94e6fb5a9b3635b699332e026e9d40..340c715bce4db2135a33d2015d171d6599d921c6 100644 --- a/tests/automatic/authorizer/check_authorize/check_linux.sh +++ b/tests/automatic/authorizer/check_authorize/check_linux.sh @@ -6,6 +6,7 @@ trap Cleanup EXIT Cleanup() { echo cleanup + echo "db.dropDatabase()" | mongo asapo_admin } mkdir -p /tmp/asapo/asap3/petra3/gpfs/p00/2019/comissioning/c20180508-000-COM20181 @@ -20,8 +21,10 @@ cp beamtime-metadata-11111112.json /tmp/asapo/beamline/p08/current/ AdminToken=$ASAPO_CREATE_TOKEN echo admin $AdminToken -curl -v --silent -H "Authorization: Bearer $AdminToken" --data '{"Subject": {"beamtimeId":"12345678"},"DaysValid":123,"AccessType":["read"]}' 127.0.0.1:8400/asapo-authorizer/admin/issue --stderr - | tee /dev/stderr | grep "bt_12345678" -curl -v --silent -H "Authorization: Bearer blabla" --data '{"Subject": {"beamtimeId":"12345678"},"DaysValid":123,"AccessType":["read"]}' 127.0.0.1:8400/asapo-authorizer/admin/issue --stderr - | tee /dev/stderr | grep "token does not match" +RevokeToken=$ASAPO_REVOKE_TOKEN + +curl -v --silent -H "Authorization: Bearer $AdminToken" --data '{"Subject": {"beamtimeId":"12345678"},"DaysValid":123,"AccessTypes":["read"]}' 127.0.0.1:8400/asapo-authorizer/admin/issue --stderr - | tee /dev/stderr | grep "bt_12345678" +curl -v --silent -H "Authorization: Bearer blabla" --data '{"Subject": {"beamtimeId":"12345678"},"DaysValid":123,"AccessTypes":["read"]}' 127.0.0.1:8400/asapo-authorizer/admin/issue --stderr - | tee /dev/stderr | grep "token does not match" curl -v --silent --data '{"SourceCredentials":"processed%c20180508-000-COM20181%%detector%","OriginHost":"127.0.0.1:5555"}' 127.0.0.1:8400/asapo-authorizer/authorize --stderr - | tee /dev/stderr | grep c20180508-000-COM20181 curl -v --silent --data '{"SourceCredentials":"processed%c20180508-000-COM20181%%detector%","OriginHost":"127.0.0.1:5555"}' 127.0.0.1:8400/asapo-authorizer/authorize --stderr - | tee /dev/stderr | grep p00 @@ -59,4 +62,18 @@ curl -v --silent --data "{\"SourceCredentials\":\"processed%auto%p07%detector%$t token=$BLP07_W_TOKEN curl -v --silent --data "{\"SourceCredentials\":\"processed%auto%p07%detector%$token\",\"OriginHost\":\"bla\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr - | tee /dev/stderr | grep write + +#revocation +token=`curl --silent -H "Authorization: Bearer $AdminToken" --data '{"Subject": {"beamtimeId":"11000015"},"DaysValid":123,"AccessTypes":["read"]}' 127.0.0.1:8400/asapo-authorizer/admin/issue | jq -r .Token` +echo $token + +curl -v --silent --data "{\"SourceCredentials\":\"processed%11000015%auto%detector%$token\",\"OriginHost\":\"bla\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr - | tee /dev/stderr | grep p00 + +#revoke token +curl -v --silent -H "Authorization: Bearer $RevokeToken" --data '{"Token": "'"$token"'"}' 127.0.0.1:8400/asapo-authorizer/admin/revoke | grep '"Revoked":true' + +sleep 1 + +curl -v --silent --data "{\"SourceCredentials\":\"processed%11000015%auto%detector%$token\",\"OriginHost\":\"bla\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr - | tee /dev/stderr | grep 401 + rm -rf /tmp/asapo/asap3 /tmp/asapo/beamline \ No newline at end of file diff --git a/tests/automatic/settings/authorizer_settings.json.tpl.lin b/tests/automatic/settings/authorizer_settings.json.tpl.lin index 1de8b3d3b3342a6c2a363e2fa9221c93d6df030f..dd30b9d3c0a6f7361f55b2a700cd39b37e5b3b67 100644 --- a/tests/automatic/settings/authorizer_settings.json.tpl.lin +++ b/tests/automatic/settings/authorizer_settings.json.tpl.lin @@ -17,5 +17,6 @@ "FilterTemplate" : "(cn=a3__BEAMLINE__-hosts)" }, "DatabaseServer":"auto", - "DiscoveryServer": "localhost:8400/asapo-discovery" + "DiscoveryServer": "localhost:8400/asapo-discovery", + "UpdateRevokedTokensIntervalSec": 60 } diff --git a/tests/automatic/settings/authorizer_settings.json.tpl.win b/tests/automatic/settings/authorizer_settings.json.tpl.win index cbf7535bc0974c0353b198b9c089e2b259fee203..dff507941808259b140f48ad9ec816b643c57bb5 100644 --- a/tests/automatic/settings/authorizer_settings.json.tpl.win +++ b/tests/automatic/settings/authorizer_settings.json.tpl.win @@ -17,5 +17,6 @@ "FilterTemplate" : "(cn=a3__BEAMLINE__-hosts)" }, "DatabaseServer":"auto", - "DiscoveryServer": "localhost:8400/asapo-discovery" + "DiscoveryServer": "localhost:8400/asapo-discovery", + "UpdateRevokedTokensIntervalSec": 60 }