mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-30 17:00:57 -06:00
use service accounts for notifications
Signed-off-by: jkoberg <jkoberg@owncloud.com>
This commit is contained in:
@@ -116,7 +116,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
logger.Fatal().Err(err).Str("addr", cfg.Notifications.RevaGateway).Msg("could not get reva gateway selector")
|
||||
}
|
||||
valueService := settingssvc.NewValueService("com.owncloud.api.settings", grpcClient)
|
||||
svc := service.NewEventsNotifier(evts, channel, logger, gatewaySelector, valueService, cfg.Notifications.MachineAuthAPIKey, cfg.Notifications.EmailTemplatePath, cfg.WebUIURL)
|
||||
svc := service.NewEventsNotifier(evts, channel, logger, gatewaySelector, valueService, cfg.ServiceAccount.ServiceAccountID, cfg.ServiceAccount.ServiceAccountSecret, cfg.Notifications.EmailTemplatePath, cfg.WebUIURL)
|
||||
|
||||
gr.Add(svc.Run, func(error) {
|
||||
cancel()
|
||||
|
||||
@@ -18,8 +18,9 @@ type Config struct {
|
||||
|
||||
WebUIURL string `yaml:"ocis_url" env:"OCIS_URL;NOTIFICATIONS_WEB_UI_URL" desc:"The public facing URL of the oCIS Web UI, used e.g. when sending notification eMails"`
|
||||
|
||||
Notifications Notifications `yaml:"notifications"`
|
||||
GRPCClientTLS shared.GRPCClientTLS `yaml:"grpc_client_tls"`
|
||||
Notifications Notifications `yaml:"notifications"`
|
||||
GRPCClientTLS shared.GRPCClientTLS `yaml:"grpc_client_tls"`
|
||||
ServiceAccount ServiceAccount `yaml:"service_account"`
|
||||
|
||||
Context context.Context `yaml:"-"`
|
||||
}
|
||||
@@ -28,7 +29,6 @@ type Config struct {
|
||||
type Notifications struct {
|
||||
SMTP SMTP `yaml:"SMTP"`
|
||||
Events Events `yaml:"events"`
|
||||
MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;NOTIFICATIONS_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."`
|
||||
EmailTemplatePath string `yaml:"email_template_path" env:"OCIS_EMAIL_TEMPLATE_PATH;NOTIFICATIONS_EMAIL_TEMPLATE_PATH" desc:"Path to Email notification templates overriding embedded ones."`
|
||||
TranslationPath string `yaml:"translation_path" env:"OCIS_TRANSLATION_PATH,NOTIFICATIONS_TRANSLATION_PATH" desc:"(optional) Set this to a path with custom translations to overwrite the builtin translations. Note that file and folder naming rules apply, see the documentation for more details."`
|
||||
RevaGateway string `yaml:"reva_gateway" env:"OCIS_REVA_GATEWAY" desc:"CS3 gateway used to look up user metadata"`
|
||||
@@ -55,3 +55,9 @@ type Events struct {
|
||||
TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;NOTIFICATIONS_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false."`
|
||||
EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;NOTIFICATIONS_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."`
|
||||
}
|
||||
|
||||
// ServiceAccount is the configuration for the used service account
|
||||
type ServiceAccount struct {
|
||||
ServiceAccountID string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;NOTIFICATIONS_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details."`
|
||||
ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;NOTIFICATIONS_SERVICE_ACCOUNT_SECRET" desc:"The service account secret."`
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ func DefaultConfig() *config.Config {
|
||||
},
|
||||
RevaGateway: shared.DefaultRevaConfig().Address,
|
||||
},
|
||||
ServiceAccount: config.ServiceAccount{
|
||||
ServiceAccountID: "service-user-id",
|
||||
ServiceAccountSecret: "secret-string",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,9 +77,6 @@ func EnsureDefaults(cfg *config.Config) {
|
||||
cfg.Tracing = &config.Tracing{}
|
||||
}
|
||||
|
||||
if cfg.Notifications.MachineAuthAPIKey == "" && cfg.Commons != nil && cfg.Commons.MachineAuthAPIKey != "" {
|
||||
cfg.Notifications.MachineAuthAPIKey = cfg.Commons.MachineAuthAPIKey
|
||||
}
|
||||
if cfg.Notifications.GRPCClientTLS == nil && cfg.Commons != nil {
|
||||
cfg.Notifications.GRPCClientTLS = structs.CopyOrZeroValue(cfg.Commons.GRPCClientTLS)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
|
||||
ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
||||
"github.com/owncloud/ocis/v2/services/notifications/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/services/notifications/pkg/config/defaults"
|
||||
|
||||
@@ -34,9 +33,5 @@ func ParseConfig(cfg *config.Config) error {
|
||||
}
|
||||
|
||||
func Validate(cfg *config.Config) error {
|
||||
if cfg.Notifications.MachineAuthAPIKey == "" {
|
||||
return shared.MissingMachineAuthApiKeyError(cfg.Service.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -42,32 +42,34 @@ func NewEventsNotifier(
|
||||
logger log.Logger,
|
||||
gatewaySelector pool.Selectable[gateway.GatewayAPIClient],
|
||||
valueService settingssvc.ValueService,
|
||||
machineAuthAPIKey, emailTemplatePath, ocisURL string) Service {
|
||||
serviceAccountID, serviceAccountSecret, emailTemplatePath, ocisURL string) Service {
|
||||
|
||||
return eventsNotifier{
|
||||
logger: logger,
|
||||
channel: channel,
|
||||
events: events,
|
||||
signals: make(chan os.Signal, 1),
|
||||
gatewaySelector: gatewaySelector,
|
||||
valueService: valueService,
|
||||
machineAuthAPIKey: machineAuthAPIKey,
|
||||
emailTemplatePath: emailTemplatePath,
|
||||
ocisURL: ocisURL,
|
||||
logger: logger,
|
||||
channel: channel,
|
||||
events: events,
|
||||
signals: make(chan os.Signal, 1),
|
||||
gatewaySelector: gatewaySelector,
|
||||
valueService: valueService,
|
||||
serviceAccountID: serviceAccountID,
|
||||
serviceAccountSecret: serviceAccountSecret,
|
||||
emailTemplatePath: emailTemplatePath,
|
||||
ocisURL: ocisURL,
|
||||
}
|
||||
}
|
||||
|
||||
type eventsNotifier struct {
|
||||
logger log.Logger
|
||||
channel channels.Channel
|
||||
events <-chan events.Event
|
||||
signals chan os.Signal
|
||||
gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
|
||||
valueService settingssvc.ValueService
|
||||
machineAuthAPIKey string
|
||||
emailTemplatePath string
|
||||
translationPath string
|
||||
ocisURL string
|
||||
logger log.Logger
|
||||
channel channels.Channel
|
||||
events <-chan events.Event
|
||||
signals chan os.Signal
|
||||
gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
|
||||
valueService settingssvc.ValueService
|
||||
emailTemplatePath string
|
||||
translationPath string
|
||||
ocisURL string
|
||||
serviceAccountID string
|
||||
serviceAccountSecret string
|
||||
}
|
||||
|
||||
func (s eventsNotifier) Run() error {
|
||||
|
||||
@@ -77,7 +77,7 @@ var _ = Describe("Notifications", func() {
|
||||
cfg := defaults.FullDefaultConfig()
|
||||
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
|
||||
ch := make(chan events.Event)
|
||||
evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gatewaySelector, vs, "", "", "")
|
||||
evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gatewaySelector, vs, "", "", "", "")
|
||||
go evts.Run()
|
||||
|
||||
ch <- ev
|
||||
@@ -275,7 +275,7 @@ var _ = Describe("Notifications X-Site Scripting", func() {
|
||||
cfg := defaults.FullDefaultConfig()
|
||||
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
|
||||
ch := make(chan events.Event)
|
||||
evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gatewaySelector, vs, "", "", "")
|
||||
evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gatewaySelector, vs, "", "", "", "")
|
||||
go evts.Run()
|
||||
|
||||
ch <- ev
|
||||
|
||||
@@ -19,13 +19,13 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
|
||||
return
|
||||
}
|
||||
|
||||
ownerCtx, owner, err := utils.Impersonate(e.Sharer, gatewayClient, s.machineAuthAPIKey)
|
||||
ctx, err := utils.GetServiceUserContext(s.serviceAccountID, gatewayClient, s.serviceAccountSecret)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Could not impersonate sharer")
|
||||
logger.Error().Err(err).Msg("Could not impersonate service user")
|
||||
return
|
||||
}
|
||||
|
||||
resourceInfo, err := s.getResourceInfo(ownerCtx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
|
||||
resourceInfo, err := s.getResourceInfo(ctx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
@@ -41,13 +41,19 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
|
||||
return
|
||||
}
|
||||
|
||||
granteeList := s.ensureGranteeList(ownerCtx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
|
||||
owner, err := utils.GetUser(e.Sharer, gatewayClient)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Could not get user")
|
||||
return
|
||||
}
|
||||
|
||||
granteeList := s.ensureGranteeList(ctx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
|
||||
if granteeList == nil {
|
||||
return
|
||||
}
|
||||
|
||||
sharerDisplayName := owner.GetDisplayName()
|
||||
recipientList, err := s.render(ownerCtx, email.ShareCreated,
|
||||
recipientList, err := s.render(ctx, email.ShareCreated,
|
||||
"ShareGrantee",
|
||||
map[string]string{
|
||||
"ShareSharer": sharerDisplayName,
|
||||
@@ -58,7 +64,7 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
|
||||
s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("could not get render the email")
|
||||
return
|
||||
}
|
||||
s.send(ownerCtx, recipientList)
|
||||
s.send(ctx, recipientList)
|
||||
}
|
||||
|
||||
func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
|
||||
@@ -73,13 +79,13 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
|
||||
return
|
||||
}
|
||||
|
||||
ownerCtx, owner, err := utils.Impersonate(e.ShareOwner, gatewayClient, s.machineAuthAPIKey)
|
||||
ctx, err := utils.GetServiceUserContext(s.serviceAccountID, gatewayClient, s.serviceAccountSecret)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Could not impersonate sharer")
|
||||
return
|
||||
}
|
||||
|
||||
resourceInfo, err := s.getResourceInfo(ownerCtx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
|
||||
resourceInfo, err := s.getResourceInfo(ctx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
@@ -87,12 +93,18 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
|
||||
return
|
||||
}
|
||||
|
||||
granteeList := s.ensureGranteeList(ownerCtx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
|
||||
owner, err := utils.GetUser(e.ShareOwner, gatewayClient)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Could not get user")
|
||||
return
|
||||
}
|
||||
|
||||
granteeList := s.ensureGranteeList(ctx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
|
||||
if granteeList == nil {
|
||||
return
|
||||
}
|
||||
|
||||
recipientList, err := s.render(ownerCtx, email.ShareExpired,
|
||||
recipientList, err := s.render(ctx, email.ShareExpired,
|
||||
"ShareGrantee",
|
||||
map[string]string{
|
||||
"ShareFolder": resourceInfo.GetName(),
|
||||
@@ -102,5 +114,5 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
|
||||
s.logger.Error().Err(err).Str("event", "ShareExpired").Msg("could not get render the email")
|
||||
return
|
||||
}
|
||||
s.send(ownerCtx, recipientList)
|
||||
s.send(ctx, recipientList)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
|
||||
return
|
||||
}
|
||||
|
||||
executantCtx, executant, err := utils.Impersonate(e.Executant, gatewayClient, s.machineAuthAPIKey)
|
||||
ctx, err := utils.GetServiceUserContext(s.serviceAccountID, gatewayClient, s.serviceAccountSecret)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
@@ -35,7 +35,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
|
||||
return
|
||||
}
|
||||
|
||||
resourceInfo, err := s.getResourceInfo(executantCtx, &resourceID, nil)
|
||||
resourceInfo, err := s.getResourceInfo(ctx, &resourceID, nil)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
@@ -51,16 +51,24 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
|
||||
return
|
||||
}
|
||||
|
||||
executant, err := utils.GetUser(e.Executant, gatewayClient)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Msg("could not get user")
|
||||
return
|
||||
}
|
||||
|
||||
// Note: We're using the 'executantCtx' (authenticated as the share executant) here for requesting
|
||||
// the Grantees of the shares. Ideally the notfication service would use some kind of service
|
||||
// user for this.
|
||||
granteeList := s.ensureGranteeList(executantCtx, executant.GetId(), e.GranteeUserID, e.GranteeGroupID)
|
||||
granteeList := s.ensureGranteeList(ctx, executant.GetId(), e.GranteeUserID, e.GranteeGroupID)
|
||||
if granteeList == nil {
|
||||
return
|
||||
}
|
||||
|
||||
sharerDisplayName := executant.GetDisplayName()
|
||||
recipientList, err := s.render(executantCtx, email.SharedSpace,
|
||||
recipientList, err := s.render(ctx, email.SharedSpace,
|
||||
"SpaceGrantee",
|
||||
map[string]string{
|
||||
"SpaceSharer": sharerDisplayName,
|
||||
@@ -71,7 +79,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
|
||||
s.logger.Error().Err(err).Str("event", "SharedSpace").Msg("could not get render the email")
|
||||
return
|
||||
}
|
||||
s.send(executantCtx, recipientList)
|
||||
s.send(ctx, recipientList)
|
||||
}
|
||||
|
||||
func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
|
||||
@@ -86,7 +94,7 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
|
||||
return
|
||||
}
|
||||
|
||||
executantCtx, executant, err := utils.Impersonate(e.Executant, gatewayClient, s.machineAuthAPIKey)
|
||||
ctx, err := utils.GetServiceUserContext(s.serviceAccountID, gatewayClient, s.serviceAccountSecret)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not handle space unshared event")
|
||||
return
|
||||
@@ -100,7 +108,7 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
|
||||
return
|
||||
}
|
||||
|
||||
resourceInfo, err := s.getResourceInfo(executantCtx, &resourceID, nil)
|
||||
resourceInfo, err := s.getResourceInfo(ctx, &resourceID, nil)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
@@ -116,16 +124,24 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
|
||||
return
|
||||
}
|
||||
|
||||
executant, err := utils.GetUser(e.Executant, gatewayClient)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Msg("could not get user")
|
||||
return
|
||||
}
|
||||
|
||||
// Note: We're using the 'executantCtx' (authenticated as the share executant) here for requesting
|
||||
// the Grantees of the shares. Ideally the notfication service would use some kind of service
|
||||
// user for this.
|
||||
granteeList := s.ensureGranteeList(executantCtx, executant.GetId(), e.GranteeUserID, e.GranteeGroupID)
|
||||
granteeList := s.ensureGranteeList(ctx, executant.GetId(), e.GranteeUserID, e.GranteeGroupID)
|
||||
if granteeList == nil {
|
||||
return
|
||||
}
|
||||
|
||||
sharerDisplayName := executant.GetDisplayName()
|
||||
recipientList, err := s.render(executantCtx, email.UnsharedSpace,
|
||||
recipientList, err := s.render(ctx, email.UnsharedSpace,
|
||||
"SpaceGrantee",
|
||||
map[string]string{
|
||||
"SpaceSharer": sharerDisplayName,
|
||||
@@ -136,7 +152,7 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
|
||||
s.logger.Error().Err(err).Str("event", "UnsharedSpace").Msg("Could not get render the email")
|
||||
return
|
||||
}
|
||||
s.send(executantCtx, recipientList)
|
||||
s.send(ctx, recipientList)
|
||||
}
|
||||
|
||||
func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExpired) {
|
||||
@@ -151,18 +167,26 @@ func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExp
|
||||
return
|
||||
}
|
||||
|
||||
ownerCtx, owner, err := utils.Impersonate(e.SpaceOwner, gatewayClient, s.machineAuthAPIKey)
|
||||
ctx, err := utils.GetServiceUserContext(s.serviceAccountID, gatewayClient, s.serviceAccountSecret)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Could not impersonate sharer")
|
||||
return
|
||||
}
|
||||
|
||||
granteeList := s.ensureGranteeList(ownerCtx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
|
||||
owner, err := utils.GetUser(e.SpaceOwner, gatewayClient)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Msg("could not get user")
|
||||
return
|
||||
}
|
||||
|
||||
granteeList := s.ensureGranteeList(ctx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
|
||||
if granteeList == nil {
|
||||
return
|
||||
}
|
||||
|
||||
recipientList, err := s.render(ownerCtx, email.MembershipExpired,
|
||||
recipientList, err := s.render(ctx, email.MembershipExpired,
|
||||
"SpaceGrantee",
|
||||
map[string]string{
|
||||
"SpaceName": e.SpaceName,
|
||||
@@ -172,5 +196,5 @@ func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExp
|
||||
s.logger.Error().Err(err).Str("event", "SpaceUnshared").Msg("could not get render the email")
|
||||
return
|
||||
}
|
||||
s.send(ownerCtx, recipientList)
|
||||
s.send(ctx, recipientList)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user