func (suite *OauthTestSuite) TestSetPassword() { var ( user *oauth.User err error ) // Insert a test user without a password user = &oauth.User{ RoleID: util.StringOrNull(roles.User), Username: "******", Password: util.StringOrNull(""), } err = suite.db.Create(user).Error assert.NoError(suite.T(), err, "Inserting test data failed") // 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(), oauth.ErrPasswordTooShort, 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 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) (*User, error) { // Start with a user without a password user := &User{ 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 TestUserGetName(t *testing.T) { user := new(accounts.User) assert.Equal(t, "", user.GetName()) user.FirstName = util.StringOrNull("John") user.LastName = util.StringOrNull("Reese") assert.Equal(t, "John Reese", user.GetName()) }
func TestNewInvitationEmail(t *testing.T) { emailFactory := accounts.NewEmailFactory(&config.Config{ Web: config.WebConfig{ AppScheme: "https", AppHost: "example.com", }, AppSpecific: config.AppSpecificConfig{ CompanyName: "Example Ltd", CompanyNoreplyEmail: "*****@*****.**", ConfirmationURLFormat: "%s://%s/confirm-email/%s", InvitationURLFormat: "%s://%s/confirm-invitation/%s", PasswordResetURLFormat: "%s://%s/reset-password/%s", OnboardingCheckpointURLFormat: "%s://%s/continue-onboarding/%s", }, }) invitation := &accounts.Invitation{ EmailTokenModel: accounts.EmailTokenModel{ Reference: "some-reference", }, InvitedUser: &accounts.User{ OauthUser: &oauth.User{ Username: "******", }, FirstName: util.StringOrNull("John"), LastName: util.StringOrNull("Reese"), }, InvitedByUser: &accounts.User{ OauthUser: &oauth.User{ Username: "******", }, FirstName: util.StringOrNull("Harold"), LastName: util.StringOrNull("Finch"), }, } email, err := emailFactory.NewInvitationEmail(invitation) assert.NoError(t, err) assert.Equal(t, "You have been invited to join example.com", email.Subject) assert.Equal(t, 1, len(email.Recipients)) assert.Equal(t, "john@reese", email.Recipients[0].Address) assert.Equal(t, "John Reese", email.Recipients[0].Name) assert.Equal(t, "harold@finch", email.From.Address) assert.Equal(t, "Harold Finch", email.From.Name) expectedPlain, err := ioutil.ReadFile("./accounts/test_templates/invitation_email.txt") assert.NoError(t, err) assert.Equal(t, string(expectedPlain), email.Text) expectedHTML, err := ioutil.ReadFile("./accounts/test_templates/invitation_email.html") assert.NoError(t, err) assert.Equal(t, string(expectedHTML), email.HTML) }
// NewUser creates new User instance func NewUser(account *Account, oauthUser *oauth.User, facebookID string, confirmed bool, data *UserRequest) (*User, error) { accountID := util.PositiveIntOrNull(int64(account.ID)) oauthUserID := util.PositiveIntOrNull(int64(oauthUser.ID)) user := &User{ AccountID: accountID, OauthUserID: oauthUserID, FacebookID: util.StringOrNull(facebookID), FirstName: util.StringOrNull(data.FirstName), LastName: util.StringOrNull(data.LastName), Picture: util.StringOrNull(data.Picture), Confirmed: confirmed, } return user, nil }
func (suite *OauthTestSuite) TestAuthorizationCodeGrantInvalidRedirectURI() { // Insert a test authorization code err := suite.db.Create(&oauth.AuthorizationCode{ 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, ) }
// NewAccount creates new Account instance func NewAccount(oauthClient *oauth.Client, name, description string) (*Account, error) { oauthClientID := util.PositiveIntOrNull(int64(oauthClient.ID)) account := &Account{ OauthClientID: oauthClientID, Name: name, Description: util.StringOrNull(description), } return account, nil }
// NewAuthorizationCode creates new AuthorizationCode instance func NewAuthorizationCode(client *Client, user *User, expiresIn int, redirectURI, scope string) *AuthorizationCode { return &AuthorizationCode{ ClientID: util.PositiveIntOrNull(int64(client.ID)), UserID: util.PositiveIntOrNull(int64(user.ID)), Code: uuid.New(), ExpiresAt: time.Now().UTC().Add(time.Duration(expiresIn) * time.Second), RedirectURI: util.StringOrNull(redirectURI), Scope: scope, } }
// UpdateUser updates an existing user func (s *Service) UpdateUser(user *User, data *UserRequest) error { // Is this a request to change user password? if data.NewPassword != "" { // Verify the user submitted current password _, err := s.oauthService.AuthUser(user.OauthUser.Username, data.Password) if err != nil { return err } // Set the new password return s.oauthService.SetPassword(user.OauthUser, data.NewPassword) } // Update user metadata return s.db.Model(user).UpdateColumns(map[string]interface{}{ "first_name": util.StringOrNull(data.FirstName), "last_name": util.StringOrNull(data.LastName), "updated_at": time.Now(), }).Error }
func (suite *OauthTestSuite) TestAuthorizationCodeGrant() { // Insert a test authorization code err := suite.db.Create(&oauth.AuthorizationCode{ 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(oauth.AccessToken), new(oauth.RefreshToken) assert.False(suite.T(), oauth.AccessTokenPreload(suite.db). Last(accessToken).RecordNotFound()) assert.False(suite.T(), oauth.RefreshTokenPreload(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(oauth.AuthorizationCode)).RecordNotFound()) }
func TestGetAuthenticatedUser(t *testing.T) { var ( user *accounts.User err error ) // A test request r, err := http.NewRequest("GET", "http://1.2.3.4/something", nil) assert.NoError(t, err, "Request setup should not get an error") user, err = accounts.GetAuthenticatedUser(r) // User object should be nil assert.Nil(t, user) // Correct error should be returned if assert.NotNil(t, err) { assert.Equal(t, accounts.ErrUserAuthenticationRequired, err) } // Set a context value of an invalid type context.Set(r, accounts.AuthenticatedUserKey, "bogus") user, err = accounts.GetAuthenticatedUser(r) // User object should be nil assert.Nil(t, user) // Correct error should be returned if assert.NotNil(t, err) { assert.Equal(t, accounts.ErrUserAuthenticationRequired, err) } // Set a valid context value context.Set(r, accounts.AuthenticatedUserKey, &accounts.User{FirstName: util.StringOrNull("John Reese")}) user, err = accounts.GetAuthenticatedUser(r) // Error should be nil assert.Nil(t, err) // Correct user object should be returned if assert.NotNil(t, user) { assert.Equal(t, "John Reese", user.FirstName.String) } }
func (s *Service) setPasswordCommon(db *gorm.DB, user *User, 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(User{ Password: util.StringOrNull(string(passwordHash)), Model: gorm.Model{UpdatedAt: time.Now().UTC()}, }).Error }
func (s *Service) createClientCommon(db *gorm.DB, clientID, secret, redirectURI string) (*Client, 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 := &Client{ 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 }
func (suite *OauthTestSuite) TestAuthUser() { var ( user *oauth.User err error ) // Insert a test user without a password err = suite.db.Create(&oauth.User{ RoleID: util.StringOrNull(roles.User), Username: "******", Password: util.StringOrNull(""), }).Error assert.NoError(suite.T(), err, "Inserting test data failed") // 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(), oauth.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(), oauth.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(), oauth.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) } // Test username case insensitivity 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) } }
// GetOrCreateFacebookUser either returns an existing user // or updates an existing email user with facebook ID or creates a new user func (s *Service) GetOrCreateFacebookUser(account *Account, facebookID string, userRequest *UserRequest) (*User, error) { var ( user *User err error userExists bool ) // Does a user with this facebook ID already exist? user, err = s.FindUserByFacebookID(facebookID) // User with this facebook ID alraedy exists if err == nil { userExists = true } if userExists == false { // Does a user with this email already exist? user, err = s.FindUserByEmail(userRequest.Email) // User with this email already exists if err == nil { userExists = true } } // Begin a transaction tx := s.db.Begin() // User already exists, update the record and return if userExists { if userRequest.Email != user.OauthUser.Username { // Update the email if it changed (should not happen) err = tx.Model(user.OauthUser).UpdateColumns(oauth.User{ Username: userRequest.Email, Model: gorm.Model{UpdatedAt: time.Now()}, }).Error if err != nil { tx.Rollback() // rollback the transaction return nil, err } } // Set the facebook ID, first name, last name, picture err = tx.Model(user).UpdateColumns(User{ FacebookID: util.StringOrNull(facebookID), FirstName: util.StringOrNull(userRequest.FirstName), LastName: util.StringOrNull(userRequest.LastName), Picture: util.StringOrNull(userRequest.Picture), Confirmed: true, Model: gorm.Model{UpdatedAt: time.Now()}, }).Error if err != nil { tx.Rollback() // rollback the transaction return nil, err } // Commit the transaction if err = tx.Commit().Error; err != nil { tx.Rollback() // rollback the transaction return nil, err } return user, nil } // Facebook registration only creates regular users userRequest.Role = roles.User user, err = s.createUserCommon( tx, account, userRequest, facebookID, // facebook ID true, // confirmed ) if err != nil { tx.Rollback() // rollback the transaction return nil, err } // Commit the transaction if err = tx.Commit().Error; err != nil { tx.Rollback() // rollback the transaction return nil, err } return user, nil }