Skip to content

Commit 1053e89

Browse files
thokra-navchristeredvartsenx10an14-nav
authored
Service accounts (#89)
- Introduce service accounts in graphql api - Rework roles to be a bit more dynamic Co-authored-by: Christer Edvartsen <[email protected]> Co-authored-by: @x10an14-nav <[email protected]>
1 parent 10a1442 commit 1053e89

File tree

99 files changed

+52069
-34852
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+52069
-34852
lines changed

.configs/gqlgen.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ omit_root_models: true
2727
autobind:
2828
- "github.com/99designs/gqlgen/graphql/introspection" # Without this line in the beginning, a `prelude.resolver.go` is generated
2929
- "github.com/nais/api/internal/activitylog"
30+
- "github.com/nais/api/internal/auth/authz"
3031
- "github.com/nais/api/internal/cost"
3132
- "github.com/nais/api/internal/deployment"
3233
- "github.com/nais/api/internal/feature"

.configs/sqlc.yaml

+4-4
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,13 @@ sql:
6868
out: "../internal/team/teamsql"
6969

7070
- <<: *default_domain
71-
name: "Roles SQL"
72-
queries: "../internal/role/queries"
71+
name: "Authz SQL"
72+
queries: "../internal/auth/authz/queries"
7373
gen:
7474
go:
7575
<<: *default_go
76-
package: "rolesql"
77-
out: "../internal/role/rolesql"
76+
package: "authzsql"
77+
out: "../internal/auth/authz/authzsql"
7878

7979
- <<: *default_domain
8080
name: "Activity log SQL"

cmd/setup_local/main.go

+41-31
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ import (
2020
"github.com/nais/api/internal/graph/model"
2121
"github.com/nais/api/internal/graph/pagination"
2222
"github.com/nais/api/internal/logger"
23-
"github.com/nais/api/internal/role"
24-
"github.com/nais/api/internal/role/rolesql"
2523
"github.com/nais/api/internal/slug"
2624
"github.com/nais/api/internal/team"
2725
"github.com/nais/api/internal/user"
26+
"github.com/nais/api/internal/usersync/usersyncer"
27+
"github.com/nais/api/internal/usersync/usersyncsql"
2828
"github.com/sethvargo/go-envconfig"
2929
"github.com/sirupsen/logrus"
3030
"golang.org/x/text/runes"
@@ -154,7 +154,7 @@ func run(ctx context.Context, cfg *seedConfig, log logrus.FieldLogger) error {
154154
ctx = activitylog.NewLoaderContext(ctx, pool)
155155
ctx = user.NewLoaderContext(ctx, pool)
156156
ctx = team.NewLoaderContext(ctx, pool, nil)
157-
ctx = role.NewLoaderContext(ctx, pool)
157+
ctx = authz.NewLoaderContext(ctx, pool)
158158
ctx = environment.NewLoaderContext(ctx, pool)
159159

160160
emails := map[string]struct{}{}
@@ -218,39 +218,52 @@ func run(ctx context.Context, cfg *seedConfig, log logrus.FieldLogger) error {
218218
return fmt.Errorf("sync environments: %w", err)
219219
}
220220

221-
defaultUserRoles := []rolesql.RoleName{
222-
rolesql.RoleNameTeamcreator,
223-
rolesql.RoleNameTeamviewer,
224-
rolesql.RoleNameUserviewer,
225-
rolesql.RoleNameServiceaccountcreator,
226-
}
227-
228221
var err error
229222
var adminUser, devUser *user.User
230223

224+
usersyncq := usersyncsql.New(database.TransactionFromContext(ctx))
225+
226+
createUser := func(ctx context.Context, name, email string) (*user.User, error) {
227+
usu, err := usersyncq.Create(ctx, usersyncsql.CreateParams{
228+
Name: name,
229+
Email: email,
230+
ExternalID: uuid.New().String(),
231+
})
232+
if err != nil {
233+
return nil, fmt.Errorf("create user: %w", err)
234+
}
235+
236+
usr, err := user.GetByEmail(ctx, usu.Email)
237+
if err != nil {
238+
return nil, fmt.Errorf("get user: %w", err)
239+
}
240+
241+
return usr, nil
242+
}
243+
231244
adminUser, err = user.GetByEmail(ctx, nameToEmail(adminName, cfg.Domain))
232245
if err != nil {
233-
adminUser, err = user.Create(ctx, adminName, nameToEmail(adminName, cfg.Domain), uuid.New().String())
246+
adminUser, err = createUser(ctx, adminName, nameToEmail(adminName, cfg.Domain))
234247
if err != nil {
235248
return fmt.Errorf("create admin user: %w", err)
236249
}
237250
}
238-
if err := role.AssignGlobalRoleToUser(ctx, adminUser.UUID, rolesql.RoleNameAdmin); err != nil {
251+
252+
if err := usersyncq.AssignGlobalAdmin(ctx, adminUser.UUID); err != nil {
239253
return fmt.Errorf("assign global admin role to admin user: %w", err)
240254
}
241255
actor := &authz.Actor{User: adminUser}
242256

243257
devUser, err = user.GetByEmail(ctx, nameToEmail(devName, cfg.Domain))
244258
if err != nil {
245-
devUser, err = user.Create(ctx, devName, nameToEmail(devName, cfg.Domain), uuid.New().String())
259+
devUser, err = createUser(ctx, devName, nameToEmail(devName, cfg.Domain))
246260
if err != nil {
247261
return fmt.Errorf("create dev user: %w", err)
248262
}
249263
}
250-
for _, roleName := range defaultUserRoles {
251-
if err := role.AssignGlobalRoleToUser(ctx, devUser.UUID, roleName); err != nil {
252-
return fmt.Errorf("assign globla role %q to dev user: %w", roleName, err)
253-
}
264+
265+
if err := usersyncer.AssignDefaultPermissionsToUser(ctx, usersyncq, devUser.UUID); err != nil {
266+
return fmt.Errorf("assign default permissions to dev user: %w", err)
254267
}
255268

256269
users := []*user.User{devUser}
@@ -263,22 +276,19 @@ func run(ctx context.Context, cfg *seedConfig, log logrus.FieldLogger) error {
263276
continue
264277
}
265278

266-
u, err := user.Create(ctx, name, email, uuid.New().String())
279+
u, err := createUser(ctx, name, email)
267280
if err != nil {
268281
return fmt.Errorf("create user %q: %w", email, err)
269282
}
270283

271-
for _, roleName := range defaultUserRoles {
272-
if err = role.AssignGlobalRoleToUser(ctx, u.UUID, roleName); err != nil {
273-
return fmt.Errorf("assign global role %q to user %q: %w", roleName, u.Email, err)
274-
}
284+
if err = usersyncer.AssignDefaultPermissionsToUser(ctx, usersyncq, u.UUID); err != nil {
285+
return fmt.Errorf("assign default permissions to user %q: %w", u.Email, err)
275286
}
276287

277288
log.Infof("%d/%d users created", i, *cfg.NumUsers)
278289
users = append(users, u)
279290
emails[email] = struct{}{}
280291
}
281-
usersCreated := len(users)
282292

283293
var devteam *team.Team
284294
devteam, err = team.Get(ctx, "devteam")
@@ -304,8 +314,8 @@ func run(ctx context.Context, cfg *seedConfig, log logrus.FieldLogger) error {
304314
return fmt.Errorf("update external references for devteam: %w", err)
305315
}
306316

307-
if err := role.AssignTeamRoleToUser(ctx, devUser.UUID, devteam.Slug, rolesql.RoleNameTeamowner); err != nil {
308-
return fmt.Errorf("assign team owner role to dev user: %w", err)
317+
if err := authz.MakeUserTeamOwner(ctx, devUser.UUID, devteam.Slug); err != nil {
318+
return fmt.Errorf("make user %q owner of team %q: %w", devUser.Email, devteam.Slug, err)
309319
}
310320

311321
input := &team.UpdateTeamEnvironmentInput{
@@ -355,16 +365,16 @@ func run(ctx context.Context, cfg *seedConfig, log logrus.FieldLogger) error {
355365
}
356366

357367
for o := 0; o < *cfg.NumOwnersPerTeam; o++ {
358-
u := users[rand.IntN(usersCreated)]
359-
if err = role.AssignTeamRoleToUser(ctx, u.UUID, t.Slug, rolesql.RoleNameTeamowner); err != nil {
360-
return fmt.Errorf("assign team owner role to user %q in team %q: %w", u.Email, t.Slug, err)
368+
u := users[rand.IntN(len(users))]
369+
if err = authz.MakeUserTeamOwner(ctx, u.UUID, t.Slug); err != nil {
370+
return fmt.Errorf("make user %q owner of team %q: %w", u.Email, t.Slug, err)
361371
}
362372
}
363373

364374
for o := 0; o < *cfg.NumMembersPerTeam; o++ {
365-
u := users[rand.IntN(usersCreated)]
366-
if err = role.AssignTeamRoleToUser(ctx, u.UUID, t.Slug, rolesql.RoleNameTeammember); err != nil {
367-
return fmt.Errorf("assign team member role to user %q in team %q: %w", u.Email, t.Slug, err)
375+
u := users[rand.IntN(len(users))]
376+
if err = authz.MakeUserTeamMember(ctx, u.UUID, t.Slug); err != nil {
377+
return fmt.Errorf("make user %q member of team %q: %w", u.Email, t.Slug, err)
368378
}
369379
}
370380

integration_tests/delete_application.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Helper.SQLExec([[
55
INSERT INTO
66
user_roles (role_name, user_id, target_team_slug)
77
VALUES (
8-
'Team member'::role_name,
8+
'Team member',
99
(SELECT id FROM users WHERE email = '[email protected]'),
1010
'slug-1'
1111
)

integration_tests/delete_job.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Helper.SQLExec([[
55
INSERT INTO
66
user_roles (role_name, user_id, target_team_slug)
77
VALUES (
8-
'Team member'::role_name,
8+
'Team member',
99
(SELECT id FROM users WHERE email = '[email protected]'),
1010
'slug-1'
1111
)

integration_tests/manage_team_members.lua

+52
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,55 @@ Test.gql("Add owner", function(t)
241241
},
242242
}
243243
end)
244+
245+
Test.gql("Remove owner", function(t)
246+
t.query(string.format([[
247+
mutation {
248+
removeTeamMember(
249+
input: {
250+
teamSlug: "%s"
251+
userEmail: "%s"
252+
}
253+
) {
254+
team {
255+
members {
256+
nodes {
257+
role
258+
user {
259+
email
260+
name
261+
}
262+
}
263+
}
264+
}
265+
}
266+
}
267+
]], TeamSlug, OwnerToAdd))
268+
269+
t.check {
270+
data = {
271+
removeTeamMember = {
272+
team = {
273+
members = {
274+
nodes = {
275+
{
276+
role = "OWNER",
277+
user = {
278+
email = "[email protected]",
279+
name = "Authenticated User",
280+
},
281+
},
282+
{
283+
role = "OWNER",
284+
user = {
285+
email = "[email protected]",
286+
name = "name-1",
287+
},
288+
},
289+
},
290+
},
291+
},
292+
},
293+
},
294+
}
295+
end)

integration_tests/reconcilers.lua

+5-5
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ Test.gql("list reconcilers with config as non-admin", function(t)
7676
data = Null,
7777
errors = {
7878
{
79-
message = Contains("you need the \"global:admin\""),
79+
message = "You are authenticated, but your account is not authorized to perform this action.",
8080
path = { "reconcilers", "nodes", Ignore(), "config" },
8181
},
8282
{
83-
message = Contains("you need the \"global:admin\""),
83+
message = "You are authenticated, but your account is not authorized to perform this action.",
8484
path = { "reconcilers", "nodes", Ignore(), "config" },
8585
},
8686
},
@@ -100,7 +100,7 @@ Test.gql("enable reconciler as non-admin", function(t)
100100
data = Null,
101101
errors = {
102102
{
103-
message = Contains("you need the \"global:admin\""),
103+
message = "You are authenticated, but your account is not authorized to perform this action.",
104104
path = { "enableReconciler" },
105105
},
106106
},
@@ -120,15 +120,15 @@ Test.gql("disable reconciler as non-admin", function(t)
120120
data = Null,
121121
errors = {
122122
{
123-
message = Contains("you need the \"global:admin\""),
123+
message = "You are authenticated, but your account is not authorized to perform this action.",
124124
path = { "disableReconciler" },
125125
},
126126
},
127127
}
128128
end)
129129

130130
-- Make the authenticated user an admin
131-
Helper.SQLExec("INSERT INTO user_roles (role_name, user_id) VALUES ('Admin', (SELECT id FROM users WHERE email = $1))",
131+
Helper.SQLExec("UPDATE users SET admin = true WHERE email = $1",
132132
133133

134134
Test.gql("enable non-configured reconciler as admin", function(t)

integration_tests/restart_application.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Helper.SQLExec([[
55
INSERT INTO
66
user_roles (role_name, user_id, target_team_slug)
77
VALUES (
8-
'Team member'::role_name,
8+
'Team member',
99
(SELECT id FROM users WHERE email = '[email protected]'),
1010
'slug-1'
1111
)

integration_tests/seeds/02_teams.sql

+1-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ FROM
1313
INSERT INTO
1414
user_roles (role_name, user_id, target_team_slug)
1515
SELECT
16-
(
17-
ARRAY['Team owner'::role_name, 'Team member'::role_name]
18-
) [ROUND(RANDOM()) + 1],
16+
(ARRAY['Team owner', 'Team member']) [ROUND(RANDOM()) + 1],
1917
u.id,
2018
t.slug
2119
FROM

0 commit comments

Comments
 (0)