예제 #1
1
func changeAccountPassword(ctx context.Context, w http.ResponseWriter, r *http.Request) {

	var acct account.Account

	var email string
	var newPassword string

	// read the account ID
	if emailBytes, err := base64.RawURLEncoding.DecodeString(rest.Param(ctx, "id")); err != nil {
		rest.WriteJSON(w, err)
		return
	} else {
		email = string(emailBytes)
	}

	if err := rest.ReadJSON(r, &newPassword); err != nil {
		rest.WriteJSON(w, err)
	} else if err := account.Get(ctx, email, &acct); err != nil {
		rest.WriteJSON(w, err)
	} else if err := acct.SetPassword(newPassword); err != nil {
		rest.WriteJSON(w, err)
	} else if err := account.Save(ctx, &acct); err != nil {
		rest.WriteJSON(w, err)
	} else {
		rest.WriteJSON(w, &rest.NoContent)
	}

}
예제 #2
1
// HasRole returns an AuthCheck that grants access under the following conditions:
//	- The account specified by the token has the specified role.
//	- The token itself has that role in scope.
//	- The token is not used up.
func HasRole(role string) AuthCheck {

	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {

		var acct account.Account
		var claimSet jws.ClaimSet

		if err := GetAccount(ctx, &acct); err != nil {
			// we can't get the user, so we can't check authentication status.
			log.Errorf(ctx, "Error getting account for authentication: %s", err.Error())
			return ErrCannotGetAccount
		} else if err := GetClaimSet(ctx, &claimSet); err != nil {
			log.Errorf(ctx, "Error getting claim set for authentication: %s", err.Error())
			return ErrCannotGetClaimSet
		} else if !acct.HasRole(role) {
			// the account making the request does not have the specified role.
			return ErrRoleMissing
		} else if !roleInScope(ctx, role) {
			// the JWT claimset for this request does not have the specified role in its scope.
			return ErrRoleNotInScope
		} else if err = UseClaimSet(ctx, &claimSet); err == ErrClaimSetUsedUp {
			// The claimset for this request has been all used up.
			return err
		} else if err != nil {
			return errors.New(http.StatusBadRequest, err.Error())
		} else {
			// All ok; this request's account may access the resource.
			return nil
		}

	}

}
예제 #3
1
// Super is an AuthCheck that grants access to the superuser (that is, the user
// identified by `Authorization: <secret>`).
func Super(ctx context.Context, w http.ResponseWriter, r *http.Request) error {

	var acct account.Account
	if err := GetAccount(ctx, &acct); err != nil {
		return err
	} else if !acct.Super() {
		return ErrForbidden
	} else {
		return nil
	}

}
예제 #4
1
// AccountMatchesParam returns an AuthCheck that grants access if paramName is the same
// as the account's ID; so, for instance, on a route to /accounts/:accountId, with
// a request to /accounts/asdf, the AuthCheck will return true if the account's ID is asdf.
// As a special case, account.Nobody and account.Super will never match in this method.
func AccountMatchesParam(paramName string) AuthCheck {

	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {

		var acct account.Account
		if err := GetAccount(ctx, &acct); err != nil {
			return err
		} else if acct.Super() || acct.Nobody() {
			return ErrAccountIDDoesNotMatch
		} else if acct.Key(ctx).Encode() != kami.Param(ctx, paramName) {
			return ErrAccountIDDoesNotMatch
		} else {
			return nil
		}

	}

}
예제 #5
1
func TestMiddleware(t *testing.T) {

	var acct account.Account
	ctx := test.WithConfig(context.Background(), map[string]interface{}{"AuthSecret": "foo"})

	// a user is no one (i.e., no Authorization header)
	w := httptest.NewRecorder()
	r, _ := http.NewRequest("GET", "/", nil)

	resultCtx := Middleware(ctx, w, r)

	err := GetAccount(resultCtx, &acct)
	if err != nil {
		t.Errorf("Unexpected error %s", err)
	}
	if !acct.Nobody() {
		t.Errorf("acct should have been the zero value, but got %+v", acct)
	}

	// bad jwt
	w = httptest.NewRecorder()
	r, _ = http.NewRequest("GET", "/", nil)
	r.Header.Set("Authorization", "wrong")

	resultCtx = Middleware(ctx, w, r)

	err = GetAccount(resultCtx, &acct)
	if err != InvalidJWTError {
		t.Errorf("Unexpected error %s", err)
	}

	// super (i.e., they passed in the secret itself)
	w = httptest.NewRecorder()
	r, _ = http.NewRequest("GET", "/", nil)
	r.Header.Set("Authorization", "foo")

	resultCtx = Middleware(ctx, w, r)

	err = GetAccount(resultCtx, &acct)
	if err != nil {
		t.Errorf("Unexpected error %s", err)
	}
	if !acct.Super() {
		t.Errorf("Expected the super account, but got %+v", acct)
	}

	// super (i.e., they passed in the secret itself)
	w = httptest.NewRecorder()
	r, _ = http.NewRequest("GET", "/", nil)
	r.Header.Set("Authorization", "foo")

	resultCtx = Middleware(ctx, w, r)

	err = GetAccount(resultCtx, &acct)
	if err != nil {
		t.Errorf("Unexpected error %s", err)
	}
	if !acct.Super() {
		t.Errorf("Expected the super account, but got %+v", acct)
	}

	// test against App Engine dev environment from this point forward
	realCtx, done, _ := aetest.NewContext()
	defer done()

	account.New(realCtx, "*****@*****.**", "foobar")

	realCtx = test.WithConfig(realCtx, map[string]interface{}{"AuthSecret": "foo"})

	// nonexistent account
	w = httptest.NewRecorder()
	r, _ = http.NewRequest("GET", "/", nil)
	r.Header.Set("Authorization", test.JWT(&jws.ClaimSet{Sub: "*****@*****.**"}, "foo"))

	resultCtx = Middleware(realCtx, w, r)
	if resultCtx != nil {
		t.Errorf("Expected Middleware to terminate (i.e. to return nil), but it didn't", err)
	}

	// existent account (i.e., the happy path)
	w = httptest.NewRecorder()
	r, _ = http.NewRequest("GET", "/", nil)
	r.Header.Set("Authorization", test.JWT(&jws.ClaimSet{Sub: "*****@*****.**"}, "foo"))

	resultCtx = Middleware(realCtx, w, r)
	if resultCtx == nil {
		t.Errorf("Expected Middleware not to terminate (i.e. to return another context), but it terminated")
	} else if err = GetAccount(resultCtx, &acct); err != nil {
		t.Errorf("Expected no error when getting a properly authenticated account, but got %s", err)
	} else if acct.Email != "*****@*****.**" {
		t.Errorf("Unexpected account on retrieval: %+v", acct)
	}

}