func TestStringOrNull(t *testing.T) { var ( nullString sql.NullString value driver.Value err error ) // When the string is empty nullString = util.StringOrNull("") // nullString.Valid should be false assert.False(t, nullString.Valid) // nullString.Value() should return nil value, err = nullString.Value() assert.Nil(t, err) assert.Nil(t, value) // When the string is not empty nullString = util.StringOrNull("foo") // nullString.Valid should be true assert.True(t, nullString.Valid) // nullString.Value() should return the string value, err = nullString.Value() assert.Nil(t, err) assert.Equal(t, "foo", value) }
func (s *Service) createUserCommon(db *gorm.DB, roleID, username, password string) (*models.OauthUser, error) { // Start with a user without a password user := &models.OauthUser{ RoleID: util.StringOrNull(roleID), Username: strings.ToLower(username), Password: util.StringOrNull(""), } // If the password is being set already, create a bcrypt hash if password != "" { if len(password) < MinPasswordLength { return nil, ErrPasswordTooShort } passwordHash, err := pass.HashPassword(password) if err != nil { return nil, err } user.Password = util.StringOrNull(string(passwordHash)) } // Check the username is available if s.UserExists(user.Username) { return nil, ErrUsernameTaken } // Create the user if err := db.Create(user).Error; err != nil { return nil, err } return user, nil }
func (s *Service) createUserCommon(db *gorm.DB, username, password string) (*User, error) { // Start with a user without a password user := &User{ Username: username, Password: util.StringOrNull(""), } // If the password is being set already, create a bcrypt hash if password != "" { passwordHash, err := pass.HashPassword(password) if err != nil { return nil, err } user.Password = util.StringOrNull(string(passwordHash)) } // Check the username is available if s.UserExists(user.Username) { return nil, ErrUsernameTaken } // Create the user if err := db.Create(user).Error; err != nil { return nil, err } return user, nil }
func (suite *OauthTestSuite) TestAuthorizationCodeGrantInvalidRedirectURI() { // Insert a test authorization code err := suite.db.Create(&models.OauthAuthorizationCode{ Code: "test_code", ExpiresAt: time.Now().UTC().Add(+10 * time.Second), Client: suite.clients[0], User: suite.users[0], RedirectURI: util.StringOrNull("https://www.example.com"), Scope: "read_write", }).Error assert.NoError(suite.T(), err, "Inserting test data failed") // Prepare a request r, err := http.NewRequest("POST", "http://1.2.3.4/v1/oauth/tokens", nil) assert.NoError(suite.T(), err, "Request setup should not get an error") r.SetBasicAuth("test_client_1", "test_secret") r.PostForm = url.Values{ "grant_type": {"authorization_code"}, "code": {"test_code"}, "redirect_uri": {"https://bogus"}, } // Serve the request w := httptest.NewRecorder() suite.router.ServeHTTP(w, r) // Check the response testutil.TestResponseForError( suite.T(), w, oauth.ErrInvalidRedirectURI.Error(), 400, ) }
func (suite *OauthTestSuite) TestSetPassword() { var ( user *User err error ) // Insert a test user without a password user = &User{ Username: "******", Password: util.StringOrNull(""), } if err := suite.db.Create(user).Error; err != nil { log.Fatal(err) } // Try to set an empty password err = suite.service.SetPassword(user, "") // Correct error should be returned if assert.NotNil(suite.T(), err) { assert.Equal(suite.T(), errCannotSetEmptyUserPassword, err) } // Try changing the password err = suite.service.SetPassword(user, "test_password") // Error should be nil assert.Nil(suite.T(), err) // User object should have been updated assert.Equal(suite.T(), "test@user_nopass", user.Username) assert.Nil(suite.T(), pass.VerifyPassword(user.Password.String, "test_password")) }
func (suite *OauthTestSuite) TestAuthUser() { var ( user *User err error ) // Insert a test user without a password if err := suite.db.Create(&User{ Username: "******", Password: util.StringOrNull(""), }).Error; err != nil { log.Fatal(err) } // When we try to authenticate a user without a password user, err = suite.service.AuthUser("test@user_nopass", "bogus") // User object should be nil assert.Nil(suite.T(), user) // Correct error should be returned if assert.NotNil(suite.T(), err) { assert.Equal(suite.T(), errUserPasswordNotSet, err) } // When we try to authenticate with a bogus username user, err = suite.service.AuthUser("bogus", "test_password") // User object should be nil assert.Nil(suite.T(), user) // Correct error should be returned if assert.NotNil(suite.T(), err) { assert.Equal(suite.T(), errUserNotFound, err) } // When we try to authenticate with an invalid password user, err = suite.service.AuthUser("test@user", "bogus") // User object should be nil assert.Nil(suite.T(), user) // Correct error should be returned if assert.NotNil(suite.T(), err) { assert.Equal(suite.T(), errInvalidUserPassword, err) } // When we try to authenticate with valid username and password user, err = suite.service.AuthUser("test@user", "test_password") // Error should be nil assert.Nil(suite.T(), err) // Correct user object should be returned if assert.NotNil(suite.T(), user) { assert.Equal(suite.T(), "test@user", user.Username) } }
func (s *Service) authorizationCodeGrant(w http.ResponseWriter, r *http.Request, client *Client) { // Fetch the authorization code authorizationCode, err := s.getValidAuthorizationCode( r.Form.Get("code"), // authorization code client, // client ) if err != nil { response.Error(w, err.Error(), http.StatusBadRequest) return } // Redirect URI must match if it was used to obtain the authorization code if util.StringOrNull(r.Form.Get("redirect_uri")) != authorizationCode.RedirectURI { response.Error(w, errInvalidRedirectURI.Error(), http.StatusBadRequest) return } // Create a new access token accessToken, err := s.GrantAccessToken( authorizationCode.Client, // client authorizationCode.User, // user authorizationCode.Scope, // scope ) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Create or retrieve a refresh token refreshToken, err := s.GetOrCreateRefreshToken( authorizationCode.Client, // client authorizationCode.User, // user authorizationCode.Scope, // scope ) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Delete the authorization code s.db.Unscoped().Delete(&authorizationCode) // Write the JSON access token to the response accessTokenRespone := &AccessTokenResponse{ ID: accessToken.ID, AccessToken: accessToken.Token, ExpiresIn: s.cnf.Oauth.AccessTokenLifetime, TokenType: "Bearer", Scope: accessToken.Scope, RefreshToken: refreshToken.Token, } response.WriteJSON(w, accessTokenRespone, 200) }
func createUser(db *gorm.DB, username, password string) (*User, error) { // Start with a user without a password user := User{ Username: username, Password: util.StringOrNull(""), } // If the password is being set already, create a bcrypt hash if password != "" { passwordHash, err := pass.HashPassword(password) if err != nil { return nil, err } user.Password = util.StringOrNull(string(passwordHash)) } // Create the user if err := db.Create(&user).Error; err != nil { return nil, err } return &user, nil }
// CreateClient saves a new client to database func (s *Service) CreateClient(clientID, secret, redirectURI string) (*Client, error) { secretHash, err := password.HashPassword(secret) if err != nil { return nil, err } client := &Client{ Key: clientID, Secret: string(secretHash), RedirectURI: util.StringOrNull(redirectURI), } if err := s.db.Create(client).Error; err != nil { return nil, err } return client, nil }
// CreateClient saves a new client to database func (s *Service) CreateClient(clientID, secret, redirectURI string) (*Client, error) { secretHash, err := password.HashPassword(secret) if err != nil { return nil, errors.New("Bcrypt error") } client := &Client{ ClientID: clientID, Secret: string(secretHash), RedirectURI: util.StringOrNull(redirectURI), } if err := s.db.Create(client).Error; err != nil { return nil, errors.New("Error saving client to database") } return client, nil }
func (suite *OauthTestSuite) TestAuthorizationCodeGrant() { // Insert a test authorization code err := suite.db.Create(&models.OauthAuthorizationCode{ Code: "test_code", ExpiresAt: time.Now().UTC().Add(+10 * time.Second), Client: suite.clients[0], User: suite.users[0], RedirectURI: util.StringOrNull("https://www.example.com"), Scope: "read_write", }).Error assert.NoError(suite.T(), err, "Inserting test data failed") // Prepare a request r, err := http.NewRequest("POST", "http://1.2.3.4/v1/oauth/tokens", nil) assert.NoError(suite.T(), err, "Request setup should not get an error") r.SetBasicAuth("test_client_1", "test_secret") r.PostForm = url.Values{ "grant_type": {"authorization_code"}, "code": {"test_code"}, "redirect_uri": {"https://www.example.com"}, } // Serve the request w := httptest.NewRecorder() suite.router.ServeHTTP(w, r) // Fetch data accessToken, refreshToken := new(models.OauthAccessToken), new(models.OauthRefreshToken) assert.False(suite.T(), models.OauthAccessTokenPreload(suite.db). Last(accessToken).RecordNotFound()) assert.False(suite.T(), models.OauthRefreshTokenPreload(suite.db). Last(refreshToken).RecordNotFound()) // Check the response expected := &oauth.AccessTokenResponse{ UserID: accessToken.User.MetaUserID, AccessToken: accessToken.Token, ExpiresIn: 3600, TokenType: tokentypes.Bearer, Scope: "read_write", RefreshToken: refreshToken.Token, } testutil.TestResponseObject(suite.T(), w, expected, 200) // The authorization code should get deleted after use assert.True(suite.T(), suite.db.Unscoped(). First(new(models.OauthAuthorizationCode)).RecordNotFound()) }
func (s *Service) setPasswordCommon(db *gorm.DB, user *models.OauthUser, password string) error { if len(password) < MinPasswordLength { return ErrPasswordTooShort } // Create a bcrypt hash passwordHash, err := pass.HashPassword(password) if err != nil { return err } // Set the password on the user object return db.Model(user).UpdateColumns(models.OauthUser{ Password: util.StringOrNull(string(passwordHash)), Model: gorm.Model{UpdatedAt: time.Now().UTC()}, }).Error }
// newAuthorizationCode creates new AuthorizationCode instance func newAuthorizationCode(expiresIn int, client *Client, user *User, redirectURI, scope string) *AuthorizationCode { clientID := util.IntOrNull(int64(client.ID)) userID := util.IntOrNull(int64(user.ID)) authorizationCode := &AuthorizationCode{ Code: uuid.New(), ExpiresAt: time.Now().Add(time.Duration(expiresIn) * time.Second), RedirectURI: util.StringOrNull(redirectURI), Scope: scope, ClientID: clientID, UserID: userID, } if clientID.Valid { authorizationCode.Client = client } if userID.Valid { authorizationCode.User = user } return authorizationCode }
func (s *Service) createClientCommon(db *gorm.DB, clientID, secret, redirectURI string) (*models.OauthClient, error) { // Check client ID if s.ClientExists(clientID) { return nil, ErrClientIDTaken } // Hash password secretHash, err := password.HashPassword(secret) if err != nil { return nil, err } client := &models.OauthClient{ Key: strings.ToLower(clientID), Secret: string(secretHash), RedirectURI: util.StringOrNull(redirectURI), } if err := db.Create(client).Error; err != nil { return nil, err } return client, nil }
// SetPassword saves a new user to database func (s *Service) SetPassword(user *User, password string) error { // Cannot set password to empty if password == "" { return ErrCannotSetEmptyUserPassword } // Create a bcrypt hash passwordHash, err := pass.HashPassword(password) if err != nil { return err } // Set the password on the user object if err := s.db.Model(user).UpdateColumns(User{ Password: util.StringOrNull(string(passwordHash)), Model: gorm.Model{UpdatedAt: time.Now()}, }).Error; err != nil { return err } return nil }
func (suite *OauthTestSuite) TestAuthorizationCodeGrant() { // Insert a test authorization code if err := suite.db.Create(&AuthorizationCode{ Code: "test_code", ExpiresAt: time.Now().Add(+10 * time.Second), Client: suite.client, User: suite.user, RedirectURI: util.StringOrNull("https://www.example.com"), Scope: "read_write", }).Error; err != nil { log.Fatal(err) } // Prepare a request object r, err := http.NewRequest("POST", "http://1.2.3.4/something", nil) if err != nil { log.Fatal(err) } r.Form = url.Values{ "grant_type": {"authorization_code"}, "code": {"test_code"}, } var w *httptest.ResponseRecorder // First we will test an invalid redirect URI error w = httptest.NewRecorder() suite.service.authorizationCodeGrant(w, r, suite.client) // Check the status code assert.Equal(suite.T(), 400, w.Code) // Check the response body assert.Equal( suite.T(), "{\"error\":\"Invalid redirect URI\"}", strings.TrimSpace(w.Body.String()), ) // Now add the redirect URI parameter r.Form.Set("redirect_uri", "https://www.example.com") // And test a successful case w = httptest.NewRecorder() suite.service.authorizationCodeGrant(w, r, suite.client) // Check the status code assert.Equal(suite.T(), 200, w.Code) // Check the correct data was inserted accessToken := new(AccessToken) assert.False(suite.T(), suite.db.First(accessToken).RecordNotFound()) refreshToken := new(RefreshToken) assert.False(suite.T(), suite.db.First(refreshToken).RecordNotFound()) // Check the response body expected, _ := json.Marshal(map[string]interface{}{ "id": accessToken.ID, "access_token": accessToken.Token, "expires_in": 3600, "token_type": "Bearer", "scope": "read_write", "refresh_token": refreshToken.Token, }) assert.Equal(suite.T(), string(expected), strings.TrimSpace(w.Body.String())) // Check the authorization code was deleted assert.True(suite.T(), suite.db.First(new(AuthorizationCode)).RecordNotFound()) }