mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-04 14:13:18 -04:00
Merge pull request #117 from owncloud/fix-create-account
Fix create account
This commit is contained in:
9
changelog/unreleased/single-service-handler.md
Normal file
9
changelog/unreleased/single-service-handler.md
Normal file
@@ -0,0 +1,9 @@
|
||||
Bugfix: Cleanup separated indices in memory
|
||||
|
||||
The accounts service was creating a bleve index instance in the service handler, thus creating separate in memory indices for the http and grpc servers. We moved the service handler creation out of the server creation so that the service handler, thus also the bleve index, is a shared instance of the servers.
|
||||
|
||||
This fixes a bug that accounts created through the web ui were not able to sign in until a service restart.
|
||||
|
||||
https://github.com/owncloud/product/issues/224
|
||||
https://github.com/owncloud/ocis-accounts/pull/117
|
||||
|
||||
@@ -43,6 +43,11 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
|
||||
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),
|
||||
@@ -52,6 +57,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
http.Metrics(mtrcs),
|
||||
http.Flags(flagset.RootWithConfig(cfg)),
|
||||
http.Flags(flagset.ServerWithConfig(cfg)),
|
||||
http.Handler(handler),
|
||||
)
|
||||
|
||||
gr.Add(server.Run, func(_ error) {
|
||||
@@ -70,6 +76,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
grpc.Context(ctx),
|
||||
grpc.Config(cfg),
|
||||
grpc.Metrics(mtrcs),
|
||||
grpc.Handler(handler),
|
||||
)
|
||||
|
||||
gr.Add(func() error {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/metrics"
|
||||
svc "github.com/owncloud/ocis-accounts/pkg/service/v0"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
)
|
||||
|
||||
@@ -20,6 +21,7 @@ type Options struct {
|
||||
Config *config.Config
|
||||
Metrics *metrics.Metrics
|
||||
Flags []cli.Flag
|
||||
Handler *svc.Service
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
@@ -74,3 +76,10 @@ func Flags(val []cli.Flag) Option {
|
||||
o.Flags = append(o.Flags, val...)
|
||||
}
|
||||
}
|
||||
|
||||
// Handler provides a function to set the handler option.
|
||||
func Handler(val *svc.Service) Option {
|
||||
return func(o *Options) {
|
||||
o.Handler = val
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mclient "github.com/micro/go-micro/v2/client"
|
||||
"github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
svc "github.com/owncloud/ocis-accounts/pkg/service/v0"
|
||||
"github.com/owncloud/ocis-pkg/v2/roles"
|
||||
"github.com/owncloud/ocis-pkg/v2/service/grpc"
|
||||
settings "github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
)
|
||||
|
||||
// Server initializes a new go-micro service ready to run
|
||||
func Server(opts ...Option) grpc.Service {
|
||||
options := newOptions(opts...)
|
||||
handler := options.Handler
|
||||
|
||||
service := grpc.NewService(
|
||||
grpc.Name(options.Config.Server.Name),
|
||||
@@ -24,31 +19,10 @@ func Server(opts ...Option) grpc.Service {
|
||||
grpc.Flags(options.Flags...),
|
||||
)
|
||||
|
||||
var hdlr *svc.Service
|
||||
var err error
|
||||
|
||||
// TODO this won't work with a registry other than mdns. Look into Micro's client initialization.
|
||||
// https://github.com/owncloud/ocis-proxy/issues/38
|
||||
rs := settings.NewRoleService("com.owncloud.api.settings", mclient.DefaultClient)
|
||||
roleManager := roles.NewManager(
|
||||
roles.CacheSize(1024),
|
||||
roles.CacheTTL(time.Hour*24*7),
|
||||
roles.Logger(options.Logger),
|
||||
roles.RoleService(rs),
|
||||
)
|
||||
|
||||
if hdlr, err = svc.New(
|
||||
svc.Logger(options.Logger),
|
||||
svc.Config(options.Config),
|
||||
svc.RoleManager(&roleManager),
|
||||
svc.RoleService(rs),
|
||||
); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("could not initialize service handler")
|
||||
}
|
||||
if err = proto.RegisterAccountsServiceHandler(service.Server(), hdlr); err != nil {
|
||||
if err := proto.RegisterAccountsServiceHandler(service.Server(), handler); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("could not register service handler")
|
||||
}
|
||||
if err = proto.RegisterGroupsServiceHandler(service.Server(), hdlr); err != nil {
|
||||
if err := proto.RegisterGroupsServiceHandler(service.Server(), handler); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("could not register groups handler")
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/metrics"
|
||||
svc "github.com/owncloud/ocis-accounts/pkg/service/v0"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
)
|
||||
|
||||
@@ -20,6 +21,7 @@ type Options struct {
|
||||
Config *config.Config
|
||||
Metrics *metrics.Metrics
|
||||
Flags []cli.Flag
|
||||
Handler *svc.Service
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
@@ -74,3 +76,10 @@ func Flags(val []cli.Flag) Option {
|
||||
o.Flags = append(o.Flags, val...)
|
||||
}
|
||||
}
|
||||
|
||||
// Handler provides a function to set the handler option.
|
||||
func Handler(val *svc.Service) Option {
|
||||
return func(o *Options) {
|
||||
o.Handler = val
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
mclient "github.com/micro/go-micro/v2/client"
|
||||
"github.com/owncloud/ocis-accounts/pkg/assets"
|
||||
"github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
svc "github.com/owncloud/ocis-accounts/pkg/service/v0"
|
||||
"github.com/owncloud/ocis-accounts/pkg/version"
|
||||
"github.com/owncloud/ocis-pkg/v2/account"
|
||||
"github.com/owncloud/ocis-pkg/v2/middleware"
|
||||
"github.com/owncloud/ocis-pkg/v2/roles"
|
||||
"github.com/owncloud/ocis-pkg/v2/service/http"
|
||||
settings "github.com/owncloud/ocis-settings/pkg/proto/v0"
|
||||
)
|
||||
|
||||
// Server initializes the http service and server.
|
||||
func Server(opts ...Option) http.Service {
|
||||
options := newOptions(opts...)
|
||||
handler := options.Handler
|
||||
|
||||
service := http.NewService(
|
||||
http.Logger(options.Logger),
|
||||
@@ -30,25 +25,6 @@ func Server(opts ...Option) http.Service {
|
||||
http.Flags(options.Flags...),
|
||||
)
|
||||
|
||||
// TODO this won't work with a registry other than mdns. Look into Micro's client initialization.
|
||||
// https://github.com/owncloud/ocis-proxy/issues/38
|
||||
rs := settings.NewRoleService("com.owncloud.api.settings", mclient.DefaultClient)
|
||||
roleManager := roles.NewManager(
|
||||
roles.CacheSize(1024),
|
||||
roles.CacheTTL(time.Hour*24*7),
|
||||
roles.Logger(options.Logger),
|
||||
roles.RoleService(rs),
|
||||
)
|
||||
handler, err := svc.New(
|
||||
svc.Logger(options.Logger),
|
||||
svc.Config(options.Config),
|
||||
svc.RoleManager(&roleManager),
|
||||
svc.RoleService(rs),
|
||||
)
|
||||
if err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("could not initialize service handler")
|
||||
}
|
||||
|
||||
mux := chi.NewMux()
|
||||
|
||||
mux.Use(middleware.RealIP)
|
||||
|
||||
@@ -37,28 +37,6 @@ import (
|
||||
// accLock mutually exclude readers from writers on account files
|
||||
var accLock sync.Mutex
|
||||
|
||||
func (s Service) indexAccounts(path string) (err error) {
|
||||
var f *os.File
|
||||
if f, err = os.Open(path); err != nil {
|
||||
s.log.Error().Err(err).Str("dir", path).Msg("could not open accounts folder")
|
||||
return
|
||||
}
|
||||
list, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Str("dir", path).Msg("could not list accounts folder")
|
||||
return
|
||||
}
|
||||
for _, file := range list {
|
||||
err = s.indexAccount(file.Name())
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Str("file", file.Name()).Msg("could not index account")
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s Service) indexAccount(id string) error {
|
||||
a := &proto.BleveAccount{
|
||||
BleveType: "account",
|
||||
@@ -344,29 +322,16 @@ func (s Service) CreateAccount(ctx context.Context, in *proto.CreateAccountReque
|
||||
|
||||
// extract group id
|
||||
// TODO groups should be ignored during create, use groups.AddMember? return error?
|
||||
|
||||
// write and index account - note: don't do anything else in between!
|
||||
if err = s.writeAccount(acc); err != nil {
|
||||
s.log.Error().Err(err).Str("id", id).Msg("could not persist new account")
|
||||
s.debugLogAccount(acc).Msg("could not persist new account")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: assign user role to all new users for now, as create Account request does not have any role field
|
||||
if s.RoleService == nil {
|
||||
return merrors.InternalServerError(s.id, "could not assign role to account: roleService not configured")
|
||||
}
|
||||
_, err = s.RoleService.AssignRoleToUser(ctx, &settings.AssignRoleToUserRequest{
|
||||
AccountUuid: acc.Id,
|
||||
RoleId: settings_svc.BundleUUIDRoleUser,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return merrors.InternalServerError(s.id, "could not assign role to account: %v", err.Error())
|
||||
}
|
||||
|
||||
if err = s.indexAccount(acc.Id); err != nil {
|
||||
return merrors.InternalServerError(s.id, "could not index new account: %v", err.Error())
|
||||
}
|
||||
|
||||
s.log.Debug().Interface("account", acc).Msg("account after indexing")
|
||||
|
||||
if acc.PasswordProfile != nil {
|
||||
@@ -382,6 +347,17 @@ func (s Service) CreateAccount(ctx context.Context, in *proto.CreateAccountReque
|
||||
out.OnPremisesSamAccountName = acc.OnPremisesSamAccountName
|
||||
}
|
||||
|
||||
// TODO: assign user role to all new users for now, as create Account request does not have any role field
|
||||
if s.RoleService == nil {
|
||||
return merrors.InternalServerError(s.id, "could not assign role to account: roleService not configured")
|
||||
}
|
||||
if _, err = s.RoleService.AssignRoleToUser(ctx, &settings.AssignRoleToUserRequest{
|
||||
AccountUuid: acc.Id,
|
||||
RoleId: settings_svc.BundleUUIDRoleUser,
|
||||
}); err != nil {
|
||||
return merrors.InternalServerError(s.id, "could not assign role to account: %v", err.Error())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -17,28 +17,6 @@ import (
|
||||
"github.com/owncloud/ocis-accounts/pkg/provider"
|
||||
)
|
||||
|
||||
func (s Service) indexGroups(path string) (err error) {
|
||||
var f *os.File
|
||||
if f, err = os.Open(path); err != nil {
|
||||
s.log.Error().Err(err).Str("dir", path).Msg("could not open groups folder")
|
||||
return
|
||||
}
|
||||
list, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Str("dir", path).Msg("could not list groups folder")
|
||||
return
|
||||
}
|
||||
for _, file := range list {
|
||||
err = s.indexGroup(file.Name())
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Str("file", file.Name()).Msg("could not index account")
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// accLock mutually exclude readers from writers on group files
|
||||
var groupLock sync.Mutex
|
||||
|
||||
|
||||
@@ -28,28 +28,28 @@ func newOptions(opts ...Option) Options {
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
// Logger provides a function to set the Logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
// Config provides a function to set the Config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
|
||||
// RoleService provides a function to set the role service option.
|
||||
// RoleService provides a function to set the RoleService option.
|
||||
func RoleService(val settings.RoleService) Option {
|
||||
return func(o *Options) {
|
||||
o.RoleService = val
|
||||
}
|
||||
}
|
||||
|
||||
// RoleManager provides a function to set the roles manager option.
|
||||
// RoleManager provides a function to set the RoleManager option.
|
||||
func RoleManager(val *roles.Manager) Option {
|
||||
return func(o *Options) {
|
||||
o.RoleManager = val
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/blevesearch/bleve"
|
||||
"github.com/blevesearch/bleve/analysis/analyzer/custom"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"github.com/blevesearch/bleve/analysis/analyzer/standard"
|
||||
"github.com/blevesearch/bleve/analysis/token/lowercase"
|
||||
"github.com/blevesearch/bleve/analysis/tokenizer/unicode"
|
||||
mclient "github.com/micro/go-micro/v2/client"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
@@ -30,228 +32,55 @@ func New(opts ...Option) (s *Service, err error) {
|
||||
options := newOptions(opts...)
|
||||
logger := options.Logger
|
||||
cfg := options.Config
|
||||
|
||||
roleService := options.RoleService
|
||||
if roleService == nil {
|
||||
// https://github.com/owncloud/ocis-proxy/issues/38
|
||||
// TODO this won't work with a registry other than mdns. Look into Micro's client initialization.
|
||||
roleService = settings.NewRoleService("com.owncloud.api.settings", mclient.DefaultClient)
|
||||
}
|
||||
roleManager := options.RoleManager
|
||||
// read all user and group records
|
||||
if roleManager == nil {
|
||||
m := roles.NewManager(
|
||||
roles.CacheSize(1024),
|
||||
roles.CacheTTL(time.Hour*24*7),
|
||||
roles.Logger(options.Logger),
|
||||
roles.RoleService(roleService),
|
||||
)
|
||||
roleManager = &m
|
||||
}
|
||||
|
||||
s = &Service{
|
||||
id: cfg.GRPC.Namespace + "." + cfg.Server.Name,
|
||||
log: logger,
|
||||
Config: cfg,
|
||||
RoleService: roleService,
|
||||
RoleManager: roleManager,
|
||||
}
|
||||
|
||||
// build an index
|
||||
if s.index, err = s.buildIndex(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create default accounts
|
||||
accountsDir := filepath.Join(cfg.Server.AccountsDataPath, "accounts")
|
||||
{
|
||||
// check if accounts exist
|
||||
var fi os.FileInfo
|
||||
if fi, err = os.Stat(accountsDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// create accounts directory
|
||||
if err = os.MkdirAll(accountsDir, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// create default accounts
|
||||
accounts := []proto.Account{
|
||||
{
|
||||
Id: "4c510ada-c86b-4815-8820-42cdf82c3d51",
|
||||
PreferredName: "einstein",
|
||||
OnPremisesSamAccountName: "einstein",
|
||||
Mail: "einstein@example.org",
|
||||
DisplayName: "Albert Einstein",
|
||||
UidNumber: 20000,
|
||||
GidNumber: 30000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=35210$sa1u5Pmfo4cr23Vw$RJNGElaDB1D3xorWkfTEGm2Ko.o2QL3E0cimKx23MNxVWVFSkUUeRoC7FqC4RzYDNQBD6cKzovTEaDD.8TDkD.",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
{Id: "6040aa17-9c64-4fef-9bd0-77234d71bad0"}, // sailing-lovers
|
||||
{Id: "dd58e5ec-842e-498b-8800-61f2ec6f911f"}, // violin-haters
|
||||
{Id: "262982c1-2362-4afa-bfdf-8cbfef64a06e"}, // physics-lovers
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c",
|
||||
PreferredName: "marie",
|
||||
OnPremisesSamAccountName: "marie",
|
||||
Mail: "marie@example.org",
|
||||
DisplayName: "Marie Curie",
|
||||
UidNumber: 20001,
|
||||
GidNumber: 30000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=81434$sa1u5Pmfo4cr23Vw$W78cyL884GmuvDpxYPvSRBVzEj02T5QhTTcI8Dv4IKvMooDFGv4bwaWMkH9HfJ0wgpEBW7Lp.4Cad0xE/MYSg1",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
{Id: "7b87fd49-286e-4a5f-bafd-c535d5dd997a"}, // radium-lovers
|
||||
{Id: "cedc21aa-4072-4614-8676-fa9165f598ff"}, // polonium-lovers
|
||||
{Id: "262982c1-2362-4afa-bfdf-8cbfef64a06e"}, // physics-lovers
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "932b4540-8d16-481e-8ef4-588e4b6b151c",
|
||||
PreferredName: "richard",
|
||||
OnPremisesSamAccountName: "richard",
|
||||
Mail: "richard@example.org",
|
||||
DisplayName: "Richard Feynman",
|
||||
UidNumber: 20002,
|
||||
GidNumber: 30000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=5524$sa1u5Pmfo4cr23Vw$58bQVL/JeUlwM0RY21YKAFMvKvwKLLysGllYXox.vwKT5dHMwdzJjCxwTDMnB2o2pwexC8o/iOXyP2zrhALS40",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
{Id: "a1726108-01f8-4c30-88df-2b1a9d1cba1a"}, // quantum-lovers
|
||||
{Id: "167cbee2-0518-455a-bfb2-031fe0621e5d"}, // philosophy-haters
|
||||
{Id: "262982c1-2362-4afa-bfdf-8cbfef64a06e"}, // physics-lovers
|
||||
},
|
||||
},
|
||||
// admin user(s)
|
||||
{
|
||||
Id: "058bff95-6708-4fe5-91e4-9ea3d377588b",
|
||||
PreferredName: "moss",
|
||||
OnPremisesSamAccountName: "moss",
|
||||
Mail: "moss@example.org",
|
||||
DisplayName: "Maurice Moss",
|
||||
UidNumber: 20003,
|
||||
GidNumber: 30000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=47068$lhw6odzXW0LTk/ao$GgxS.pIgP8jawLJBAiyNor2FrWzrULF95PwspRkli2W3VF.4HEwTYlQfRXbNQBMjNCEcEYlgZo3a.kRz2k2N0/",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
},
|
||||
},
|
||||
// technical users for kopano and reva
|
||||
{
|
||||
Id: "820ba2a1-3f54-4538-80a4-2d73007e30bf",
|
||||
PreferredName: "konnectd",
|
||||
OnPremisesSamAccountName: "konnectd",
|
||||
Mail: "idp@example.org",
|
||||
DisplayName: "Kopano Konnectd",
|
||||
UidNumber: 10000,
|
||||
GidNumber: 15000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=9746$sa1u5Pmfo4cr23Vw$2hnwpkTvUkWX0v6mh8Aw1pbzEXa9EUJzmrey4g2W/8arwWCwhteqU//3aWnA3S0d5T21fOKYteoqlsN1IbTcN.",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "34f38767-c937-4eb6-b847-1c175829a2a0"}, // sysusers
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "bc596f3c-c955-4328-80a0-60d018b4ad57",
|
||||
PreferredName: "reva",
|
||||
OnPremisesSamAccountName: "reva",
|
||||
Mail: "storage@example.org",
|
||||
DisplayName: "Reva Inter Operability Platform",
|
||||
UidNumber: 10001,
|
||||
GidNumber: 15000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=91087$sa1u5Pmfo4cr23Vw$wPC3BbMTbP/ytlo0p.f99zJifyO70AUCdKIK9hkhwutBKGCirLmZs/MsWAG6xHjVvmnmHN5NoON7FUGv5pPaN.",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "34f38767-c937-4eb6-b847-1c175829a2a0"}, // sysusers
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := range accounts {
|
||||
// create account on disk
|
||||
var bytes []byte
|
||||
if bytes, err = json.Marshal(&accounts[i]); err != nil {
|
||||
logger.Error().Err(err).Interface("account", &accounts[i]).Msg("could not marshal default account")
|
||||
return
|
||||
}
|
||||
path := filepath.Join(accountsDir, accounts[i].Id)
|
||||
if err = ioutil.WriteFile(path, bytes, 0600); err != nil {
|
||||
accounts[i].PasswordProfile.Password = "***REMOVED***"
|
||||
logger.Error().Err(err).Str("path", path).Interface("account", &accounts[i]).Msg("could not persist default account")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// set role for admin users and regular users
|
||||
assignRoleToUser("058bff95-6708-4fe5-91e4-9ea3d377588b", settings_svc.BundleUUIDRoleAdmin, roleService, logger)
|
||||
for _, accountID := range []string{
|
||||
"058bff95-6708-4fe5-91e4-9ea3d377588b", //moss
|
||||
} {
|
||||
assignRoleToUser(accountID, settings_svc.BundleUUIDRoleAdmin, roleService, logger)
|
||||
}
|
||||
for _, accountID := range []string{
|
||||
"4c510ada-c86b-4815-8820-42cdf82c3d51", //einstein
|
||||
"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", //marie
|
||||
"932b4540-8d16-481e-8ef4-588e4b6b151c", //richard
|
||||
} {
|
||||
assignRoleToUser(accountID, settings_svc.BundleUUIDRoleUser, roleService, logger)
|
||||
}
|
||||
}
|
||||
} else if !fi.IsDir() {
|
||||
return nil, fmt.Errorf("%s is not a directory", accountsDir)
|
||||
}
|
||||
if err = s.createDefaultAccounts(accountsDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create default groups
|
||||
groupsDir := filepath.Join(cfg.Server.AccountsDataPath, "groups")
|
||||
{
|
||||
// check if groups exist
|
||||
var fi os.FileInfo
|
||||
if fi, err = os.Stat(groupsDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// create accounts directory
|
||||
if err = os.MkdirAll(groupsDir, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// create default accounts
|
||||
groups := []proto.Group{
|
||||
{Id: "34f38767-c937-4eb6-b847-1c175829a2a0", GidNumber: 15000, OnPremisesSamAccountName: "sysusers", DisplayName: "Technical users", Description: "A group for technical users. They should not show up in sharing dialogs.", Members: []*proto.Account{
|
||||
{Id: "820ba2a1-3f54-4538-80a4-2d73007e30bf"}, // konnectd
|
||||
{Id: "bc596f3c-c955-4328-80a0-60d018b4ad57"}, // reva
|
||||
}},
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa", GidNumber: 30000, OnPremisesSamAccountName: "users", DisplayName: "Users", Description: "A group every normal user belongs to.", Members: []*proto.Account{
|
||||
{Id: "4c510ada-c86b-4815-8820-42cdf82c3d51"}, // einstein
|
||||
{Id: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}, // marie
|
||||
{Id: "932b4540-8d16-481e-8ef4-588e4b6b151c"}, // feynman
|
||||
}},
|
||||
{Id: "6040aa17-9c64-4fef-9bd0-77234d71bad0", GidNumber: 30001, OnPremisesSamAccountName: "sailing-lovers", DisplayName: "Sailing lovers", Members: []*proto.Account{
|
||||
{Id: "4c510ada-c86b-4815-8820-42cdf82c3d51"}, // einstein
|
||||
}},
|
||||
{Id: "dd58e5ec-842e-498b-8800-61f2ec6f911f", GidNumber: 30002, OnPremisesSamAccountName: "violin-haters", DisplayName: "Violin haters", Members: []*proto.Account{
|
||||
{Id: "4c510ada-c86b-4815-8820-42cdf82c3d51"}, // einstein
|
||||
}},
|
||||
{Id: "7b87fd49-286e-4a5f-bafd-c535d5dd997a", GidNumber: 30003, OnPremisesSamAccountName: "radium-lovers", DisplayName: "Radium lovers", Members: []*proto.Account{
|
||||
{Id: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}, // marie
|
||||
}},
|
||||
{Id: "cedc21aa-4072-4614-8676-fa9165f598ff", GidNumber: 30004, OnPremisesSamAccountName: "polonium-lovers", DisplayName: "Polonium lovers", Members: []*proto.Account{
|
||||
{Id: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}, // marie
|
||||
}},
|
||||
{Id: "a1726108-01f8-4c30-88df-2b1a9d1cba1a", GidNumber: 30005, OnPremisesSamAccountName: "quantum-lovers", DisplayName: "Quantum lovers", Members: []*proto.Account{
|
||||
{Id: "932b4540-8d16-481e-8ef4-588e4b6b151c"}, // feynman
|
||||
}},
|
||||
{Id: "167cbee2-0518-455a-bfb2-031fe0621e5d", GidNumber: 30006, OnPremisesSamAccountName: "philosophy-haters", DisplayName: "Philosophy haters", Members: []*proto.Account{
|
||||
{Id: "932b4540-8d16-481e-8ef4-588e4b6b151c"}, // feynman
|
||||
}},
|
||||
{Id: "262982c1-2362-4afa-bfdf-8cbfef64a06e", GidNumber: 30007, OnPremisesSamAccountName: "physics-lovers", DisplayName: "Physics lovers", Members: []*proto.Account{
|
||||
{Id: "4c510ada-c86b-4815-8820-42cdf82c3d51"}, // einstein
|
||||
{Id: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}, // marie
|
||||
{Id: "932b4540-8d16-481e-8ef4-588e4b6b151c"}, // feynman
|
||||
}},
|
||||
}
|
||||
for i := range groups {
|
||||
var bytes []byte
|
||||
if bytes, err = json.Marshal(&groups[i]); err != nil {
|
||||
logger.Error().Err(err).Interface("group", &groups[i]).Msg("could not marshal default group")
|
||||
return
|
||||
}
|
||||
path := filepath.Join(groupsDir, groups[i].Id)
|
||||
if err = ioutil.WriteFile(path, bytes, 0600); err != nil {
|
||||
logger.Error().Err(err).Str("path", path).Interface("group", &groups[i]).Msg("could not persist default group")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !fi.IsDir() {
|
||||
return nil, fmt.Errorf("%s is not a directory", groupsDir)
|
||||
}
|
||||
if err = s.createDefaultGroups(groupsDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO watch folders for new records
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s Service) buildIndex() (index bleve.Index, err error) {
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
// keep all symbols in terms to allow exact maching, eg. emails
|
||||
indexMapping.DefaultAnalyzer = keyword.Name
|
||||
@@ -282,7 +111,7 @@ func New(opts ...Option) (s *Service, err error) {
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
lowercaseTextFieldMapping := bleve.NewTextFieldMapping()
|
||||
lowercaseTextFieldMapping.Analyzer = "lowercase"
|
||||
@@ -321,34 +150,244 @@ func New(opts ...Option) (s *Service, err error) {
|
||||
// documents in the index by querying for that property.
|
||||
indexMapping.TypeField = "BleveType"
|
||||
|
||||
s = &Service{
|
||||
id: cfg.GRPC.Namespace + "." + cfg.Server.Name,
|
||||
log: logger,
|
||||
Config: cfg,
|
||||
RoleService: roleService,
|
||||
RoleManager: roleManager,
|
||||
}
|
||||
|
||||
indexDir := filepath.Join(cfg.Server.AccountsDataPath, "index.bleve")
|
||||
indexDir := filepath.Join(s.Config.Server.AccountsDataPath, "index.bleve")
|
||||
// for now recreate index on every start
|
||||
if err = os.RemoveAll(indexDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.index, err = bleve.New(indexDir, indexMapping); err != nil {
|
||||
return
|
||||
}
|
||||
if err = s.indexAccounts(accountsDir); err != nil {
|
||||
if index, err = bleve.New(indexDir, indexMapping); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = s.indexGroups(groupsDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO watch folders for new records
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s Service) createDefaultAccounts(accountsDir string) (err error) {
|
||||
// check if accounts exist
|
||||
var fi os.FileInfo
|
||||
if fi, err = os.Stat(accountsDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// create accounts directory
|
||||
if err = os.MkdirAll(accountsDir, 0700); err != nil {
|
||||
return
|
||||
}
|
||||
// create default accounts
|
||||
accounts := []proto.Account{
|
||||
{
|
||||
Id: "4c510ada-c86b-4815-8820-42cdf82c3d51",
|
||||
PreferredName: "einstein",
|
||||
OnPremisesSamAccountName: "einstein",
|
||||
Mail: "einstein@example.org",
|
||||
DisplayName: "Albert Einstein",
|
||||
UidNumber: 20000,
|
||||
GidNumber: 30000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=35210$sa1u5Pmfo4cr23Vw$RJNGElaDB1D3xorWkfTEGm2Ko.o2QL3E0cimKx23MNxVWVFSkUUeRoC7FqC4RzYDNQBD6cKzovTEaDD.8TDkD.",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
{Id: "6040aa17-9c64-4fef-9bd0-77234d71bad0"}, // sailing-lovers
|
||||
{Id: "dd58e5ec-842e-498b-8800-61f2ec6f911f"}, // violin-haters
|
||||
{Id: "262982c1-2362-4afa-bfdf-8cbfef64a06e"}, // physics-lovers
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c",
|
||||
PreferredName: "marie",
|
||||
OnPremisesSamAccountName: "marie",
|
||||
Mail: "marie@example.org",
|
||||
DisplayName: "Marie Curie",
|
||||
UidNumber: 20001,
|
||||
GidNumber: 30000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=81434$sa1u5Pmfo4cr23Vw$W78cyL884GmuvDpxYPvSRBVzEj02T5QhTTcI8Dv4IKvMooDFGv4bwaWMkH9HfJ0wgpEBW7Lp.4Cad0xE/MYSg1",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
{Id: "7b87fd49-286e-4a5f-bafd-c535d5dd997a"}, // radium-lovers
|
||||
{Id: "cedc21aa-4072-4614-8676-fa9165f598ff"}, // polonium-lovers
|
||||
{Id: "262982c1-2362-4afa-bfdf-8cbfef64a06e"}, // physics-lovers
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "932b4540-8d16-481e-8ef4-588e4b6b151c",
|
||||
PreferredName: "richard",
|
||||
OnPremisesSamAccountName: "richard",
|
||||
Mail: "richard@example.org",
|
||||
DisplayName: "Richard Feynman",
|
||||
UidNumber: 20002,
|
||||
GidNumber: 30000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=5524$sa1u5Pmfo4cr23Vw$58bQVL/JeUlwM0RY21YKAFMvKvwKLLysGllYXox.vwKT5dHMwdzJjCxwTDMnB2o2pwexC8o/iOXyP2zrhALS40",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
{Id: "a1726108-01f8-4c30-88df-2b1a9d1cba1a"}, // quantum-lovers
|
||||
{Id: "167cbee2-0518-455a-bfb2-031fe0621e5d"}, // philosophy-haters
|
||||
{Id: "262982c1-2362-4afa-bfdf-8cbfef64a06e"}, // physics-lovers
|
||||
},
|
||||
},
|
||||
// admin user(s)
|
||||
{
|
||||
Id: "058bff95-6708-4fe5-91e4-9ea3d377588b",
|
||||
PreferredName: "moss",
|
||||
OnPremisesSamAccountName: "moss",
|
||||
Mail: "moss@example.org",
|
||||
DisplayName: "Maurice Moss",
|
||||
UidNumber: 20003,
|
||||
GidNumber: 30000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=47068$lhw6odzXW0LTk/ao$GgxS.pIgP8jawLJBAiyNor2FrWzrULF95PwspRkli2W3VF.4HEwTYlQfRXbNQBMjNCEcEYlgZo3a.kRz2k2N0/",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
},
|
||||
},
|
||||
// technical users for kopano and reva
|
||||
{
|
||||
Id: "820ba2a1-3f54-4538-80a4-2d73007e30bf",
|
||||
PreferredName: "konnectd",
|
||||
OnPremisesSamAccountName: "konnectd",
|
||||
Mail: "idp@example.org",
|
||||
DisplayName: "Kopano Konnectd",
|
||||
UidNumber: 10000,
|
||||
GidNumber: 15000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=9746$sa1u5Pmfo4cr23Vw$2hnwpkTvUkWX0v6mh8Aw1pbzEXa9EUJzmrey4g2W/8arwWCwhteqU//3aWnA3S0d5T21fOKYteoqlsN1IbTcN.",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "34f38767-c937-4eb6-b847-1c175829a2a0"}, // sysusers
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "bc596f3c-c955-4328-80a0-60d018b4ad57",
|
||||
PreferredName: "reva",
|
||||
OnPremisesSamAccountName: "reva",
|
||||
Mail: "storage@example.org",
|
||||
DisplayName: "Reva Inter Operability Platform",
|
||||
UidNumber: 10001,
|
||||
GidNumber: 15000,
|
||||
PasswordProfile: &proto.PasswordProfile{
|
||||
Password: "$6$rounds=91087$sa1u5Pmfo4cr23Vw$wPC3BbMTbP/ytlo0p.f99zJifyO70AUCdKIK9hkhwutBKGCirLmZs/MsWAG6xHjVvmnmHN5NoON7FUGv5pPaN.",
|
||||
},
|
||||
AccountEnabled: true,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "34f38767-c937-4eb6-b847-1c175829a2a0"}, // sysusers
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := range accounts {
|
||||
// create account on disk
|
||||
var bytes []byte
|
||||
if bytes, err = json.Marshal(&accounts[i]); err != nil {
|
||||
s.log.Error().Err(err).Interface("account", &accounts[i]).Msg("could not marshal default account")
|
||||
return
|
||||
}
|
||||
path := filepath.Join(accountsDir, accounts[i].Id)
|
||||
if err = ioutil.WriteFile(path, bytes, 0600); err != nil {
|
||||
accounts[i].PasswordProfile.Password = "***REMOVED***"
|
||||
s.log.Error().Err(err).Str("path", path).Interface("account", &accounts[i]).Msg("could not persist default account")
|
||||
return
|
||||
}
|
||||
if err = s.indexAccount(accounts[i].Id); err != nil {
|
||||
accounts[i].PasswordProfile.Password = "***REMOVED***"
|
||||
s.log.Error().Err(err).Str("path", path).Interface("account", &accounts[i]).Msg("could not index default account")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// set role for admin users and regular users
|
||||
assignRoleToUser("058bff95-6708-4fe5-91e4-9ea3d377588b", settings_svc.BundleUUIDRoleAdmin, s.RoleService, s.log)
|
||||
for _, accountID := range []string{
|
||||
"058bff95-6708-4fe5-91e4-9ea3d377588b", //moss
|
||||
} {
|
||||
assignRoleToUser(accountID, settings_svc.BundleUUIDRoleAdmin, s.RoleService, s.log)
|
||||
}
|
||||
for _, accountID := range []string{
|
||||
"4c510ada-c86b-4815-8820-42cdf82c3d51", //einstein
|
||||
"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", //marie
|
||||
"932b4540-8d16-481e-8ef4-588e4b6b151c", //richard
|
||||
} {
|
||||
assignRoleToUser(accountID, settings_svc.BundleUUIDRoleUser, s.RoleService, s.log)
|
||||
}
|
||||
}
|
||||
} else if !fi.IsDir() {
|
||||
return fmt.Errorf("%s is not a directory", accountsDir)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Service) createDefaultGroups(groupsDir string) (err error) {
|
||||
// check if groups exist
|
||||
var fi os.FileInfo
|
||||
if fi, err = os.Stat(groupsDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// create accounts directory
|
||||
if err = os.MkdirAll(groupsDir, 0700); err != nil {
|
||||
return
|
||||
}
|
||||
// create default accounts
|
||||
groups := []proto.Group{
|
||||
{Id: "34f38767-c937-4eb6-b847-1c175829a2a0", GidNumber: 15000, OnPremisesSamAccountName: "sysusers", DisplayName: "Technical users", Description: "A group for technical users. They should not show up in sharing dialogs.", Members: []*proto.Account{
|
||||
{Id: "820ba2a1-3f54-4538-80a4-2d73007e30bf"}, // konnectd
|
||||
{Id: "bc596f3c-c955-4328-80a0-60d018b4ad57"}, // reva
|
||||
}},
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa", GidNumber: 30000, OnPremisesSamAccountName: "users", DisplayName: "Users", Description: "A group every normal user belongs to.", Members: []*proto.Account{
|
||||
{Id: "4c510ada-c86b-4815-8820-42cdf82c3d51"}, // einstein
|
||||
{Id: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}, // marie
|
||||
{Id: "932b4540-8d16-481e-8ef4-588e4b6b151c"}, // feynman
|
||||
}},
|
||||
{Id: "6040aa17-9c64-4fef-9bd0-77234d71bad0", GidNumber: 30001, OnPremisesSamAccountName: "sailing-lovers", DisplayName: "Sailing lovers", Members: []*proto.Account{
|
||||
{Id: "4c510ada-c86b-4815-8820-42cdf82c3d51"}, // einstein
|
||||
}},
|
||||
{Id: "dd58e5ec-842e-498b-8800-61f2ec6f911f", GidNumber: 30002, OnPremisesSamAccountName: "violin-haters", DisplayName: "Violin haters", Members: []*proto.Account{
|
||||
{Id: "4c510ada-c86b-4815-8820-42cdf82c3d51"}, // einstein
|
||||
}},
|
||||
{Id: "7b87fd49-286e-4a5f-bafd-c535d5dd997a", GidNumber: 30003, OnPremisesSamAccountName: "radium-lovers", DisplayName: "Radium lovers", Members: []*proto.Account{
|
||||
{Id: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}, // marie
|
||||
}},
|
||||
{Id: "cedc21aa-4072-4614-8676-fa9165f598ff", GidNumber: 30004, OnPremisesSamAccountName: "polonium-lovers", DisplayName: "Polonium lovers", Members: []*proto.Account{
|
||||
{Id: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}, // marie
|
||||
}},
|
||||
{Id: "a1726108-01f8-4c30-88df-2b1a9d1cba1a", GidNumber: 30005, OnPremisesSamAccountName: "quantum-lovers", DisplayName: "Quantum lovers", Members: []*proto.Account{
|
||||
{Id: "932b4540-8d16-481e-8ef4-588e4b6b151c"}, // feynman
|
||||
}},
|
||||
{Id: "167cbee2-0518-455a-bfb2-031fe0621e5d", GidNumber: 30006, OnPremisesSamAccountName: "philosophy-haters", DisplayName: "Philosophy haters", Members: []*proto.Account{
|
||||
{Id: "932b4540-8d16-481e-8ef4-588e4b6b151c"}, // feynman
|
||||
}},
|
||||
{Id: "262982c1-2362-4afa-bfdf-8cbfef64a06e", GidNumber: 30007, OnPremisesSamAccountName: "physics-lovers", DisplayName: "Physics lovers", Members: []*proto.Account{
|
||||
{Id: "4c510ada-c86b-4815-8820-42cdf82c3d51"}, // einstein
|
||||
{Id: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}, // marie
|
||||
{Id: "932b4540-8d16-481e-8ef4-588e4b6b151c"}, // feynman
|
||||
}},
|
||||
}
|
||||
for i := range groups {
|
||||
var bytes []byte
|
||||
if bytes, err = json.Marshal(&groups[i]); err != nil {
|
||||
s.log.Error().Err(err).Interface("group", &groups[i]).Msg("could not marshal default group")
|
||||
return
|
||||
}
|
||||
path := filepath.Join(groupsDir, groups[i].Id)
|
||||
if err = ioutil.WriteFile(path, bytes, 0600); err != nil {
|
||||
s.log.Error().Err(err).Str("path", path).Interface("group", &groups[i]).Msg("could not persist default group")
|
||||
return
|
||||
}
|
||||
if err = s.indexGroup(groups[i].Id); err != nil {
|
||||
s.log.Error().Err(err).Str("path", path).Interface("group", &groups[i]).Msg("could not index default group")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !fi.IsDir() {
|
||||
return fmt.Errorf("%s is not a directory", groupsDir)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func assignRoleToUser(accountID, roleID string, rs settings.RoleService, logger log.Logger) (ok bool) {
|
||||
_, err := rs.AssignRoleToUser(context.Background(), &settings.AssignRoleToUserRequest{
|
||||
AccountUuid: accountID,
|
||||
|
||||
Reference in New Issue
Block a user