Skip to content

Commit c59f6f8

Browse files
committed
backend changes
1 parent be8cca1 commit c59f6f8

File tree

2 files changed

+50
-22
lines changed

2 files changed

+50
-22
lines changed

coderd/userauth.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/coder/coder/v2/coderd/cryptokeys"
2828
"github.com/coder/coder/v2/coderd/idpsync"
2929
"github.com/coder/coder/v2/coderd/jwtutils"
30+
"github.com/coder/coder/v2/coderd/telemetry"
3031
"github.com/coder/coder/v2/coderd/util/ptr"
3132

3233
"github.com/coder/coder/v2/coderd/apikey"
@@ -1046,6 +1047,10 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
10461047
defer params.CommitAuditLogs()
10471048
if err != nil {
10481049
if httpErr := idpsync.IsHTTPError(err); httpErr != nil {
1050+
// In the device flow, the error page is rendered client-side.
1051+
if api.GithubOAuth2Config.DeviceFlowEnabled && httpErr.RenderStaticPage {
1052+
httpErr.RenderStaticPage = false
1053+
}
10491054
httpErr.Write(rw, r)
10501055
return
10511056
}
@@ -1573,7 +1578,17 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
15731578
isConvertLoginType = true
15741579
}
15751580

1576-
if user.ID == uuid.Nil && !params.AllowSignups {
1581+
// nolint:gocritic // Getting user count is a system function.
1582+
userCount, err := tx.GetUserCount(dbauthz.AsSystemRestricted(ctx))
1583+
if err != nil {
1584+
return xerrors.Errorf("unable to fetch user count: %w", err)
1585+
}
1586+
1587+
// Allow the first user to sign up with OIDC, regardless of
1588+
// whether signups are enabled or not.
1589+
allowSignup := userCount == 0 || params.AllowSignups
1590+
1591+
if user.ID == uuid.Nil && !allowSignup {
15771592
signupsDisabledText := "Please contact your Coder administrator to request access."
15781593
if api.OIDCConfig != nil && api.OIDCConfig.SignupsDisabledText != "" {
15791594
signupsDisabledText = render.HTMLFromMarkdown(api.OIDCConfig.SignupsDisabledText)
@@ -1634,6 +1649,12 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
16341649
return xerrors.Errorf("unable to fetch default organization: %w", err)
16351650
}
16361651

1652+
rbacRoles := []string{}
1653+
// If this is the first user, add the owner role.
1654+
if userCount == 0 {
1655+
rbacRoles = append(rbacRoles, rbac.RoleOwner().String())
1656+
}
1657+
16371658
//nolint:gocritic
16381659
user, err = api.CreateUser(dbauthz.AsSystemRestricted(ctx), tx, CreateUserRequest{
16391660
CreateUserRequestWithOrgs: codersdk.CreateUserRequestWithOrgs{
@@ -1648,10 +1669,20 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
16481669
},
16491670
LoginType: params.LoginType,
16501671
accountCreatorName: "oauth",
1672+
RBACRoles: rbacRoles,
16511673
})
16521674
if err != nil {
16531675
return xerrors.Errorf("create user: %w", err)
16541676
}
1677+
1678+
if userCount == 0 {
1679+
telemetryUser := telemetry.ConvertUser(user)
1680+
// The email is not anonymized for the first user.
1681+
telemetryUser.Email = &user.Email
1682+
api.Telemetry.Report(&telemetry.Snapshot{
1683+
Users: []telemetry.User{telemetryUser},
1684+
})
1685+
}
16551686
}
16561687

16571688
// Activate dormant user on sign-in

coderd/users.go

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ func (api *API) firstUser(rw http.ResponseWriter, r *http.Request) {
118118
// @Success 201 {object} codersdk.CreateFirstUserResponse
119119
// @Router /users/first [post]
120120
func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
121+
// The first user can also be created via oidc, so if making changes to the flow,
122+
// ensure that the oidc flow is also updated.
121123
ctx := r.Context()
122124
var createUser codersdk.CreateFirstUserRequest
123125
if !httpapi.Read(ctx, rw, r, &createUser) {
@@ -198,6 +200,7 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
198200
OrganizationIDs: []uuid.UUID{defaultOrg.ID},
199201
},
200202
LoginType: database.LoginTypePassword,
203+
RBACRoles: []string{rbac.RoleOwner().String()},
201204
accountCreatorName: "coder",
202205
})
203206
if err != nil {
@@ -225,23 +228,6 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
225228
Users: []telemetry.User{telemetryUser},
226229
})
227230

228-
// TODO: @emyrk this currently happens outside the database tx used to create
229-
// the user. Maybe I add this ability to grant roles in the createUser api
230-
// and add some rbac bypass when calling api functions this way??
231-
// Add the admin role to this first user.
232-
//nolint:gocritic // needed to create first user
233-
_, err = api.Database.UpdateUserRoles(dbauthz.AsSystemRestricted(ctx), database.UpdateUserRolesParams{
234-
GrantedRoles: []string{rbac.RoleOwner().String()},
235-
ID: user.ID,
236-
})
237-
if err != nil {
238-
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
239-
Message: "Internal error updating user's roles.",
240-
Detail: err.Error(),
241-
})
242-
return
243-
}
244-
245231
httpapi.Write(ctx, rw, http.StatusCreated, codersdk.CreateFirstUserResponse{
246232
UserID: user.ID,
247233
OrganizationID: defaultOrg.ID,
@@ -1345,6 +1331,7 @@ type CreateUserRequest struct {
13451331
LoginType database.LoginType
13461332
SkipNotifications bool
13471333
accountCreatorName string
1334+
RBACRoles []string
13481335
}
13491336

13501337
func (api *API) CreateUser(ctx context.Context, store database.Store, req CreateUserRequest) (database.User, error) {
@@ -1354,6 +1341,13 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
13541341
return database.User{}, xerrors.Errorf("invalid username %q: %w", req.Username, usernameValid)
13551342
}
13561343

1344+
// If the caller didn't specify rbac roles, default to
1345+
// a member of the site.
1346+
rbacRoles := []string{}
1347+
if req.RBACRoles != nil {
1348+
rbacRoles = req.RBACRoles
1349+
}
1350+
13571351
var user database.User
13581352
err := store.InTx(func(tx database.Store) error {
13591353
orgRoles := make([]string, 0)
@@ -1370,10 +1364,9 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
13701364
CreatedAt: dbtime.Now(),
13711365
UpdatedAt: dbtime.Now(),
13721366
HashedPassword: []byte{},
1373-
// All new users are defaulted to members of the site.
1374-
RBACRoles: []string{},
1375-
LoginType: req.LoginType,
1376-
Status: status,
1367+
RBACRoles: rbacRoles,
1368+
LoginType: req.LoginType,
1369+
Status: status,
13771370
}
13781371
// If a user signs up with OAuth, they can have no password!
13791372
if req.Password != "" {
@@ -1431,6 +1424,10 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
14311424
}
14321425

14331426
for _, u := range userAdmins {
1427+
if u.ID == user.ID {
1428+
// If the new user is an admin, don't notify them about themselves.
1429+
continue
1430+
}
14341431
if _, err := api.NotificationsEnqueuer.EnqueueWithData(
14351432
// nolint:gocritic // Need notifier actor to enqueue notifications
14361433
dbauthz.AsNotifier(ctx),

0 commit comments

Comments
 (0)