diff --git a/changelog/unreleased/single-service-handler.md b/changelog/unreleased/single-service-handler.md new file mode 100644 index 0000000000..3d59184649 --- /dev/null +++ b/changelog/unreleased/single-service-handler.md @@ -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 + diff --git a/pkg/command/server.go b/pkg/command/server.go index 4ea5298728..cea54cf3c4 100644 --- a/pkg/command/server.go +++ b/pkg/command/server.go @@ -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 { diff --git a/pkg/server/grpc/option.go b/pkg/server/grpc/option.go index 06e8538e9f..7e6a5a52ea 100644 --- a/pkg/server/grpc/option.go +++ b/pkg/server/grpc/option.go @@ -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 + } +} diff --git a/pkg/server/grpc/server.go b/pkg/server/grpc/server.go index 22717a3d8b..af5b0c602c 100644 --- a/pkg/server/grpc/server.go +++ b/pkg/server/grpc/server.go @@ -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") } diff --git a/pkg/server/http/option.go b/pkg/server/http/option.go index 3687b735bb..069b0136e6 100644 --- a/pkg/server/http/option.go +++ b/pkg/server/http/option.go @@ -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 + } +} diff --git a/pkg/server/http/server.go b/pkg/server/http/server.go index 64fef9dd0b..e931bcebcf 100644 --- a/pkg/server/http/server.go +++ b/pkg/server/http/server.go @@ -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) diff --git a/pkg/service/v0/accounts.go b/pkg/service/v0/accounts.go index 92cc2db22a..28e7489fe3 100644 --- a/pkg/service/v0/accounts.go +++ b/pkg/service/v0/accounts.go @@ -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 } diff --git a/pkg/service/v0/groups.go b/pkg/service/v0/groups.go index d06f338e5d..0e5f8a2530 100644 --- a/pkg/service/v0/groups.go +++ b/pkg/service/v0/groups.go @@ -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 diff --git a/pkg/service/v0/option.go b/pkg/service/v0/option.go index 559f992a2f..d9bda3879a 100644 --- a/pkg/service/v0/option.go +++ b/pkg/service/v0/option.go @@ -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 diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 1e967ff464..ceafad450e 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -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,