Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service accounts #89

Merged
merged 48 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
35c2c68
scaffold service accounts
thokra-nav Feb 3, 2025
38b4026
Start working on DB schema
christeredvartsen Feb 3, 2025
048ef76
Somewhat happy with the db schema
christeredvartsen Feb 4, 2025
cbc9726
Initial commit of createServiceAccount mutation
christeredvartsen Feb 4, 2025
117445f
Add a bunch of mutations and types
christeredvartsen Feb 4, 2025
97bccd8
Add activity log entries
christeredvartsen Feb 4, 2025
f8706b6
Create service account activity log
thokra-nav Feb 4, 2025
3522aba
Updat service account
thokra-nav Feb 4, 2025
23ec4c9
Delete service account
thokra-nav Feb 4, 2025
e6a02aa
Changes to the schema
christeredvartsen Feb 5, 2025
ecb2653
Start adapting code to the new roles
christeredvartsen Feb 6, 2025
24f7ad9
Move role package to auth/authz and create specific expored functions
christeredvartsen Feb 6, 2025
20f44a4
Implement missing funcs (still missing a few...)
christeredvartsen Feb 6, 2025
6bb459e
Implement some missing funcs
christeredvartsen Feb 7, 2025
230e20d
Implement missing queries and fix tests
christeredvartsen Feb 7, 2025
e2f7b00
graphql: List roles
thokra-nav Feb 7, 2025
ac10890
Cleanup roles and authorizations
thokra-nav Feb 7, 2025
13a50f1
Rework admin role
thokra-nav Feb 7, 2025
a339874
Add missing field
thokra-nav Feb 7, 2025
8c8b0e1
Regenerate files
thokra-nav Feb 7, 2025
b363eb4
Fix usersync
christeredvartsen Feb 10, 2025
539dae9
More changes to roles. Add roles to sa through graph
thokra-nav Feb 11, 2025
bf8280f
Fix generated code
thokra-nav Feb 11, 2025
410ad23
Add some tests for service account resolvers
christeredvartsen Feb 11, 2025
e93389a
Implement resolver
christeredvartsen Feb 11, 2025
5bcbc27
Make list deterministic
christeredvartsen Feb 12, 2025
5bab828
Create service account tokens
christeredvartsen Feb 12, 2025
a95050b
Implement missing resolvers
christeredvartsen Feb 12, 2025
ce0f187
Fix static service accounts
christeredvartsen Feb 12, 2025
a3c5ea7
Add update and delete sa token tests
thokra-nav Feb 12, 2025
d63cde4
Support expiry in token middleware
thokra-nav Feb 12, 2025
d89366c
Add tokens to service account type
thokra-nav Feb 12, 2025
ef47da6
Remove unused funcs
christeredvartsen Feb 13, 2025
7be92ef
Check permissions of roles assigned to service accounts
thokra-nav Feb 13, 2025
1195924
Allow service accounts to do actions given permission
thokra-nav Feb 13, 2025
b466b29
Delete more during migration
thokra-nav Feb 13, 2025
69941c9
Add some docs and rename some fields
christeredvartsen Feb 13, 2025
2f18176
Adjust field names
christeredvartsen Feb 13, 2025
7aec1d8
Add some TODOs for activity log creation
christeredvartsen Feb 13, 2025
c5d7f50
Reintroduce names for service account tokens
christeredvartsen Feb 14, 2025
09a226d
Remove user roles before running tests
christeredvartsen Feb 14, 2025
97dc40e
Add lastUsedAt to service accounts and token
christeredvartsen Feb 14, 2025
e654202
Encrypt sa token in database
thokra-nav Feb 14, 2025
57aac0a
remove option to change expires at for sa token
thokra-nav Feb 14, 2025
a95fa63
Fix broken tests
christeredvartsen Feb 14, 2025
b0b5dbc
Add length constraint on service account and token names
christeredvartsen Feb 14, 2025
9955a5c
Create audit events
christeredvartsen Feb 14, 2025
a746d5a
Add some simple descriptions for authorizations and roles
thokra-nav Feb 14, 2025
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
1 change: 1 addition & 0 deletions .configs/gqlgen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ omit_root_models: true
autobind:
- "github.com/99designs/gqlgen/graphql/introspection" # Without this line in the beginning, a `prelude.resolver.go` is generated
- "github.com/nais/api/internal/activitylog"
- "github.com/nais/api/internal/auth/authz"
- "github.com/nais/api/internal/cost"
- "github.com/nais/api/internal/deployment"
- "github.com/nais/api/internal/feature"
Expand Down
8 changes: 4 additions & 4 deletions .configs/sqlc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ sql:
out: "../internal/team/teamsql"

- <<: *default_domain
name: "Roles SQL"
queries: "../internal/role/queries"
name: "Authz SQL"
queries: "../internal/auth/authz/queries"
gen:
go:
<<: *default_go
package: "rolesql"
out: "../internal/role/rolesql"
package: "authzsql"
out: "../internal/auth/authz/authzsql"

- <<: *default_domain
name: "Activity log SQL"
Expand Down
72 changes: 41 additions & 31 deletions cmd/setup_local/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import (
"github.com/nais/api/internal/graph/model"
"github.com/nais/api/internal/graph/pagination"
"github.com/nais/api/internal/logger"
"github.com/nais/api/internal/role"
"github.com/nais/api/internal/role/rolesql"
"github.com/nais/api/internal/slug"
"github.com/nais/api/internal/team"
"github.com/nais/api/internal/user"
"github.com/nais/api/internal/usersync/usersyncer"
"github.com/nais/api/internal/usersync/usersyncsql"
"github.com/sethvargo/go-envconfig"
"github.com/sirupsen/logrus"
"golang.org/x/text/runes"
Expand Down Expand Up @@ -154,7 +154,7 @@ func run(ctx context.Context, cfg *seedConfig, log logrus.FieldLogger) error {
ctx = activitylog.NewLoaderContext(ctx, pool)
ctx = user.NewLoaderContext(ctx, pool)
ctx = team.NewLoaderContext(ctx, pool, nil)
ctx = role.NewLoaderContext(ctx, pool)
ctx = authz.NewLoaderContext(ctx, pool)
ctx = environment.NewLoaderContext(ctx, pool)

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

defaultUserRoles := []rolesql.RoleName{
rolesql.RoleNameTeamcreator,
rolesql.RoleNameTeamviewer,
rolesql.RoleNameUserviewer,
rolesql.RoleNameServiceaccountcreator,
}

var err error
var adminUser, devUser *user.User

usersyncq := usersyncsql.New(database.TransactionFromContext(ctx))

createUser := func(ctx context.Context, name, email string) (*user.User, error) {
usu, err := usersyncq.Create(ctx, usersyncsql.CreateParams{
Name: name,
Email: email,
ExternalID: uuid.New().String(),
})
if err != nil {
return nil, fmt.Errorf("create user: %w", err)
}

usr, err := user.GetByEmail(ctx, usu.Email)
if err != nil {
return nil, fmt.Errorf("get user: %w", err)
}

return usr, nil
}

adminUser, err = user.GetByEmail(ctx, nameToEmail(adminName, cfg.Domain))
if err != nil {
adminUser, err = user.Create(ctx, adminName, nameToEmail(adminName, cfg.Domain), uuid.New().String())
adminUser, err = createUser(ctx, adminName, nameToEmail(adminName, cfg.Domain))
if err != nil {
return fmt.Errorf("create admin user: %w", err)
}
}
if err := role.AssignGlobalRoleToUser(ctx, adminUser.UUID, rolesql.RoleNameAdmin); err != nil {

if err := usersyncq.AssignGlobalAdmin(ctx, adminUser.UUID); err != nil {
return fmt.Errorf("assign global admin role to admin user: %w", err)
}
actor := &authz.Actor{User: adminUser}

devUser, err = user.GetByEmail(ctx, nameToEmail(devName, cfg.Domain))
if err != nil {
devUser, err = user.Create(ctx, devName, nameToEmail(devName, cfg.Domain), uuid.New().String())
devUser, err = createUser(ctx, devName, nameToEmail(devName, cfg.Domain))
if err != nil {
return fmt.Errorf("create dev user: %w", err)
}
}
for _, roleName := range defaultUserRoles {
if err := role.AssignGlobalRoleToUser(ctx, devUser.UUID, roleName); err != nil {
return fmt.Errorf("assign globla role %q to dev user: %w", roleName, err)
}

if err := usersyncer.AssignDefaultPermissionsToUser(ctx, usersyncq, devUser.UUID); err != nil {
return fmt.Errorf("assign default permissions to dev user: %w", err)
}

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

u, err := user.Create(ctx, name, email, uuid.New().String())
u, err := createUser(ctx, name, email)
if err != nil {
return fmt.Errorf("create user %q: %w", email, err)
}

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

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

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

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

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

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

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

Expand Down
2 changes: 1 addition & 1 deletion integration_tests/delete_application.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Helper.SQLExec([[
INSERT INTO
user_roles (role_name, user_id, target_team_slug)
VALUES (
'Team member'::role_name,
'Team member',
(SELECT id FROM users WHERE email = '[email protected]'),
'slug-1'
)
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/delete_job.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Helper.SQLExec([[
INSERT INTO
user_roles (role_name, user_id, target_team_slug)
VALUES (
'Team member'::role_name,
'Team member',
(SELECT id FROM users WHERE email = '[email protected]'),
'slug-1'
)
Expand Down
52 changes: 52 additions & 0 deletions integration_tests/manage_team_members.lua
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,55 @@ Test.gql("Add owner", function(t)
},
}
end)

Test.gql("Remove owner", function(t)
t.query(string.format([[
mutation {
removeTeamMember(
input: {
teamSlug: "%s"
userEmail: "%s"
}
) {
team {
members {
nodes {
role
user {
email
name
}
}
}
}
}
}
]], TeamSlug, OwnerToAdd))

t.check {
data = {
removeTeamMember = {
team = {
members = {
nodes = {
{
role = "OWNER",
user = {
email = "[email protected]",
name = "Authenticated User",
},
},
{
role = "OWNER",
user = {
email = "[email protected]",
name = "name-1",
},
},
},
},
},
},
},
}
end)
10 changes: 5 additions & 5 deletions integration_tests/reconcilers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ Test.gql("list reconcilers with config as non-admin", function(t)
data = Null,
errors = {
{
message = Contains("you need the \"global:admin\""),
message = "You are authenticated, but your account is not authorized to perform this action.",
path = { "reconcilers", "nodes", Ignore(), "config" },
},
{
message = Contains("you need the \"global:admin\""),
message = "You are authenticated, but your account is not authorized to perform this action.",
path = { "reconcilers", "nodes", Ignore(), "config" },
},
},
Expand All @@ -100,7 +100,7 @@ Test.gql("enable reconciler as non-admin", function(t)
data = Null,
errors = {
{
message = Contains("you need the \"global:admin\""),
message = "You are authenticated, but your account is not authorized to perform this action.",
path = { "enableReconciler" },
},
},
Expand All @@ -120,15 +120,15 @@ Test.gql("disable reconciler as non-admin", function(t)
data = Null,
errors = {
{
message = Contains("you need the \"global:admin\""),
message = "You are authenticated, but your account is not authorized to perform this action.",
path = { "disableReconciler" },
},
},
}
end)

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

Test.gql("enable non-configured reconciler as admin", function(t)
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/restart_application.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Helper.SQLExec([[
INSERT INTO
user_roles (role_name, user_id, target_team_slug)
VALUES (
'Team member'::role_name,
'Team member',
(SELECT id FROM users WHERE email = '[email protected]'),
'slug-1'
)
Expand Down
4 changes: 1 addition & 3 deletions integration_tests/seeds/02_teams.sql
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ FROM
INSERT INTO
user_roles (role_name, user_id, target_team_slug)
SELECT
(
ARRAY['Team owner'::role_name, 'Team member'::role_name]
) [ROUND(RANDOM()) + 1],
(ARRAY['Team owner', 'Team member']) [ROUND(RANDOM()) + 1],
u.id,
t.slug
FROM
Expand Down
Loading