func (h *Handler) TokenHandler(w http.ResponseWriter, r *http.Request) { resp := h.server.NewResponse() r.ParseForm() defer resp.Close() if ar := h.server.HandleAccessRequest(resp, r); ar != nil { switch ar.Type { case osin.AUTHORIZATION_CODE: data, ok := ar.UserData.(string) if !ok { http.Error(w, fmt.Sprintf("Could not assert UserData to string: %v", ar.UserData), http.StatusInternalServerError) return } var claims jwt.ClaimsCarrier if err := json.Unmarshal([]byte(data), &claims); err != nil { http.Error(w, fmt.Sprintf("Could not unmarshal UserData: %v", ar.UserData), http.StatusInternalServerError) return } ar.UserData = jwt.NewClaimsCarrier(uuid.New(), claims.GetSubject(), h.Issuer, h.Audience, time.Now(), time.Now()) ar.Authorized = true case osin.REFRESH_TOKEN: data, ok := ar.UserData.(map[string]interface{}) if !ok { http.Error(w, fmt.Sprintf("Could not assert UserData type: %v", ar.UserData), http.StatusInternalServerError) return } claims := jwt.ClaimsCarrier(data) ar.UserData = jwt.NewClaimsCarrier(uuid.New(), claims.GetSubject(), h.Issuer, h.Audience, time.Now(), time.Now()) ar.Authorized = true case osin.PASSWORD: // TODO if !ar.Client.isAllowedToAuthenticateUser // TODO ... return // TODO } if user, err := h.authenticate(w, r, ar.Username, ar.Password); err == nil { ar.UserData = jwt.NewClaimsCarrier(uuid.New(), user.GetID(), h.Issuer, h.Audience, time.Now(), time.Now()) ar.Authorized = true } case osin.CLIENT_CREDENTIALS: ar.UserData = jwt.NewClaimsCarrier(uuid.New(), ar.Client.GetId(), h.Issuer, h.Audience, time.Now(), time.Now()) ar.Authorized = true // TODO ASSERTION workflow http://leastprivilege.com/2013/12/23/advanced-oauth2-assertion-flow-why/ // TODO Since assertions are only a draft for now and there is no need for SAML or similar this is postponed. //case osin.ASSERTION: // if ar.AssertionType == "urn:hydra" && ar.Assertion == "osin.data" { // ar.Authorized = true // } } h.server.FinishAccessRequest(resp, r, ar) } if resp.IsError { resp.StatusCode = http.StatusUnauthorized } osin.OutputJSON(resp, w, r) }
func (h *Handler) Create(ctx context.Context, rw http.ResponseWriter, req *http.Request) { type Payload struct { Email string `valid:"email,required" json:"email" ` Password string `valid:"length(6|254),required" json:"password"` Data string `valid:"optional,json", json:"data"` } var p Payload decoder := json.NewDecoder(req.Body) if err := decoder.Decode(&p); err != nil { http.Error(rw, err.Error(), http.StatusBadRequest) return } if v, err := govalidator.ValidateStruct(p); !v { if err != nil { http.Error(rw, err.Error(), http.StatusBadRequest) return } http.Error(rw, "Payload did not validate.", http.StatusBadRequest) return } if p.Data == "" { p.Data = "{}" } user, err := h.s.Create(uuid.New(), p.Email, p.Password, p.Data) if err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) return } WriteJSON(rw, user) }
func (h *Handler) Create(ctx context.Context, rw http.ResponseWriter, req *http.Request) { subject, ok := mux.Vars(req)["subject"] if !ok { http.Error(rw, "No subject given.", http.StatusBadRequest) return } var conn DefaultConnection decoder := json.NewDecoder(req.Body) if err := decoder.Decode(&conn); err != nil { http.Error(rw, "Could not decode request: "+err.Error(), http.StatusBadRequest) return } if v, err := govalidator.ValidateStruct(conn); !v { if err != nil { http.Error(rw, err.Error(), http.StatusBadRequest) return } http.Error(rw, "Payload did not validate.", http.StatusBadRequest) return } conn.ID = uuid.New() conn.LocalSubject = subject if err := h.s.Create(&conn); err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) return } WriteJSON(rw, &conn) }
func (c *User) Create(ctx *cli.Context) { email := ctx.Args().First() if email == "" { log.Fatalf("Please provide an email address.") } password := ctx.String("password") if password == "" { password = getPassword() } c.Ctx.Start() user, err := c.Ctx.Accounts.Create(uuid.New(), email, password, "{}") if err != nil { log.Fatalf("%s", err) } fmt.Printf(`Created user as "%s".`+"\n", user.GetID()) if ctx.Bool("as-superuser") { if err := c.Ctx.Policies.Create(superUserPolicy(user.GetID())); err != nil { log.Fatalf("%s", err) } fmt.Printf(`Granted superuser privileges to user "%s".`+"\n", user.GetID()) } }
func (c *Client) Create(ctx *cli.Context) { seq, err := sequence.RuneSequence(10, []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")) if err != nil { log.Fatalf("err") } client := &osin.DefaultClient{ Id: uuid.New(), Secret: string(seq), RedirectUri: "", UserData: "", } c.Ctx.Start() if err := c.Ctx.Osins.CreateClient(client); err != nil { log.Fatalf("%s", err) } fmt.Printf(`Created client "%s" with secret "%s".`+"\n", client.Id, client.Secret) if ctx.Bool("as-superuser") { if err := c.Ctx.Policies.Create(superUserPolicy(client.Id)); err != nil { log.Fatalf("%s", err) } fmt.Printf(`Granted superuser privileges to client "%s".`+"\n", client.Id) } }
func mockAuthorization(c test) func(h hcon.ContextHandler) hcon.ContextHandler { return func(h hcon.ContextHandler) hcon.ContextHandler { return hcon.ContextHandlerFunc(func(ctx context.Context, rw http.ResponseWriter, req *http.Request) { claims := hjwt.NewClaimsCarrier(uuid.New(), "hydra", c.subject, "tests", time.Now(), time.Now()) ctx = hcon.NewContextFromAuthValues(ctx, claims, &c.token, c.policies) h.ServeHTTPContext(ctx, rw, req) }) } }
func superUserPolicy(subject string) policy.Policy { return &policy.DefaultPolicy{ ID: uuid.New(), Description: "Super user policy generated by the CLI.", Effect: policy.AllowAccess, Subjects: []string{subject}, Permissions: []string{".*"}, Resources: []string{".*"}, } }
func (h *Handler) AuthorizeHandler(w http.ResponseWriter, r *http.Request) { resp := h.server.NewResponse() defer resp.Close() if ar := h.server.HandleAuthorizeRequest(resp, r); ar != nil { // For now, a provider must be given. // TODO there should be a fallback provider which is a redirect to the login endpoint. This should be configurable by env var. // Let's see if this is a valid provider. If not, return an error. provider, err := h.Providers.Find(r.URL.Query().Get("provider")) if err != nil { http.Error(w, fmt.Sprintf(`Provider "%s" not known.`, err), http.StatusBadRequest) return } // This could be made configurable with `connection.GetCodeKeyName()` code := r.URL.Query().Get("access_code") if code == "" { // If no code was given we have to initiate the provider's authorization workflow url := provider.GetAuthCodeURL(ar) http.Redirect(w, r, url, http.StatusFound) return } // Create a session by exchanging the code for the auth code connection, err := provider.Exchange(code) if err != nil { http.Error(w, fmt.Sprintf("Could not exchange access code: %s", err), http.StatusUnauthorized) return } subject := connection.GetRemoteSubject() user, err := h.Connections.FindByRemoteSubject(provider.GetID(), subject) if err == account.ErrNotFound { // The subject is not linked to any account. http.Error(w, "Provided token is not linked to any existing account.", http.StatusUnauthorized) return } else if err != nil { // Something else went wrong http.Error(w, fmt.Sprintf("Could assert subject claim: %s", err), http.StatusInternalServerError) return } ar.UserData = jwt.NewClaimsCarrier(uuid.New(), user.GetLocalSubject(), h.Issuer, h.Audience, time.Now(), time.Now()) ar.Authorized = true h.server.FinishAuthorizeRequest(resp, r, ar) } if resp.IsError { resp.StatusCode = http.StatusUnauthorized } osin.OutputJSON(resp, w, r) }
func TestConnection(t *testing.T) { c := &DefaultConnection{ ID: uuid.New(), LocalSubject: "peter", RemoteSubject: "*****@*****.**", Provider: "google", } assert.Equal(t, c.ID, c.GetID()) assert.Equal(t, c.Provider, c.GetProvider()) assert.Equal(t, c.LocalSubject, c.GetLocalSubject()) assert.Equal(t, c.RemoteSubject, c.GetRemoteSubject()) }
func (h *Handler) Create(ctx context.Context, rw http.ResponseWriter, req *http.Request) { var p DefaultPolicy decoder := json.NewDecoder(req.Body) if err := decoder.Decode(&p); err != nil { http.Error(rw, err.Error(), http.StatusBadRequest) return } p.ID = uuid.New() if err := h.s.Create(&p); err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) return } pkg.WriteJSON(rw, p) }
func TestClaimsCarrier(t *testing.T) { for k, c := range []struct { id string issuer string subject string audience string notBefore time.Time issuedAt time.Time }{ {uuid.New(), "hydra", "peter", "app", time.Now(), time.Now()}, } { carrier := NewClaimsCarrier(c.id, c.issuer, c.subject, c.audience, c.notBefore, c.issuedAt) assert.Equal(t, c.id, carrier.GetID(), "Case %d", k) assert.Equal(t, c.issuer, carrier.GetIssuer(), "Case %d", k) assert.Equal(t, c.subject, carrier.GetSubject(), "Case %d", k) assert.Equal(t, c.audience, carrier.GetAudience(), "Case %d", k) assert.Equal(t, c.notBefore, carrier.GetNotBefore(), "Case %d", k) assert.Equal(t, c.issuedAt, carrier.GetIssuedAt(), "Case %d", k) assert.Empty(t, carrier.getAsString("doesnotexist"), "Case %d", k) assert.Equal(t, time.Time{}, carrier.getAsTime("doesnotexist"), "Case %d", k) assert.NotEmpty(t, carrier.String(), "Case %d", k) } }
func (h *Handler) Create(ctx context.Context, rw http.ResponseWriter, req *http.Request) { var p payload decoder := json.NewDecoder(req.Body) if err := decoder.Decode(&p); err != nil { http.Error(rw, err.Error(), http.StatusBadRequest) return } if v, err := govalidator.ValidateStruct(p); !v { if err != nil { http.Error(rw, err.Error(), http.StatusBadRequest) return } http.Error(rw, "Payload did not validate.", http.StatusBadRequest) return } secret, err := sequence.RuneSequence(12, []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")) if err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) return } client := &osin.DefaultClient{ Id: uuid.New(), Secret: string(secret), RedirectUri: p.RedirectURIs, UserData: "{}", } if err := h.s.CreateClient(client); err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) return } WriteJSON(rw, client) }
log.Fatalf("Could not connect to database: %s", err) } defer c.KillRemove() if err := db.Ping(); err != nil { log.Fatalf("Could not ping: %s", err) } store = New(db) if err := store.CreateSchemas(); err != nil { log.Fatalf("Could not ping: %s", err) } os.Exit(m.Run()) } var connection = &DefaultConnection{ID: uuid.New(), LocalSubject: "peter", RemoteSubject: "peterson", Provider: "google"} func TestCreateGetFindDelete(t *testing.T) { require.Nil(t, store.Create(connection)) c, err := store.Get(connection.ID) require.Nil(t, err) require.Equal(t, connection, c) c, err = store.FindByRemoteSubject("google", "peterson") require.Nil(t, err) require.Equal(t, connection, c) cs, err := store.FindAllByLocalSubject("peter") require.Nil(t, err) require.Equal(t, connection, cs[0])
func TestCreateGetDelete(t *testing.T) { for k, c := range []test{ test{ subject: "peter", token: &jwt.Token{Valid: false}, expected: result{create: http.StatusUnauthorized, get: 0, delete: 0}, }, test{ subject: "peter", token: &jwt.Token{Valid: true}, policies: policies["empty"], expected: result{create: http.StatusForbidden, get: 0, delete: 0}, }, test{ subject: "peter", token: &jwt.Token{Valid: true}, policies: policies["empty"], expected: result{create: http.StatusForbidden, get: 0, delete: 0}, }, test{ subject: "max", token: &jwt.Token{Valid: true}, policies: policies["empty"], expected: result{create: http.StatusForbidden, get: 0, delete: 0}, }, test{ subject: "max", token: &jwt.Token{Valid: true}, payload: payload{}, policies: policies["allow-create"], expected: result{ create: http.StatusForbidden, get: 0, delete: 0, }, }, test{ subject: "peter", token: &jwt.Token{Valid: true}, payload: payload{Email: uuid.New() + "@foobar.com", Data: "{}"}, policies: policies["allow-create"], expected: result{ create: http.StatusBadRequest, get: 0, delete: 0, }, }, test{ subject: "peter", token: &jwt.Token{Valid: true}, payload: payload{Email: uuid.New() + "@foobar.com", Password: "******", Data: "{}"}, policies: policies["allow-create"], expected: result{ create: http.StatusBadRequest, get: 0, delete: 0, }, }, test{ subject: "peter", token: &jwt.Token{Valid: true}, payload: payload{Email: "notemail", Password: "******", Data: "{}"}, policies: policies["allow-create"], expected: result{ create: http.StatusBadRequest, get: 0, delete: 0, }, }, test{ subject: "peter", token: &jwt.Token{Valid: true}, payload: payload{Email: uuid.New() + "@bar.com", Password: "", Data: "{}"}, policies: policies["allow-create"], expected: result{ create: http.StatusBadRequest, get: 0, delete: 0, }, }, test{ subject: "peter", token: &jwt.Token{Valid: true}, payload: payload{Email: uuid.New() + "@bar.com", Password: "******", Data: "not json"}, policies: policies["allow-create"], expected: result{ create: http.StatusBadRequest, get: 0, delete: 0, }, }, test{ subject: "peter", token: &jwt.Token{Valid: true}, payload: payload{Email: uuid.New() + "@bar.com", Password: "******", Data: "{}"}, policies: policies["allow-create"], expected: result{ create: http.StatusOK, get: http.StatusForbidden, delete: http.StatusForbidden, }, }, test{ subject: "peter", token: &jwt.Token{Valid: true}, payload: payload{Email: uuid.New() + "@bar.com", Password: "******", Data: "{}"}, policies: policies["allow-create-get"], expected: result{ create: http.StatusOK, get: http.StatusOK, delete: http.StatusForbidden, }, }, test{ subject: "peter", token: &jwt.Token{Valid: true}, payload: payload{Email: uuid.New() + "@bar.com", Password: "******", Data: "{}"}, policies: policies["allow-all"], expected: result{ create: http.StatusOK, get: http.StatusOK, delete: http.StatusAccepted, }, }, } { router := mux.NewRouter() hd.SetRoutes(router, mock(c)) ts := httptest.NewServer(router) defer ts.Close() t.Logf(ts.URL + "/accounts") request := gorequest.New() resp, body, _ := request.Post(ts.URL + "/accounts").Send(c.payload).End() require.Equal(t, c.expected.create, resp.StatusCode, "case %d: %s", k, body) if resp.StatusCode != http.StatusOK { return } user := assertAccount(t, c, body) resp, body, _ = request.Get(ts.URL + "/accounts/" + user.GetID()).End() require.Equal(t, c.expected.get, resp.StatusCode, "case %d: %s", k, body) if resp.StatusCode != http.StatusOK { return } user = assertAccount(t, c, body) resp, body, _ = request.Delete(ts.URL + "/accounts/" + user.GetID()).End() require.Equal(t, c.expected.delete, resp.StatusCode, "case %d: %s", k, body) if resp.StatusCode != http.StatusAccepted { return } resp, body, _ = request.Get(ts.URL + "/accounts/" + user.GetID()).End() require.Equal(t, http.StatusNotFound, resp.StatusCode, "case %d: %s", k, body) } }
func TestMain(m *testing.M) { c, db, err := dockertest.OpenPostgreSQLContainerConnection(15, time.Second) if err != nil { log.Fatalf("Could not connect to database: %s", err) } defer c.KillRemove() accountStore := acpg.New(&hash.BCrypt{10}, db) policyStore := ppg.New(db) osinStore := opg.New(db) connectionStore := cpg.New(db) registry := provider.NewRegistry([]provider.Provider{&prov{}}) j := jwt.New([]byte(jwt.TestCertificates[0][1]), []byte(jwt.TestCertificates[1][1])) if err := connectionStore.CreateSchemas(); err != nil { log.Fatalf("Could not set up schemas: %v", err) } else if err := policyStore.CreateSchemas(); err != nil { log.Fatalf("Could not set up schemas: %v", err) } else if err := accountStore.CreateSchemas(); err != nil { log.Fatalf("Could not set up schemas: %v", err) } else if err := osinStore.CreateSchemas(); err != nil { log.Fatalf("Could not set up schemas: %v", err) } handler = &Handler{ OAuthConfig: DefaultConfig(), OAuthStore: osinStore, JWT: j, Accounts: accountStore, Policies: policyStore, Guard: new(guard.Guard), Connections: connectionStore, Providers: registry, Issuer: "hydra", Audience: "tests", } pol := policy.DefaultPolicy{ ID: "3", Description: "", Effect: policy.AllowAccess, Subjects: []string{}, Permissions: []string{"authorize"}, Resources: []string{"/oauth2/authorize"}, Conditions: []policy.Condition{}, } if err := osinStore.CreateClient(&osin.DefaultClient{"1", "secret", "/callback", ""}); err != nil { log.Fatalf("Could create client: %s", err) } else if _, err := accountStore.Create("2", "*****@*****.**", "secret", "{}"); err != nil { log.Fatalf("Could create account: %s", err) } else if err := policyStore.Create(&pol); err != nil { log.Fatalf("Could create client: %s", err) } else if err := connectionStore.Create(&connection.DefaultConnection{ ID: uuid.New(), Provider: "MockProvider", LocalSubject: "2", RemoteSubject: "remote-id", }); err != nil { log.Fatalf("Could create client: %s", err) } os.Exit(m.Run()) }