Add the backchannel logout event
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
Enhancement: Add the backchannel logout event
|
||||||
|
|
||||||
|
We've added the backchannel logout event
|
||||||
|
|
||||||
|
https://github.com/owncloud/ocis/pull/9447
|
||||||
|
https://github.com/owncloud/ocis/issues/9355
|
||||||
@@ -15,7 +15,7 @@ require (
|
|||||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||||
github.com/coreos/go-oidc/v3 v3.10.0
|
github.com/coreos/go-oidc/v3 v3.10.0
|
||||||
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781
|
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781
|
||||||
github.com/cs3org/reva/v2 v2.20.0
|
github.com/cs3org/reva/v2 v2.20.1-0.20240625085350-9351da5ba08f
|
||||||
github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25
|
github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25
|
||||||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
|
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
|
||||||
github.com/egirna/icap-client v0.1.1
|
github.com/egirna/icap-client v0.1.1
|
||||||
|
|||||||
@@ -1024,8 +1024,8 @@ github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c=
|
|||||||
github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME=
|
github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME=
|
||||||
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 h1:BUdwkIlf8IS2FasrrPg8gGPHQPOrQ18MS1Oew2tmGtY=
|
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 h1:BUdwkIlf8IS2FasrrPg8gGPHQPOrQ18MS1Oew2tmGtY=
|
||||||
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
|
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
|
||||||
github.com/cs3org/reva/v2 v2.20.0 h1:UzfWFEg1c3J78LuB1BbOCuWrl5MsieAV5hu55Jj4lW4=
|
github.com/cs3org/reva/v2 v2.20.1-0.20240625085350-9351da5ba08f h1:5oA/uUdun3s5t6l85ZzgaNwTJnLGd0qeU0By8PEKw/s=
|
||||||
github.com/cs3org/reva/v2 v2.20.0/go.mod h1:Rb2XnhpGKnH7k6WBFZlMygbyBxW6ma09Z4Uk+ro0v+A=
|
github.com/cs3org/reva/v2 v2.20.1-0.20240625085350-9351da5ba08f/go.mod h1:Rb2XnhpGKnH7k6WBFZlMygbyBxW6ma09Z4Uk+ro0v+A=
|
||||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||||
|
|||||||
@@ -39,5 +39,6 @@ func RegisteredEvents() []events.Unmarshaller {
|
|||||||
events.GroupDeleted{},
|
events.GroupDeleted{},
|
||||||
events.GroupMemberAdded{},
|
events.GroupMemberAdded{},
|
||||||
events.GroupMemberRemoved{},
|
events.GroupMemberRemoved{},
|
||||||
|
events.BackchannelLogout{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ var _registeredEvents = []events.Unmarshaller{
|
|||||||
events.LinkCreated{},
|
events.LinkCreated{},
|
||||||
events.LinkUpdated{},
|
events.LinkUpdated{},
|
||||||
events.LinkRemoved{},
|
events.LinkRemoved{},
|
||||||
|
events.BackchannelLogout{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server is the entrypoint for the server command.
|
// Server is the entrypoint for the server command.
|
||||||
|
|||||||
@@ -11,3 +11,9 @@ type FileEvent struct {
|
|||||||
// Only in case of sharing (refactor this into separate struct when more fields are needed)
|
// Only in case of sharing (refactor this into separate struct when more fields are needed)
|
||||||
AffectedUserIDs []string `json:"affecteduserids"`
|
AffectedUserIDs []string `json:"affecteduserids"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackchannelLogout is emitted when the callback revived from the identity provider
|
||||||
|
type BackchannelLogout struct {
|
||||||
|
UserID string `json:"userid"`
|
||||||
|
Timestamp string `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -159,6 +159,8 @@ func (cl *ClientlogService) processEvent(event events.Event) {
|
|||||||
fileEv("link-updated", &provider.Reference{ResourceId: e.ItemID})
|
fileEv("link-updated", &provider.Reference{ResourceId: e.ItemID})
|
||||||
case events.LinkRemoved:
|
case events.LinkRemoved:
|
||||||
fileEv("link-removed", &provider.Reference{ResourceId: e.ItemID})
|
fileEv("link-removed", &provider.Reference{ResourceId: e.ItemID})
|
||||||
|
case events.BackchannelLogout:
|
||||||
|
evType, users, data = backchannelLogoutEvent(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -287,3 +289,10 @@ func isRename(o, n *provider.Reference) bool {
|
|||||||
}
|
}
|
||||||
return filepath.Base(o.GetPath()) != filepath.Base(n.GetPath())
|
return filepath.Base(o.GetPath()) != filepath.Base(n.GetPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func backchannelLogoutEvent(e events.BackchannelLogout) (string, []string, BackchannelLogout) {
|
||||||
|
return "backchannel-logout", []string{e.Executant.GetOpaqueId()}, BackchannelLogout{
|
||||||
|
UserID: e.Executant.GetOpaqueId(),
|
||||||
|
Timestamp: e.Timestamp.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||||
|
"github.com/cs3org/reva/v2/pkg/events"
|
||||||
|
"github.com/cs3org/reva/v2/pkg/events/stream"
|
||||||
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
||||||
"github.com/cs3org/reva/v2/pkg/store"
|
"github.com/cs3org/reva/v2/pkg/store"
|
||||||
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
||||||
@@ -132,22 +135,65 @@ func Server(cfg *config.Config) *cli.Command {
|
|||||||
proxy.Logger(logger),
|
proxy.Logger(logger),
|
||||||
proxy.Config(cfg),
|
proxy.Config(cfg),
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize reverse proxy: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gatewaySelector, err := pool.GatewaySelector(
|
||||||
|
cfg.Reva.Address,
|
||||||
|
append(
|
||||||
|
cfg.Reva.GetRevaOptions(),
|
||||||
|
pool.WithRegistry(registry.GetRegistry()),
|
||||||
|
pool.WithTracerProvider(traceProvider),
|
||||||
|
)...)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal().Err(err).Msg("Failed to get gateway selector")
|
||||||
|
}
|
||||||
|
|
||||||
|
var userProvider backend.UserBackend
|
||||||
|
switch cfg.AccountBackend {
|
||||||
|
case "cs3":
|
||||||
|
userProvider = backend.NewCS3UserBackend(
|
||||||
|
backend.WithLogger(logger),
|
||||||
|
backend.WithRevaGatewaySelector(gatewaySelector),
|
||||||
|
backend.WithMachineAuthAPIKey(cfg.MachineAuthAPIKey),
|
||||||
|
backend.WithOIDCissuer(cfg.OIDC.Issuer),
|
||||||
|
backend.WithServiceAccount(cfg.ServiceAccount),
|
||||||
|
backend.WithAutoProvisionClaims(cfg.AutoProvisionClaims),
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
logger.Fatal().Msgf("Invalid accounts backend type '%s'", cfg.AccountBackend)
|
||||||
|
}
|
||||||
|
|
||||||
|
var publisher events.Stream
|
||||||
|
if cfg.Events.Endpoint != "" {
|
||||||
|
var err error
|
||||||
|
publisher, err = stream.NatsFromConfig(cfg.Service.Name, false, stream.NatsConfig(cfg.Events))
|
||||||
|
if err != nil {
|
||||||
|
logger.Error().
|
||||||
|
Err(err).
|
||||||
|
Msg("Error initializing events publisher")
|
||||||
|
return fmt.Errorf("could not initialize events publisher %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lh := staticroutes.StaticRouteHandler{
|
lh := staticroutes.StaticRouteHandler{
|
||||||
Prefix: cfg.HTTP.Root,
|
Prefix: cfg.HTTP.Root,
|
||||||
UserInfoCache: userInfoCache,
|
UserInfoCache: userInfoCache,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
Config: *cfg,
|
Config: *cfg,
|
||||||
OidcClient: oidcClient,
|
OidcClient: oidcClient,
|
||||||
OidcHttpClient: oidcHTTPClient,
|
OidcHttpClient: oidcHTTPClient,
|
||||||
Proxy: rp,
|
Proxy: rp,
|
||||||
|
EventsPublisher: publisher,
|
||||||
|
UserProvider: userProvider,
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize reverse proxy: %w", err)
|
return fmt.Errorf("failed to initialize reverse proxy: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
middlewares := loadMiddlewares(ctx, logger, cfg, userInfoCache, signingKeyStore, traceProvider, *m)
|
middlewares := loadMiddlewares(logger, cfg, userInfoCache, signingKeyStore, traceProvider, *m, userProvider, gatewaySelector)
|
||||||
server, err := proxyHTTP.Server(
|
server, err := proxyHTTP.Server(
|
||||||
proxyHTTP.Handler(lh.Handler()),
|
proxyHTTP.Handler(lh.Handler()),
|
||||||
proxyHTTP.Logger(logger),
|
proxyHTTP.Logger(logger),
|
||||||
@@ -200,37 +246,12 @@ func Server(cfg *config.Config) *cli.Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config, userInfoCache, signingKeyStore microstore.Store, traceProvider trace.TracerProvider, metrics metrics.Metrics) alice.Chain {
|
func loadMiddlewares(logger log.Logger, cfg *config.Config,
|
||||||
|
userInfoCache, signingKeyStore microstore.Store, traceProvider trace.TracerProvider, metrics metrics.Metrics,
|
||||||
|
userProvider backend.UserBackend, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) alice.Chain {
|
||||||
|
|
||||||
rolesClient := settingssvc.NewRoleService("com.owncloud.api.settings", cfg.GrpcClient)
|
rolesClient := settingssvc.NewRoleService("com.owncloud.api.settings", cfg.GrpcClient)
|
||||||
policiesProviderClient := policiessvc.NewPoliciesProviderService("com.owncloud.api.policies", cfg.GrpcClient)
|
policiesProviderClient := policiessvc.NewPoliciesProviderService("com.owncloud.api.policies", cfg.GrpcClient)
|
||||||
gatewaySelector, err := pool.GatewaySelector(
|
|
||||||
cfg.Reva.Address,
|
|
||||||
append(
|
|
||||||
cfg.Reva.GetRevaOptions(),
|
|
||||||
pool.WithRegistry(registry.GetRegistry()),
|
|
||||||
pool.WithTracerProvider(traceProvider),
|
|
||||||
)...)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal().Err(err).Msg("Failed to get gateway selector")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal().Err(err).
|
|
||||||
Msg("Failed to create token manager")
|
|
||||||
}
|
|
||||||
var userProvider backend.UserBackend
|
|
||||||
switch cfg.AccountBackend {
|
|
||||||
case "cs3":
|
|
||||||
userProvider = backend.NewCS3UserBackend(
|
|
||||||
backend.WithLogger(logger),
|
|
||||||
backend.WithRevaGatewaySelector(gatewaySelector),
|
|
||||||
backend.WithMachineAuthAPIKey(cfg.MachineAuthAPIKey),
|
|
||||||
backend.WithOIDCissuer(cfg.OIDC.Issuer),
|
|
||||||
backend.WithServiceAccount(cfg.ServiceAccount),
|
|
||||||
backend.WithAutoProvisionClaims(cfg.AutoProvisionClaims),
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
logger.Fatal().Msgf("Invalid accounts backend type '%s'", cfg.AccountBackend)
|
|
||||||
}
|
|
||||||
|
|
||||||
var roleAssigner userroles.UserRoleAssigner
|
var roleAssigner userroles.UserRoleAssigner
|
||||||
switch cfg.RoleAssignment.Driver {
|
switch cfg.RoleAssignment.Driver {
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ import (
|
|||||||
|
|
||||||
// Config combines all available configuration parts.
|
// Config combines all available configuration parts.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Commons *shared.Commons `mask:"struct" yaml:"-"` // don't use this directly as configuration for a service
|
Commons *shared.Commons `yaml:"-" mask:"struct"` // don't use this directly as configuration for a service
|
||||||
|
|
||||||
Service Service `yaml:"-"`
|
Service Service `yaml:"-"`
|
||||||
|
|
||||||
Tracing *Tracing `yaml:"tracing"`
|
Tracing *Tracing `yaml:"tracing"`
|
||||||
Log *Log `yaml:"log"`
|
Log *Log `yaml:"log"`
|
||||||
Debug Debug `mask:"struct" yaml:"debug"`
|
Debug Debug `yaml:"debug" mask:"struct"`
|
||||||
|
|
||||||
HTTP HTTP `yaml:"http"`
|
HTTP HTTP `yaml:"http"`
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ type Config struct {
|
|||||||
AccountBackend string `yaml:"account_backend" env:"PROXY_ACCOUNT_BACKEND_TYPE" desc:"Account backend the PROXY service should use. Currently only 'cs3' is possible here." introductionVersion:"pre5.0"`
|
AccountBackend string `yaml:"account_backend" env:"PROXY_ACCOUNT_BACKEND_TYPE" desc:"Account backend the PROXY service should use. Currently only 'cs3' is possible here." introductionVersion:"pre5.0"`
|
||||||
UserOIDCClaim string `yaml:"user_oidc_claim" env:"PROXY_USER_OIDC_CLAIM" desc:"The name of an OpenID Connect claim that is used for resolving users with the account backend. The value of the claim must hold a per user unique, stable and non re-assignable identifier. The availability of claims depends on your Identity Provider. There are common claims available for most Identity providers like 'email' or 'preferred_username' but you can also add your own claim." introductionVersion:"pre5.0"`
|
UserOIDCClaim string `yaml:"user_oidc_claim" env:"PROXY_USER_OIDC_CLAIM" desc:"The name of an OpenID Connect claim that is used for resolving users with the account backend. The value of the claim must hold a per user unique, stable and non re-assignable identifier. The availability of claims depends on your Identity Provider. There are common claims available for most Identity providers like 'email' or 'preferred_username' but you can also add your own claim." introductionVersion:"pre5.0"`
|
||||||
UserCS3Claim string `yaml:"user_cs3_claim" env:"PROXY_USER_CS3_CLAIM" desc:"The name of a CS3 user attribute (claim) that should be mapped to the 'user_oidc_claim'. Supported values are 'username', 'mail' and 'userid'." introductionVersion:"pre5.0"`
|
UserCS3Claim string `yaml:"user_cs3_claim" env:"PROXY_USER_CS3_CLAIM" desc:"The name of a CS3 user attribute (claim) that should be mapped to the 'user_oidc_claim'. Supported values are 'username', 'mail' and 'userid'." introductionVersion:"pre5.0"`
|
||||||
MachineAuthAPIKey string `mask:"password" yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;PROXY_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services." introductionVersion:"pre5.0"`
|
MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;PROXY_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services." introductionVersion:"pre5.0" mask:"password"`
|
||||||
AutoprovisionAccounts bool `yaml:"auto_provision_accounts" env:"PROXY_AUTOPROVISION_ACCOUNTS" desc:"Set this to 'true' to automatically provision users that do not yet exist in the users service on-demand upon first sign-in. To use this a write-enabled libregraph user backend needs to be setup an running." introductionVersion:"pre5.0"`
|
AutoprovisionAccounts bool `yaml:"auto_provision_accounts" env:"PROXY_AUTOPROVISION_ACCOUNTS" desc:"Set this to 'true' to automatically provision users that do not yet exist in the users service on-demand upon first sign-in. To use this a write-enabled libregraph user backend needs to be setup an running." introductionVersion:"pre5.0"`
|
||||||
AutoProvisionClaims AutoProvisionClaims `yaml:"auto_provision_claims"`
|
AutoProvisionClaims AutoProvisionClaims `yaml:"auto_provision_claims"`
|
||||||
EnableBasicAuth bool `yaml:"enable_basic_auth" env:"PROXY_ENABLE_BASIC_AUTH" desc:"Set this to true to enable 'basic authentication' (username/password)." introductionVersion:"pre5.0"`
|
EnableBasicAuth bool `yaml:"enable_basic_auth" env:"PROXY_ENABLE_BASIC_AUTH" desc:"Set this to true to enable 'basic authentication' (username/password)." introductionVersion:"pre5.0"`
|
||||||
@@ -44,8 +44,9 @@ type Config struct {
|
|||||||
AuthMiddleware AuthMiddleware `yaml:"auth_middleware"`
|
AuthMiddleware AuthMiddleware `yaml:"auth_middleware"`
|
||||||
PoliciesMiddleware PoliciesMiddleware `yaml:"policies_middleware"`
|
PoliciesMiddleware PoliciesMiddleware `yaml:"policies_middleware"`
|
||||||
CSPConfigFileLocation string `yaml:"csp_config_file_location" env:"PROXY_CSP_CONFIG_FILE_LOCATION" desc:"The location of the CSP configuration file." introductionVersion:"6.0.0"`
|
CSPConfigFileLocation string `yaml:"csp_config_file_location" env:"PROXY_CSP_CONFIG_FILE_LOCATION" desc:"The location of the CSP configuration file." introductionVersion:"6.0.0"`
|
||||||
|
Events Events `yaml:"events"`
|
||||||
|
|
||||||
Context context.Context `yaml:"-" json:"-"`
|
Context context.Context `json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Policy enables us to use multiple directors.
|
// Policy enables us to use multiple directors.
|
||||||
@@ -217,3 +218,14 @@ type ServiceAccount struct {
|
|||||||
ServiceAccountID string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;PROXY_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details." introductionVersion:"5.0"`
|
ServiceAccountID string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;PROXY_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details." introductionVersion:"5.0"`
|
||||||
ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;PROXY_SERVICE_ACCOUNT_SECRET" desc:"The service account secret." introductionVersion:"5.0"`
|
ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;PROXY_SERVICE_ACCOUNT_SECRET" desc:"The service account secret." introductionVersion:"5.0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Events combines the configuration options for the event bus.
|
||||||
|
type Events struct {
|
||||||
|
Endpoint string `yaml:"endpoint" env:"OCIS_EVENTS_ENDPOINT;PROXY_EVENTS_ENDPOINT" desc:"The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Set to a empty string to disable emitting events." introductionVersion:"%%NEXT%%"`
|
||||||
|
Cluster string `yaml:"cluster" env:"OCIS_EVENTS_CLUSTER;PROXY_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture." introductionVersion:"%%NEXT%%"`
|
||||||
|
TLSInsecure bool `yaml:"tls_insecure" env:"OCIS_INSECURE;PROXY_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates." introductionVersion:"%%NEXT%%"`
|
||||||
|
TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;PROXY_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided PROXY_EVENTS_TLS_INSECURE will be seen as false." introductionVersion:"%%NEXT%%"`
|
||||||
|
EnableTLS bool `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;PROXY_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." introductionVersion:"%%NEXT%%"`
|
||||||
|
AuthUsername string `yaml:"username" env:"OCIS_EVENTS_AUTH_USERNAME;PROXY_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services." introductionVersion:"%%NEXT%%"`
|
||||||
|
AuthPassword string `yaml:"password" env:"OCIS_EVENTS_AUTH_PASSWORD;PROXY_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the ocis service which receives and delivers events between the services." introductionVersion:"%%NEXT%%"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -92,6 +92,11 @@ func DefaultConfig() *config.Config {
|
|||||||
EnableBasicAuth: false,
|
EnableBasicAuth: false,
|
||||||
InsecureBackends: false,
|
InsecureBackends: false,
|
||||||
CSPConfigFileLocation: "",
|
CSPConfigFileLocation: "",
|
||||||
|
Events: config.Events{
|
||||||
|
Endpoint: "127.0.0.1:9233",
|
||||||
|
Cluster: "ocis-cluster",
|
||||||
|
EnableTLS: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
package staticroutes
|
package staticroutes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/cs3org/reva/v2/pkg/events"
|
||||||
|
"github.com/cs3org/reva/v2/pkg/utils"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
|
"github.com/owncloud/ocis/v2/ocis-pkg/oidc"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/shamaton/msgpack/v2"
|
||||||
microstore "go-micro.dev/v4/store"
|
microstore "go-micro.dev/v4/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,7 +39,6 @@ func (s *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Re
|
|||||||
render.JSON(w, r, nil)
|
render.JSON(w, r, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error().Err(err).Msg("Error reading userinfo cache")
|
logger.Error().Err(err).Msg("Error reading userinfo cache")
|
||||||
render.Status(r, http.StatusBadRequest)
|
render.Status(r, http.StatusBadRequest)
|
||||||
@@ -42,6 +47,10 @@ func (s *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, record := range records {
|
for _, record := range records {
|
||||||
|
err := s.publishBackchannelLogoutEvent(r.Context(), record, logoutToken)
|
||||||
|
if err != nil {
|
||||||
|
s.Logger.Warn().Err(err).Msg("could not publish backchannel logout event")
|
||||||
|
}
|
||||||
err = s.UserInfoCache.Delete(string(record.Value))
|
err = s.UserInfoCache.Delete(string(record.Value))
|
||||||
if err != nil && !errors.Is(err, microstore.ErrNotFound) {
|
if err != nil && !errors.Is(err, microstore.ErrNotFound) {
|
||||||
// Spec requires us to return a 400 BadRequest when the session could not be destroyed
|
// Spec requires us to return a 400 BadRequest when the session could not be destroyed
|
||||||
@@ -62,3 +71,43 @@ func (s *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Re
|
|||||||
render.Status(r, http.StatusOK)
|
render.Status(r, http.StatusOK)
|
||||||
render.JSON(w, r, nil)
|
render.JSON(w, r, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// publishBackchannelLogoutEvent publishes a backchannel logout event when the callback revived from the identity provider
|
||||||
|
func (s StaticRouteHandler) publishBackchannelLogoutEvent(ctx context.Context, record *microstore.Record, logoutToken *oidc.LogoutToken) error {
|
||||||
|
if s.EventsPublisher == nil {
|
||||||
|
return fmt.Errorf("the events publisher is not set")
|
||||||
|
}
|
||||||
|
urecords, err := s.UserInfoCache.Read(string(record.Value))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("reading userinfo cache: %w", err)
|
||||||
|
}
|
||||||
|
if len(urecords) == 0 {
|
||||||
|
return fmt.Errorf("userinfo not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
var claims map[string]interface{}
|
||||||
|
if err = msgpack.UnmarshalAsMap(urecords[0].Value, &claims); err != nil {
|
||||||
|
return fmt.Errorf("could not unmarshal userinfo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
oidcClaim, ok := claims[s.Config.UserOIDCClaim].(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("could not get claim %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user, _, err := s.UserProvider.GetUserByClaims(ctx, s.Config.UserCS3Claim, oidcClaim)
|
||||||
|
if err != nil || user.GetId() == nil {
|
||||||
|
return fmt.Errorf("could not get user by claims: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e := events.BackchannelLogout{
|
||||||
|
Executant: user.GetId(),
|
||||||
|
SessionId: logoutToken.SessionId,
|
||||||
|
Timestamp: utils.TSNow(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := events.Publish(ctx, s.EventsPublisher, e); err != nil {
|
||||||
|
return fmt.Errorf("could not publish user created event %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,22 +3,26 @@ package staticroutes
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/cs3org/reva/v2/pkg/events"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||||
"github.com/owncloud/ocis/v2/ocis-pkg/oidc"
|
"github.com/owncloud/ocis/v2/ocis-pkg/oidc"
|
||||||
"github.com/owncloud/ocis/v2/services/proxy/pkg/config"
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/config"
|
||||||
|
"github.com/owncloud/ocis/v2/services/proxy/pkg/user/backend"
|
||||||
microstore "go-micro.dev/v4/store"
|
microstore "go-micro.dev/v4/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StaticRouteHandler defines a Route Handler for static routes
|
// StaticRouteHandler defines a Route Handler for static routes
|
||||||
type StaticRouteHandler struct {
|
type StaticRouteHandler struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
Proxy http.Handler
|
Proxy http.Handler
|
||||||
UserInfoCache microstore.Store
|
UserInfoCache microstore.Store
|
||||||
Logger log.Logger
|
Logger log.Logger
|
||||||
Config config.Config
|
Config config.Config
|
||||||
OidcClient oidc.OIDCClient
|
OidcClient oidc.OIDCClient
|
||||||
OidcHttpClient *http.Client
|
OidcHttpClient *http.Client
|
||||||
|
EventsPublisher events.Publisher
|
||||||
|
UserProvider backend.UserBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
type jse struct {
|
type jse struct {
|
||||||
|
|||||||
+14
@@ -88,3 +88,17 @@ func (PersonalDataExtracted) Unmarshal(v []byte) (interface{}, error) {
|
|||||||
err := json.Unmarshal(v, &e)
|
err := json.Unmarshal(v, &e)
|
||||||
return e, err
|
return e, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackchannelLogout is emitted when the callback from the identity provider is received
|
||||||
|
type BackchannelLogout struct {
|
||||||
|
Executant *user.UserId
|
||||||
|
SessionId string
|
||||||
|
Timestamp *types.Timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal to fulfill umarshaller interface
|
||||||
|
func (BackchannelLogout) Unmarshal(v []byte) (interface{}, error) {
|
||||||
|
e := BackchannelLogout{}
|
||||||
|
err := json.Unmarshal(v, &e)
|
||||||
|
return e, err
|
||||||
|
}
|
||||||
|
|||||||
+13
-3
@@ -44,20 +44,30 @@ func GetServiceUserContext(serviceUserID string, gwc gateway.GatewayAPIClient, s
|
|||||||
|
|
||||||
// GetServiceUserContextWithContext returns an authenticated context of the given service user
|
// GetServiceUserContextWithContext returns an authenticated context of the given service user
|
||||||
func GetServiceUserContextWithContext(ctx context.Context, gwc gateway.GatewayAPIClient, serviceUserID string, serviceUserSecret string) (context.Context, error) {
|
func GetServiceUserContextWithContext(ctx context.Context, gwc gateway.GatewayAPIClient, serviceUserID string, serviceUserSecret string) (context.Context, error) {
|
||||||
|
token, err := GetServiceUserToken(ctx, gwc, serviceUserID, serviceUserSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, token), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServiceUserToken returns a reva authentication token for the given service user
|
||||||
|
func GetServiceUserToken(ctx context.Context, gwc gateway.GatewayAPIClient, serviceUserID string, serviceUserSecret string) (string, error) {
|
||||||
authRes, err := gwc.Authenticate(ctx, &gateway.AuthenticateRequest{
|
authRes, err := gwc.Authenticate(ctx, &gateway.AuthenticateRequest{
|
||||||
Type: "serviceaccounts",
|
Type: "serviceaccounts",
|
||||||
ClientId: serviceUserID,
|
ClientId: serviceUserID,
|
||||||
ClientSecret: serviceUserSecret,
|
ClientSecret: serviceUserSecret,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkStatusCode("authenticating service user", authRes.GetStatus().GetMessage(), authRes.GetStatus().GetCode()); err != nil {
|
if err := checkStatusCode("authenticating service user", authRes.GetStatus().GetMessage(), authRes.GetStatus().GetCode()); err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, authRes.Token), nil
|
return authRes.Token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUser gets the specified user
|
// GetUser gets the specified user
|
||||||
|
|||||||
Vendored
+1
-1
@@ -366,7 +366,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1
|
|||||||
github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1
|
github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1
|
||||||
github.com/cs3org/go-cs3apis/cs3/tx/v1beta1
|
github.com/cs3org/go-cs3apis/cs3/tx/v1beta1
|
||||||
github.com/cs3org/go-cs3apis/cs3/types/v1beta1
|
github.com/cs3org/go-cs3apis/cs3/types/v1beta1
|
||||||
# github.com/cs3org/reva/v2 v2.20.0
|
# github.com/cs3org/reva/v2 v2.20.1-0.20240625085350-9351da5ba08f
|
||||||
## explicit; go 1.21
|
## explicit; go 1.21
|
||||||
github.com/cs3org/reva/v2/cmd/revad/internal/grace
|
github.com/cs3org/reva/v2/cmd/revad/internal/grace
|
||||||
github.com/cs3org/reva/v2/cmd/revad/runtime
|
github.com/cs3org/reva/v2/cmd/revad/runtime
|
||||||
|
|||||||
Reference in New Issue
Block a user