Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions api/asymmetric_login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,121 @@ func (ts *SignupTestSuite) TestWrongKeyFormatGetChallengeToken() {
require.Equal(ts.T(), []byte(`{"code":422,"msg":"Key verification failed: Provided key cannot be ETH address"}`), msg)
}

func (ts *SignupTestSuite) TestFirstSignInSuperAdmin() {
//FirstUser is SuperAdmin config on true
ts.Config.FirstUserSuperAdmin = true

// Request body
var buffer bytes.Buffer
require.NoError(ts.T(), json.NewEncoder(&buffer).Encode(map[string]interface{}{
"key": "0x6BE46d7D863666546b77951D5dfffcF075F36E68",
"algorithm": "ETH",
}))

// Setup request
req := httptest.NewRequest(http.MethodPost, "/sign_challenge", &buffer)
req.Header.Set("Content-Type", "application/json")

// Setup response recorder
w := httptest.NewRecorder()

ts.API.handler.ServeHTTP(w, req)

require.Equal(ts.T(), http.StatusOK, w.Code)

jsonData := GetChallengeTokenResponse{}
require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&jsonData))
require.NotEmpty(ts.T(), jsonData)

user, key, err := models.FindUserWithAsymmetrickey(ts.API.db, "0x6BE46d7D863666546b77951D5dfffcF075F36E68")
require.NoError(ts.T(), err)
require.NotEmpty(ts.T(), user)
require.NotEmpty(ts.T(), key)
assert.Equal(ts.T(), key.ChallengeToken.String(), jsonData.ChallengeToken)
assert.Equal(ts.T(), user.Role, "superadmin")
assert.Equal(ts.T(), user.IsSuperAdmin, true)
}

func (ts *SignupTestSuite) TestFirstSignInConfigFalse() {
//FirstUser is SuperAdmin config on false
ts.Config.FirstUserSuperAdmin = false

// Request body
var buffer bytes.Buffer
require.NoError(ts.T(), json.NewEncoder(&buffer).Encode(map[string]interface{}{
"key": "0x6BE46d7D863666546b77951D5dfffcF075F36E68",
"algorithm": "ETH",
}))

// Setup request
req := httptest.NewRequest(http.MethodPost, "/sign_challenge", &buffer)
req.Header.Set("Content-Type", "application/json")

// Setup response recorder
w := httptest.NewRecorder()

ts.API.handler.ServeHTTP(w, req)

require.Equal(ts.T(), http.StatusOK, w.Code)

jsonData := GetChallengeTokenResponse{}
require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&jsonData))
require.NotEmpty(ts.T(), jsonData)

user, key, err := models.FindUserWithAsymmetrickey(ts.API.db, "0x6BE46d7D863666546b77951D5dfffcF075F36E68")
require.NoError(ts.T(), err)
require.NotEmpty(ts.T(), user)
require.NotEmpty(ts.T(), key)
assert.Equal(ts.T(), key.ChallengeToken.String(), jsonData.ChallengeToken)
assert.Equal(ts.T(), user.Role, "authenticated")
assert.Equal(ts.T(), user.IsSuperAdmin, false)
}

func (ts *SignupTestSuite) TestNotFirstSignIn() {
Comment thread
eliot-blankenberg marked this conversation as resolved.
//FirstUser is SuperAdmin config on true
ts.Config.FirstUserSuperAdmin = true

// Request body
var buffer bytes.Buffer
require.NoError(ts.T(), json.NewEncoder(&buffer).Encode(map[string]interface{}{
"key": "0x6BE46d7D863666546b77951D5dfffcF075F36E68",
"algorithm": "ETH",
}))

var buffer2 bytes.Buffer
require.NoError(ts.T(), json.NewEncoder(&buffer2).Encode(map[string]interface{}{
"key": "0x6BE46d7D863666546677951D5dfffcF075F36E68",
"algorithm": "ETH",
}))

// Setup request
req := httptest.NewRequest(http.MethodPost, "/sign_challenge", &buffer)
req.Header.Set("Content-Type", "application/json")

// Setup response recorder
w := httptest.NewRecorder()

ts.API.handler.ServeHTTP(w, req)

req = httptest.NewRequest(http.MethodPost, "/sign_challenge", &buffer2)
req.Header.Set("Content-Type", "application/json")

ts.API.handler.ServeHTTP(w, req)

require.Equal(ts.T(), http.StatusOK, w.Code)

jsonData := GetChallengeTokenResponse{}
require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&jsonData))
require.NotEmpty(ts.T(), jsonData)

user, key, err := models.FindUserWithAsymmetrickey(ts.API.db, "0x6BE46d7D863666546677951D5dfffcF075F36E68")
require.NoError(ts.T(), err)
require.NotEmpty(ts.T(), user)
require.NotEmpty(ts.T(), key)
assert.Equal(ts.T(), user.Role, "authenticated")
assert.Equal(ts.T(), user.IsSuperAdmin, false)
}

type AsymmetricSignInTestSuite struct {
suite.Suite
API *API
Expand Down
1 change: 1 addition & 0 deletions api/asymmetric_signin.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func (a *API) GetChallengeToken(w http.ResponseWriter, r *http.Request) error {
Provider: "AsymmetricKey",
Aud: aud,
})

if terr != nil {
return terr
}
Expand Down
19 changes: 17 additions & 2 deletions api/signup.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,12 +301,27 @@ func (a *API) signupNewUser(ctx context.Context, conn *storage.Connection, param

err = conn.Transaction(func(tx *storage.Connection) error {
var terr error
userExist, terr := models.AnyUser(tx)

if terr != nil {
return terr
}

if terr = tx.Create(user); terr != nil {
return internalServerError("Database error saving new user").WithInternalError(terr)
}
if terr = user.SetRole(tx, config.JWT.DefaultGroupName); terr != nil {
return internalServerError("Database error updating user").WithInternalError(terr)

if config.FirstUserSuperAdmin && !userExist {
terr = user.SetSuperAdmin(tx)
if terr != nil {
return terr
}
} else {
if terr = user.SetRole(tx, config.JWT.DefaultGroupName); terr != nil {
return internalServerError("Database error updating user").WithInternalError(terr)
}
}

if terr = triggerEventHooks(ctx, tx, ValidateEvent, user, instanceID, config); terr != nil {
return terr
}
Expand Down
2 changes: 1 addition & 1 deletion api/signup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (ts *SignupTestSuite) TestWebhookTriggered() {
assert.Len(u, 10)
// assert.Equal(t, user.ID, u["id"]) TODO
assert.Equal("authenticated", u["aud"])
assert.Equal("authenticated", u["role"])
assert.Equal("superadmin", u["role"])
assert.Equal("test@example.com", u["email"])

appmeta, ok := u["app_metadata"].(map[string]interface{})
Expand Down
25 changes: 13 additions & 12 deletions conf/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,18 +181,19 @@ type SecurityConfiguration struct {

// Configuration holds all the per-instance configuration.
type Configuration struct {
SiteURL string `json:"site_url" split_words:"true" required:"true"`
URIAllowList []string `json:"uri_allow_list" split_words:"true"`
PasswordMinLength int `json:"password_min_length" split_words:"true"`
JWT JWTConfiguration `json:"jwt"`
SMTP SMTPConfiguration `json:"smtp"`
Mailer MailerConfiguration `json:"mailer"`
External ProviderConfiguration `json:"external"`
Sms SmsProviderConfiguration `json:"sms"`
DisableSignup bool `json:"disable_signup" split_words:"true"`
Webhook WebhookConfig `json:"webhook" split_words:"true"`
Security SecurityConfiguration `json:"security"`
Cookie struct {
SiteURL string `json:"site_url" split_words:"true" required:"true"`
URIAllowList []string `json:"uri_allow_list" split_words:"true"`
PasswordMinLength int `json:"password_min_length" split_words:"true"`
JWT JWTConfiguration `json:"jwt"`
SMTP SMTPConfiguration `json:"smtp"`
Mailer MailerConfiguration `json:"mailer"`
External ProviderConfiguration `json:"external"`
Sms SmsProviderConfiguration `json:"sms"`
DisableSignup bool `json:"disable_signup" split_words:"true"`
FirstUserSuperAdmin bool `json:"first_user_super_admin" split_words:"true"`
Webhook WebhookConfig `json:"webhook" split_words:"true"`
Security SecurityConfiguration `json:"security"`
Cookie struct {
Key string `json:"key"`
Domain string `json:"domain"`
Duration int `json:"duration"`
Expand Down
1 change: 1 addition & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ GOTRUE_SITE_URL="http://localhost:3000"
GOTRUE_EXTERNAL_EMAIL_ENABLED="true"
GOTRUE_EXTERNAL_PHONE_ENABLED="true"
GOTRUE_EXTERNAL_IOS_BUNDLE_ID="com.supabase.gotrue"
GOTRUE_FIRST_USER_SUPER_ADMIN="true"

# Whitelist redirect to URLs here
GOTRUE_URI_ALLOW_LIST=["http://localhost:3000"]
Expand Down
22 changes: 22 additions & 0 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ func (u *User) SetRole(tx *storage.Connection, roleName string) error {
return tx.UpdateOnly(u, "role")
}

//SetSuperAdmin sets the user as SuperAdmin
func (u *User) SetSuperAdmin(tx *storage.Connection) error {
u.IsSuperAdmin = true
err := u.SetRole(tx, "superadmin")
if err != nil {
return err
}
return tx.UpdateOnly(u, "is_super_admin")
}

// HasRole returns true when the users role is set to roleName
func (u *User) HasRole(roleName string) bool {
return u.Role == roleName
Expand Down Expand Up @@ -347,6 +357,18 @@ func findUser(tx *storage.Connection, query string, args ...interface{}) (*User,
return obj, nil
}

func AnyUser(tx *storage.Connection) (bool, error) {
Comment thread
dpatsora marked this conversation as resolved.
obj := &User{}
err := tx.Eager().Q().First(obj)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return false, nil
}
return false, err
}
return true, nil
}

// FindUserByConfirmationToken finds users with the matching confirmation token.
func FindUserByConfirmationToken(tx *storage.Connection, token string) (*User, error) {
user, err := findUser(tx, "confirmation_token = ?", token)
Expand Down