Пример #1
0
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"))
}
Пример #2
0
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)
}
Пример #3
0
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
}
Пример #4
0
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())
}
Пример #5
0
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)
}
Пример #6
0
// 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,
	)
}
Пример #8
0
// 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
}
Пример #9
0
// 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,
	}
}
Пример #10
0
// 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())
}
Пример #12
0
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)
	}
}
Пример #13
0
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
}
Пример #14
0
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
}
Пример #15
0
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)
	}
}
Пример #16
0
// 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
}