Skip to content

Commit

Permalink
feat(api,ui): organization for user (#5996)
Browse files Browse the repository at this point in the history
  • Loading branch information
richardlt authored Nov 23, 2021
1 parent 94a9638 commit bbecfff
Show file tree
Hide file tree
Showing 102 changed files with 2,500 additions and 816 deletions.
1 change: 1 addition & 0 deletions cli/cdsctl/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func adminCommands() []*cobra.Command {
adminCurl(),
adminFeatures(),
adminWorkflows(),
adminUsers(),
}
}

Expand Down
58 changes: 58 additions & 0 deletions cli/cdsctl/admin_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"context"
"fmt"

"github.com/spf13/cobra"

"github.com/ovh/cds/cli"
)

var adminUsersCmd = cli.Command{
Name: "users",
Aliases: []string{"user"},
Short: "Manage CDS users",
}

func adminUsers() *cobra.Command {
return cli.NewCommand(adminUsersCmd, nil, []*cobra.Command{
cli.NewCommand(adminUserSetOrganizationCmd, adminUserSetOrganizationRun, nil),
})
}

var adminUserSetOrganizationCmd = cli.Command{
Name: "set-organization",
Short: "Set organization for given user",
Args: []cli.Arg{
{
Name: "username",
},
{
Name: "organization",
},
},
}

func adminUserSetOrganizationRun(v cli.Values) error {
ctx := context.Background()
username := v.GetString("username")
organization := v.GetString("organization")

u, err := client.UserGet(ctx, username)
if err != nil {
return err
}
if u.Organization != "" {
return cli.NewError("user organization already set to %q", u.Organization)
}

u.Organization = organization

if err := client.UserUpdate(ctx, u.Username, u); err != nil {
return err
}

fmt.Printf("User organization set to %q\n", u.Organization)
return nil
}
52 changes: 52 additions & 0 deletions cli/cdsctl/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (

"github.com/ovh/cds/cli"
"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/cdsclient"
"github.com/ovh/cds/sdk/exportentities"
)

var groupCmd = cli.Command{
Expand All @@ -25,6 +27,8 @@ func group() *cobra.Command {
cli.NewDeleteCommand(groupDeleteCmd, groupDeleteRun, nil, withAllCommandModifiers()...),
cli.NewCommand(groupGrantCmd, groupGrantRun, nil, withAllCommandModifiers()...),
cli.NewCommand(groupRevokeCmd, groupRevokeRun, nil, withAllCommandModifiers()...),
cli.NewCommand(groupExportCmd, groupExportRun, nil, withAllCommandModifiers()...),
cli.NewCommand(groupImportCmd, groupImportRun, nil, withAllCommandModifiers()...),
groupMember(),
})
}
Expand Down Expand Up @@ -202,3 +206,51 @@ func groupRevokeRun(v cli.Values) error {

return nil
}

var groupExportCmd = cli.Command{
Name: "export",
Short: "Export a CDS group",
Args: []cli.Arg{
{
Name: "group-name",
},
},
Flags: []cli.Flag{
{
Name: "format",
Usage: "Specify export format (json or yaml)",
Default: "yaml",
},
},
}

func groupExportRun(v cli.Values) error {
buf, err := client.GroupExport(v.GetString("group-name"), cdsclient.Format(v.GetString("format")))
if err != nil {
return err
}
fmt.Println(string(buf))
return nil
}

var groupImportCmd = cli.Command{
Name: "import",
Short: "Import a group",
Args: []cli.Arg{
{Name: "path"},
},
}

func groupImportRun(c cli.Values) error {
path := c.GetString("path")
contentFile, format, err := exportentities.OpenPath(path)
if err != nil {
return err
}
defer contentFile.Close() //nolint
if _, err := client.GroupImport(contentFile, cdsclient.ContentType(format.ContentType())); err != nil {
return err
}
fmt.Println("Group imported.")
return nil
}
3 changes: 2 additions & 1 deletion cli/cdsctl/project_favorite.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"

"github.com/ovh/cds/cli"
Expand All @@ -22,7 +23,7 @@ func projectFavoriteRun(c cli.Values) error {
ProjectKey: c.GetString(_ProjectKey),
}

res, err := client.UpdateFavorite(params)
res, err := client.UpdateFavorite(context.Background(), params)
if err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion cli/cdsctl/tools.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"
"os"
"strings"
Expand Down Expand Up @@ -71,7 +72,7 @@ func (y yamlSchemaVSCodeInstaller) Install(schemas yamlSchemaPath) error {
}

func toolsYamlSchemaRun(v cli.Values) error {
res, err := client.UserGetSchema()
res, err := client.UserGetSchema(context.Background())
if err != nil {
return err
}
Expand Down
7 changes: 4 additions & 3 deletions cli/cdsctl/user.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -30,7 +31,7 @@ var userListCmd = cli.Command{
}

func userListRun(v cli.Values) (cli.ListResult, error) {
users, err := client.UserList()
users, err := client.UserList(context.Background())
if err != nil {
return nil, err
}
Expand All @@ -43,7 +44,7 @@ var userMeCmd = cli.Command{
}

func userMeRun(v cli.Values) (interface{}, error) {
u, err := client.UserGetMe()
u, err := client.UserGetMe(context.Background())
if err != nil {
return nil, err
}
Expand All @@ -61,7 +62,7 @@ var userShowCmd = cli.Command{
}

func userShowRun(v cli.Values) (interface{}, error) {
u, err := client.UserGet(v.GetString("username"))
u, err := client.UserGet(context.Background(), v.GetString("username"))
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion cli/cdsctl/workflow_favorite.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"

"github.com/ovh/cds/cli"
Expand All @@ -23,7 +24,7 @@ func workflowFavoriteRun(c cli.Values) error {
WorkflowName: c.GetString(_WorkflowName),
}

res, err := client.UpdateFavorite(params)
res, err := client.UpdateFavorite(context.Background(), params)
if err != nil {
return err
}
Expand Down
14 changes: 7 additions & 7 deletions engine/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,12 @@ type Configuration struct {
InsecureSkipVerifyTLS bool `toml:"insecureSkipVerifyTLS" json:"insecureSkipVerifyTLS" default:"false"`
} `toml:"internalServiceMesh" json:"internalServiceMesh"`
Auth struct {
TokenDefaultDuration int64 `toml:"tokenDefaultDuration" default:"30" comment:"The default duration of a token (in days)" json:"tokenDefaultDuration"`
TokenOverlapDefaultDuration string `toml:"tokenOverlapDefaultDuration" default:"24h" comment:"The default overlap duration when a token is regen" json:"tokenOverlapDefaultDuration"`
DefaultGroup string `toml:"defaultGroup" default:"" comment:"The default group is the group in which every new user will be granted at signup" json:"defaultGroup"`
DisableAddUserInDefaultGroup bool `toml:"disableAddUserInDefaultGroup" default:"false" comment:"If false, user are automatically added in the default group" json:"disableAddUserInDefaultGroup"`
RSAPrivateKey string `toml:"rsaPrivateKey" default:"" comment:"The RSA Private Key used to sign and verify the JWT Tokens issued by the API \nThis is mandatory." json:"-"`
TokenDefaultDuration int64 `toml:"tokenDefaultDuration" default:"30" comment:"The default duration of a token (in days)" json:"tokenDefaultDuration"`
TokenOverlapDefaultDuration string `toml:"tokenOverlapDefaultDuration" default:"24h" comment:"The default overlap duration when a token is regen" json:"tokenOverlapDefaultDuration"`
DefaultGroup string `toml:"defaultGroup" default:"" comment:"The default group is the group in which every new user will be granted at signup" json:"defaultGroup"`
DisableAddUserInDefaultGroup bool `toml:"disableAddUserInDefaultGroup" default:"false" comment:"If false, user are automatically added in the default group" json:"disableAddUserInDefaultGroup"`
RSAPrivateKey string `toml:"rsaPrivateKey" default:"" comment:"The RSA Private Key used to sign and verify the JWT Tokens issued by the API \nThis is mandatory." json:"-"`
AllowedOrganizations sdk.StringSlice `toml:"allowedOrganizations" default:"" comment:"The list of allowed organizations for CDS users, let empty to authorize all organizations." json:"allowedOrganizations"`
LDAP struct {
Enabled bool `toml:"enabled" default:"false" json:"enabled"`
SignupDisabled bool `toml:"signupDisabled" default:"false" json:"signupDisabled"`
Expand All @@ -120,7 +121,6 @@ type Configuration struct {
MFASupportEnabled bool `json:"mfa_support_enabled" default:"false" toml:"mfaSupportEnabled"`
Enabled bool `json:"enabled" default:"false" toml:"enabled"`
SignupDisabled bool `json:"signupDisabled" default:"false" toml:"signupDisabled"`
MailDomain string `json:"mailDomain" toml:"mailDomain"`
RedirectMethod string `json:"redirect_method" toml:"redirectMethod"`
RedirectURL string `json:"redirect_url" toml:"redirectURL"`
Keys struct {
Expand Down Expand Up @@ -679,7 +679,6 @@ func (a *API) Serve(ctx context.Context) error {

if a.Config.Auth.CorporateSSO.Enabled {
driverConfig := corpsso.Config{
MailDomain: a.Config.Auth.CorporateSSO.MailDomain,
MFASupportEnabled: a.Config.Auth.CorporateSSO.MFASupportEnabled,
}
driverConfig.Request.Keys.RequestSigningKey = a.Config.Auth.CorporateSSO.Keys.RequestSigningKey
Expand All @@ -688,6 +687,7 @@ func (a *API) Serve(ctx context.Context) error {
driverConfig.Token.SigningKey = a.Config.Auth.CorporateSSO.Keys.TokenSigningKey
driverConfig.Token.KeySigningKey.KeySigningKey = a.Config.Auth.CorporateSSO.Keys.TokenKeySigningKey.KeySigningKey
driverConfig.Token.KeySigningKey.SigningKeyClaim = a.Config.Auth.CorporateSSO.Keys.TokenKeySigningKey.SigningKeyClaim
driverConfig.AllowedOrganizations = a.Config.Auth.AllowedOrganizations

a.AuthenticationDrivers[sdk.ConsumerCorporateSSO] = corpsso.NewDriver(driverConfig)
}
Expand Down
2 changes: 2 additions & 0 deletions engine/api/api_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ func (api *API) InitRouter() {

// Group
r.Handle("/group", Scope(sdk.AuthConsumerScopeGroup), r.GET(api.getGroupsHandler), r.POST(api.postGroupHandler))
r.Handle("/group/import", Scope(sdk.AuthConsumerScopeGroup), r.POST(api.postGroupImportHandler))
r.Handle("/group/{permGroupName}", Scope(sdk.AuthConsumerScopeGroup), r.GET(api.getGroupHandler), r.PUT(api.putGroupHandler), r.DELETE(api.deleteGroupHandler))
r.Handle("/group/{permGroupName}/export", Scope(sdk.AuthConsumerScopeGroup), r.GET(api.getGroupExportHandler))
r.Handle("/group/{permGroupName}/user", Scope(sdk.AuthConsumerScopeGroup), r.POST(api.postGroupUserHandler))
r.Handle("/group/{permGroupName}/user/{username}", Scope(sdk.AuthConsumerScopeGroup), r.PUT(api.putGroupUserHandler), r.DELETE(api.deleteGroupUserHandler))
r.Handle("/group/{permGroupName}/project", Scope(sdk.AuthConsumerScopeGroup), r.GET(api.getProjectGroupHandler))
Expand Down
21 changes: 14 additions & 7 deletions engine/api/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,18 @@ func (api *API) postAuthSigninHandler() service.Handler {
hasInitToken = hasInitToken && initToken != ""

// Check if a consumer exists for consumer type and external user identifier
consumer, err := authentication.LoadConsumerByTypeAndUserExternalID(ctx, tx, consumerType, userInfo.ExternalID)
consumer, err := authentication.LoadConsumerByTypeAndUserExternalID(ctx, tx, consumerType, userInfo.ExternalID,
authentication.LoadConsumerOptions.WithAuthentifiedUser)
if err != nil && !sdk.ErrorIs(err, sdk.ErrNotFound) {
return err
}

// If there is no existing consumer we should check if a user need to be created
// Then we want to create a new consumer for current type
if consumer == nil {
var u *sdk.AuthentifiedUser

var u *sdk.AuthentifiedUser
if consumer != nil {
u = consumer.AuthentifiedUser
} else {
currentConsumer := getAPIConsumer(ctx)
if currentConsumer != nil {
// If no consumer already exists for given request, but there is a current session
Expand Down Expand Up @@ -208,9 +210,10 @@ func (api *API) postAuthSigninHandler() service.Handler {
}

u = &sdk.AuthentifiedUser{
Ring: sdk.UserRingUser,
Username: userInfo.Username,
Fullname: userInfo.Fullname,
Ring: sdk.UserRingUser,
Username: userInfo.Username,
Fullname: userInfo.Fullname,
Organization: userInfo.Organization,
}

// If a magic token is given and there is no admin already registered, set new user as admin
Expand Down Expand Up @@ -257,6 +260,10 @@ func (api *API) postAuthSigninHandler() service.Handler {
}
}

if err := api.userSetOrganization(ctx, tx, u, userInfo.Organization); err != nil {
return err
}

// If a new user has been created and a first admin has been create,
// let's init the builtin consumers from the magix token
if signupDone && hasInitToken {
Expand Down
Loading

0 comments on commit bbecfff

Please sign in to comment.