Add 'accounts/' from commit 'd2585070bc25d2c8cd0c86476041ee502afb4ac5'
git-subtree-dir: accounts git-subtree-mainline:7b8e9bc298git-subtree-split:d2585070bc
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v2/client/grpc"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/flagset"
|
||||
accounts "github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
)
|
||||
|
||||
// AddAccount command creates a new account
|
||||
func AddAccount(cfg *config.Config) *cli.Command {
|
||||
a := &accounts.Account{
|
||||
PasswordProfile: &accounts.PasswordProfile{},
|
||||
}
|
||||
return &cli.Command{
|
||||
Name: "add",
|
||||
Usage: "Create a new account",
|
||||
Aliases: []string{"create", "a"},
|
||||
Flags: flagset.AddAccountWithConfig(cfg, a),
|
||||
Before: func(c *cli.Context) error {
|
||||
// Write value of username to the flags beneath, as preferred name
|
||||
// and on-premises-sam-account-name is probably confusing for users.
|
||||
if username := c.String("username"); username != "" {
|
||||
if !c.IsSet("on-premises-sam-account-name") {
|
||||
if err := c.Set("on-premises-sam-account-name", username); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !c.IsSet("preferred-name") {
|
||||
if err := c.Set("preferred-name", username); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
accSvcID := cfg.GRPC.Namespace + "." + cfg.Server.Name
|
||||
accSvc := accounts.NewAccountsService(accSvcID, grpc.NewClient())
|
||||
_, err := accSvc.CreateAccount(c.Context, &accounts.CreateAccountRequest{
|
||||
Account: a,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Errorf("could not create account %w", err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v2/client/grpc"
|
||||
tw "github.com/olekukonko/tablewriter"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/flagset"
|
||||
accounts "github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// InspectAccount command shows detailed information about a specific account.
|
||||
func InspectAccount(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "inspect",
|
||||
Usage: "Show detailed data on an existing account",
|
||||
ArgsUsage: "id",
|
||||
Flags: flagset.InspectAccountWithConfig(cfg),
|
||||
Action: func(c *cli.Context) error {
|
||||
accServiceID := cfg.GRPC.Namespace + "." + cfg.Server.Name
|
||||
if c.NArg() != 1 {
|
||||
fmt.Println("Please provide a user-id")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
uid := c.Args().First()
|
||||
accSvc := accounts.NewAccountsService(accServiceID, grpc.NewClient())
|
||||
acc, err := accSvc.GetAccount(c.Context, &accounts.GetAccountRequest{
|
||||
Id: uid,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Errorf("could not view account %w", err))
|
||||
return err
|
||||
}
|
||||
|
||||
buildAccountInspectTable(acc).Render()
|
||||
return nil
|
||||
}}
|
||||
}
|
||||
|
||||
func buildAccountInspectTable(acc *accounts.Account) *tw.Table {
|
||||
table := tw.NewWriter(os.Stdout)
|
||||
table.SetAutoMergeCells(true)
|
||||
table.AppendBulk([][]string{
|
||||
{"ID", acc.Id},
|
||||
{"Mail", acc.Mail},
|
||||
{"DisplayName", acc.DisplayName},
|
||||
{"PreferredName", acc.PreferredName},
|
||||
{"AccountEnabled", strconv.FormatBool(acc.AccountEnabled)},
|
||||
{"CreationType", acc.CreationType},
|
||||
{"CreatedDateTime", acc.CreatedDateTime.String()},
|
||||
{"Description", acc.Description},
|
||||
{"ExternalUserState", acc.ExternalUserState},
|
||||
{"UidNumber", fmt.Sprintf("%+d", acc.UidNumber)},
|
||||
{"GidNumber", fmt.Sprintf("%+d", acc.GidNumber)},
|
||||
{"IsResourceAccount", strconv.FormatBool(acc.IsResourceAccount)},
|
||||
{"OnPremisesDistinguishedName", acc.OnPremisesDistinguishedName},
|
||||
{"OnPremisesDomainName", acc.OnPremisesDomainName},
|
||||
{"OnPremisesImmutableId", acc.OnPremisesImmutableId},
|
||||
{"OnPremisesSamAccountName", acc.OnPremisesSamAccountName},
|
||||
{"OnPremisesSecurityIdentifier", acc.OnPremisesSecurityIdentifier},
|
||||
{"OnPremisesUserPrincipalName", acc.OnPremisesUserPrincipalName},
|
||||
{"RefreshTokenValidFromDateTime", acc.RefreshTokensValidFromDateTime.String()},
|
||||
})
|
||||
|
||||
// Merged cell with group memberships
|
||||
for k := range acc.MemberOf {
|
||||
table.Append([]string{"MemberOf", acc.MemberOf[k].DisplayName})
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v2/client/grpc"
|
||||
tw "github.com/olekukonko/tablewriter"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/flagset"
|
||||
accounts "github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ListAccounts command lists all accounts
|
||||
func ListAccounts(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "list",
|
||||
Usage: "List existing accounts",
|
||||
Aliases: []string{"ls"},
|
||||
Flags: flagset.ListAccountsWithConfig(cfg),
|
||||
Action: func(c *cli.Context) error {
|
||||
accSvcID := cfg.GRPC.Namespace + "." + cfg.Server.Name
|
||||
accSvc := accounts.NewAccountsService(accSvcID, grpc.NewClient())
|
||||
resp, err := accSvc.ListAccounts(c.Context, &accounts.ListAccountsRequest{})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Errorf("could not list accounts %w", err))
|
||||
return err
|
||||
}
|
||||
|
||||
buildAccountsListTable(resp.Accounts).Render()
|
||||
return nil
|
||||
}}
|
||||
}
|
||||
|
||||
// buildAccountsListTable creates an ascii table for printing on the cli
|
||||
func buildAccountsListTable(accs []*accounts.Account) *tw.Table {
|
||||
table := tw.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Id", "DisplayName", "Mail", "AccountEnabled"})
|
||||
table.SetAutoFormatHeaders(false)
|
||||
for _, acc := range accs {
|
||||
table.Append([]string{
|
||||
acc.Id,
|
||||
acc.DisplayName,
|
||||
acc.Mail,
|
||||
strconv.FormatBool(acc.AccountEnabled)})
|
||||
}
|
||||
return table
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v2/client/grpc"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/flagset"
|
||||
accounts "github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
"os"
|
||||
)
|
||||
|
||||
// RemoveAccount command deletes an existing account.
|
||||
func RemoveAccount(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "remove",
|
||||
Usage: "Removes an existing account",
|
||||
ArgsUsage: "id",
|
||||
Aliases: []string{"rm"},
|
||||
Flags: flagset.RemoveAccountWithConfig(cfg),
|
||||
Action: func(c *cli.Context) error {
|
||||
accServiceID := cfg.GRPC.Namespace + "." + cfg.Server.Name
|
||||
if c.NArg() != 1 {
|
||||
fmt.Println("Please provide a user-id")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
uid := c.Args().First()
|
||||
accSvc := accounts.NewAccountsService(accServiceID, grpc.NewClient())
|
||||
_, err := accSvc.DeleteAccount(c.Context, &accounts.DeleteAccountRequest{Id: uid})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Errorf("could not delete account %w", err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/owncloud/ocis-accounts/pkg/flagset"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/version"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultConfigPaths = []string{"/etc/ocis", "$HOME/.ocis", "./config"}
|
||||
defaultFilename = "accounts"
|
||||
)
|
||||
|
||||
// Execute is the entry point for the ocis-accounts command.
|
||||
func Execute() error {
|
||||
cfg := config.New()
|
||||
app := &cli.App{
|
||||
Name: "ocis-accounts",
|
||||
Version: version.String,
|
||||
Usage: "Provide accounts and groups for oCIS",
|
||||
Compiled: version.Compiled(),
|
||||
|
||||
Authors: []*cli.Author{
|
||||
{
|
||||
Name: "ownCloud GmbH",
|
||||
Email: "support@owncloud.com",
|
||||
},
|
||||
},
|
||||
|
||||
Flags: flagset.RootWithConfig(cfg),
|
||||
|
||||
Before: func(c *cli.Context) error {
|
||||
return ParseConfig(c, cfg)
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
Server(cfg),
|
||||
AddAccount(cfg),
|
||||
UpdateAccount(cfg),
|
||||
ListAccounts(cfg),
|
||||
InspectAccount(cfg),
|
||||
RemoveAccount(cfg),
|
||||
},
|
||||
}
|
||||
|
||||
cli.HelpFlag = &cli.BoolFlag{
|
||||
Name: "help,h",
|
||||
Usage: "Show the help",
|
||||
}
|
||||
|
||||
cli.VersionFlag = &cli.BoolFlag{
|
||||
Name: "version,v",
|
||||
Usage: "Print the version",
|
||||
}
|
||||
|
||||
return app.Run(os.Args)
|
||||
}
|
||||
|
||||
// NewLogger initializes a service-specific logger instance.
|
||||
func NewLogger(cfg *config.Config) log.Logger {
|
||||
return log.NewLogger(
|
||||
log.Name("accounts"),
|
||||
log.Level(cfg.Log.Level),
|
||||
log.Pretty(cfg.Log.Pretty),
|
||||
log.Color(cfg.Log.Color),
|
||||
)
|
||||
}
|
||||
|
||||
// ParseConfig loads accounts configuration from Viper known paths.
|
||||
func ParseConfig(c *cli.Context, cfg *config.Config) error {
|
||||
logger := NewLogger(cfg)
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.SetEnvPrefix("ACCOUNTS")
|
||||
viper.AutomaticEnv()
|
||||
|
||||
if c.IsSet("config-file") {
|
||||
viper.SetConfigFile(c.String("config-file"))
|
||||
} else {
|
||||
viper.SetConfigName(defaultFilename)
|
||||
|
||||
for _, v := range defaultConfigPaths {
|
||||
viper.AddConfigPath(v)
|
||||
}
|
||||
}
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
switch err.(type) {
|
||||
case viper.ConfigFileNotFoundError:
|
||||
logger.Info().
|
||||
Msg("Continue without config")
|
||||
case viper.UnsupportedConfigError:
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Unsupported config type")
|
||||
default:
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Failed to read config")
|
||||
}
|
||||
}
|
||||
|
||||
if err := viper.Unmarshal(&cfg); err != nil {
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Failed to parse config")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/oklog/run"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/flagset"
|
||||
"github.com/owncloud/ocis-accounts/pkg/metrics"
|
||||
"github.com/owncloud/ocis-accounts/pkg/server/grpc"
|
||||
"github.com/owncloud/ocis-accounts/pkg/server/http"
|
||||
svc "github.com/owncloud/ocis-accounts/pkg/service/v0"
|
||||
)
|
||||
|
||||
// Server is the entry point for the server command.
|
||||
func Server(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "server",
|
||||
Usage: "Start ocis accounts service",
|
||||
Description: "uses an LDAP server as the storage backend",
|
||||
Flags: flagset.ServerWithConfig(cfg),
|
||||
Before: func(ctx *cli.Context) error {
|
||||
if cfg.HTTP.Root != "/" {
|
||||
cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/")
|
||||
}
|
||||
|
||||
// When running on single binary mode the before hook from the root command won't get called. We manually
|
||||
// call this before hook from ocis command, so the configuration can be loaded.
|
||||
return ParseConfig(ctx, cfg)
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := NewLogger(cfg)
|
||||
|
||||
var (
|
||||
gr = run.Group{}
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
mtrcs = metrics.New()
|
||||
)
|
||||
|
||||
defer cancel()
|
||||
|
||||
handler, err := svc.New(svc.Logger(logger), svc.Config(cfg))
|
||||
if err != nil {
|
||||
logger.Fatal().Err(err).Msg("could not initialize service handler")
|
||||
}
|
||||
|
||||
{
|
||||
server := http.Server(
|
||||
http.Logger(logger),
|
||||
http.Name(cfg.Server.Name),
|
||||
http.Context(ctx),
|
||||
http.Config(cfg),
|
||||
http.Metrics(mtrcs),
|
||||
http.Flags(flagset.RootWithConfig(cfg)),
|
||||
http.Flags(flagset.ServerWithConfig(cfg)),
|
||||
http.Handler(handler),
|
||||
)
|
||||
|
||||
gr.Add(server.Run, func(_ error) {
|
||||
logger.Info().
|
||||
Str("server", "http").
|
||||
Msg("Shutting down server")
|
||||
|
||||
cancel()
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
server := grpc.Server(
|
||||
grpc.Logger(logger),
|
||||
grpc.Name(cfg.Server.Name),
|
||||
grpc.Context(ctx),
|
||||
grpc.Config(cfg),
|
||||
grpc.Metrics(mtrcs),
|
||||
grpc.Handler(handler),
|
||||
)
|
||||
|
||||
gr.Add(func() error {
|
||||
logger.Info().Str("service", server.Name()).Msg("Reporting settings bundles to settings service")
|
||||
go svc.RegisterSettingsBundles(&logger)
|
||||
go svc.RegisterPermissions(&logger)
|
||||
return server.Run()
|
||||
}, func(_ error) {
|
||||
logger.Info().
|
||||
Str("server", "grpc").
|
||||
Msg("Shutting down server")
|
||||
|
||||
cancel()
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
stop := make(chan os.Signal, 1)
|
||||
|
||||
gr.Add(func() error {
|
||||
signal.Notify(stop, os.Interrupt)
|
||||
|
||||
<-stop
|
||||
|
||||
return nil
|
||||
}, func(err error) {
|
||||
close(stop)
|
||||
cancel()
|
||||
})
|
||||
}
|
||||
|
||||
return gr.Run()
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v2/client/grpc"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/flagset"
|
||||
accounts "github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
"google.golang.org/genproto/protobuf/field_mask"
|
||||
)
|
||||
|
||||
// UpdateAccount command for modifying accounts including password policies
|
||||
func UpdateAccount(cfg *config.Config) *cli.Command {
|
||||
a := &accounts.Account{
|
||||
PasswordProfile: &accounts.PasswordProfile{},
|
||||
}
|
||||
return &cli.Command{
|
||||
Name: "update",
|
||||
Usage: "Make changes to an existing account",
|
||||
ArgsUsage: "id",
|
||||
Flags: flagset.UpdateAccountWithConfig(cfg, a),
|
||||
Before: func(c *cli.Context) error {
|
||||
if len(c.StringSlice("password_policies")) > 0 {
|
||||
// StringSliceFlag doesn't support Destination
|
||||
a.PasswordProfile.PasswordPolicies = c.StringSlice("password_policies")
|
||||
}
|
||||
|
||||
if c.NArg() != 1 {
|
||||
return errors.New("missing account-id")
|
||||
}
|
||||
|
||||
if c.NumFlags() == 0 {
|
||||
return errors.New("missing attribute-flags for update")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
a.Id = c.Args().First()
|
||||
accSvcID := cfg.GRPC.Namespace + "." + cfg.Server.Name
|
||||
accSvc := accounts.NewAccountsService(accSvcID, grpc.NewClient())
|
||||
_, err := accSvc.UpdateAccount(c.Context, &accounts.UpdateAccountRequest{
|
||||
Account: a,
|
||||
UpdateMask: buildAccUpdateMask(c.FlagNames()),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Errorf("could not update account %w", err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}}
|
||||
}
|
||||
|
||||
// buildAccUpdateMask by mapping passed update flags to account fieldNames.
|
||||
//
|
||||
// The UpdateMask is passed with the update-request to the server so that
|
||||
// only the modified values are transferred.
|
||||
func buildAccUpdateMask(setFlags []string) *field_mask.FieldMask {
|
||||
var flagToPath = map[string]string{
|
||||
"enabled": "AccountEnabled",
|
||||
"displayname": "DisplayName",
|
||||
"preferred-name": "PreferredName",
|
||||
"uidnumber": "UidNumber",
|
||||
"gidnumber": "GidNumber",
|
||||
"mail": "Mail",
|
||||
"description": "Description",
|
||||
"password": "PasswordProfile.Password",
|
||||
"password-policies": "PasswordProfile.PasswordPolicies",
|
||||
"force-password-change": "PasswordProfile.ForceChangePasswordNextSignIn",
|
||||
"force-password-change-mfa": "PasswordProfile.ForceChangePasswordNextSignInWithMfa",
|
||||
"on-premises-sam-account-name": "OnPremisesSamAccountName",
|
||||
}
|
||||
|
||||
updatedPaths := make([]string, 0)
|
||||
|
||||
for _, v := range setFlags {
|
||||
if _, ok := flagToPath[v]; ok {
|
||||
updatedPaths = append(updatedPaths, flagToPath[v])
|
||||
}
|
||||
}
|
||||
|
||||
return &field_mask.FieldMask{Paths: updatedPaths}
|
||||
}
|
||||
Reference in New Issue
Block a user