Skip to content

Commit be13c84

Browse files
committed
backend changes
1 parent c8abf58 commit be13c84

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"
@@ -1054,6 +1055,10 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
10541055
defer params.CommitAuditLogs()
10551056
if err != nil {
10561057
if httpErr := idpsync.IsHTTPError(err); httpErr != nil {
1058+
// In the device flow, the error page is rendered client-side.
1059+
if api.GithubOAuth2Config.DeviceFlowEnabled && httpErr.RenderStaticPage {
1060+
httpErr.RenderStaticPage = false
1061+
}
10571062
httpErr.Write(rw, r)
10581063
return
10591064
}
@@ -1634,7 +1639,17 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
16341639
isConvertLoginType = true
16351640
}
16361641

1637-
if user.ID == uuid.Nil && !params.AllowSignups {
1642+
// nolint:gocritic // Getting user count is a system function.
1643+
userCount, err := tx.GetUserCount(dbauthz.AsSystemRestricted(ctx))
1644+
if err != nil {
1645+
return xerrors.Errorf("unable to fetch user count: %w", err)
1646+
}
1647+
1648+
// Allow the first user to sign up with OIDC, regardless of
1649+
// whether signups are enabled or not.
1650+
allowSignup := userCount == 0 || params.AllowSignups
1651+
1652+
if user.ID == uuid.Nil && !allowSignup {
16381653
signupsDisabledText := "Please contact your Coder administrator to request access."
16391654
if api.OIDCConfig != nil && api.OIDCConfig.SignupsDisabledText != "" {
16401655
signupsDisabledText = render.HTMLFromMarkdown(api.OIDCConfig.SignupsDisabledText)
@@ -1695,6 +1710,12 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
16951710
return xerrors.Errorf("unable to fetch default organization: %w", err)
16961711
}
16971712

1713+
rbacRoles := []string{}
1714+
// If this is the first user, add the owner role.
1715+
if userCount == 0 {
1716+
rbacRoles = append(rbacRoles, rbac.RoleOwner().String())
1717+
}
1718+
16981719
//nolint:gocritic
16991720
user, err = api.CreateUser(dbauthz.AsSystemRestricted(ctx), tx, CreateUserRequest{
17001721
CreateUserRequestWithOrgs: codersdk.CreateUserRequestWithOrgs{
@@ -1709,10 +1730,20 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
17091730
},
17101731
LoginType: params.LoginType,
17111732
accountCreatorName: "oauth",
1733+
RBACRoles: rbacRoles,
17121734
})
17131735
if err != nil {
17141736
return xerrors.Errorf("create user: %w", err)
17151737
}
1738+
1739+
if userCount == 0 {
1740+
telemetryUser := telemetry.ConvertUser(user)
1741+
// The email is not anonymized for the first user.
1742+
telemetryUser.Email = &user.Email
1743+
api.Telemetry.Report(&telemetry.Snapshot{
1744+
Users: []telemetry.User{telemetryUser},
1745+
})
1746+
}
17161747
}
17171748

17181749
// 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,
@@ -1351,6 +1337,7 @@ type CreateUserRequest struct {
13511337
LoginType database.LoginType
13521338
SkipNotifications bool
13531339
accountCreatorName string
1340+
RBACRoles []string
13541341
}
13551342

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

1350+
// If the caller didn't specify rbac roles, default to
1351+
// a member of the site.
1352+
rbacRoles := []string{}
1353+
if req.RBACRoles != nil {
1354+
rbacRoles = req.RBACRoles
1355+
}
1356+
13631357
var user database.User
13641358
err := store.InTx(func(tx database.Store) error {
13651359
orgRoles := make([]string, 0)
@@ -1376,10 +1370,9 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
13761370
CreatedAt: dbtime.Now(),
13771371
UpdatedAt: dbtime.Now(),
13781372
HashedPassword: []byte{},
1379-
// All new users are defaulted to members of the site.
1380-
RBACRoles: []string{},
1381-
LoginType: req.LoginType,
1382-
Status: status,
1373+
RBACRoles: rbacRoles,
1374+
LoginType: req.LoginType,
1375+
Status: status,
13831376
}
13841377
// If a user signs up with OAuth, they can have no password!
13851378
if req.Password != "" {
@@ -1437,6 +1430,10 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
14371430
}
14381431

14391432
for _, u := range userAdmins {
1433+
if u.ID == user.ID {
1434+
// If the new user is an admin, don't notify them about themselves.
1435+
continue
1436+
}
14401437
if _, err := api.NotificationsEnqueuer.EnqueueWithData(
14411438
// nolint:gocritic // Need notifier actor to enqueue notifications
14421439
dbauthz.AsNotifier(ctx),

0 commit comments

Comments
 (0)