예제 #1
0
파일: handler.go 프로젝트: thanzen/hydra
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)
}
예제 #2
0
파일: handler.go 프로젝트: thanzen/hydra
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)
}
예제 #3
0
파일: handler.go 프로젝트: thanzen/hydra
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)
}
예제 #4
0
파일: user.go 프로젝트: thanzen/hydra
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())
	}
}
예제 #5
0
파일: client.go 프로젝트: thanzen/hydra
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)
	}
}
예제 #6
0
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)
		})
	}
}
예제 #7
0
파일: helper.go 프로젝트: thanzen/hydra
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{".*"},
	}
}
예제 #8
0
파일: handler.go 프로젝트: thanzen/hydra
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)
}
예제 #9
0
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())
}
예제 #10
0
파일: handler.go 프로젝트: thanzen/hydra
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)
}
예제 #11
0
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)
	}
}
예제 #12
0
파일: handler.go 프로젝트: thanzen/hydra
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)
}
예제 #13
0
파일: store_test.go 프로젝트: thanzen/hydra
		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])
예제 #14
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)
	}
}
예제 #15
0
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())
}