From 8e32b4f5da49b1f7bf3c067acd67aee6eeb99643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Franke?= Date: Wed, 5 Apr 2023 12:39:05 +0200 Subject: [PATCH] Integrate keycloak and events data into graph. This PR adds the data from keycloak and events into the GDPR export of the graph service. --- services/graph/pkg/config/config.go | 13 ++++++++++++- services/graph/pkg/server/http/server.go | 16 ++++++++++++++++ services/graph/pkg/service/v0/graph.go | 4 ++++ services/graph/pkg/service/v0/option.go | 18 ++++++++++++++++++ services/graph/pkg/service/v0/personaldata.go | 14 +++++++++++--- services/graph/pkg/service/v0/service.go | 1 + services/invitations/pkg/config/config.go | 12 ++++++------ 7 files changed, 68 insertions(+), 10 deletions(-) diff --git a/services/graph/pkg/config/config.go b/services/graph/pkg/config/config.go index 0dac204c2..da11aa48d 100644 --- a/services/graph/pkg/config/config.go +++ b/services/graph/pkg/config/config.go @@ -30,7 +30,8 @@ type Config struct { Identity Identity `yaml:"identity"` Events Events `yaml:"events"` - MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;USERLOG_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."` + MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;USERLOG_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."` + Keycloak Keycloak `yaml:"keycloak"` Context context.Context `yaml:"-"` } @@ -121,3 +122,13 @@ type CORS struct { AllowedHeaders []string `yaml:"allow_headers" env:"OCIS_CORS_ALLOW_HEADERS;GRAPH_CORS_ALLOW_HEADERS" desc:"A comma-separated list of allowed CORS headers. See following chapter for more details: *Access-Control-Request-Headers* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Request-Headers."` AllowCredentials bool `yaml:"allow_credentials" env:"OCIS_CORS_ALLOW_CREDENTIALS;GRAPH_CORS_ALLOW_CREDENTIALS" desc:"Allow credentials for CORS.See following chapter for more details: *Access-Control-Allow-Credentials* at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials."` } + +// Keycloak configuration +type Keycloak struct { + BasePath string `yaml:"base_path" env:"KEYCLOAK_BASE_PATH;GRAPH_KEYCLOAK_BASE_PATH" desc:"The URL to access keycloak."` + ClientID string `yaml:"client_id" env:"KEYCLOAK_CLIENT_ID;GRAPH_KEYCLOAK_CLIENT_ID" desc:"The client id to authenticate with keycloak."` + ClientSecret string `yaml:"client_secret" env:"KEYCLOAK_CLIENT_SECRET;GRAPH_KEYCLOAK_CLIENT_SECRET" desc:"The client secret to use in authentication."` + ClientRealm string `yaml:"client_realm" env:"KEYCLOAK_CLIENT_REALM;GRAPH_KEYCLOAK_CLIENT_REALM" desc:"The realm the client is defined in."` + UserRealm string `yaml:"user_realm" env:"KEYCLOAK_USER_REALM;GRAPH_KEYCLOAK_USER_REALM" desc:"The realm users are defined."` + InsecureSkipVerify bool `yaml:"insecure_skip_verify" env:"KEYCLOAK_INSECURE_SKIP_VERIFY;GRAPH_KEYCLOAK_INSECURE_SKIP_VERIFY" desc:"Disable TLS certificate validation for Keycloak connections. Do not set this in production environments."` +} diff --git a/services/graph/pkg/server/http/server.go b/services/graph/pkg/server/http/server.go index 83643968a..5e7f89bd1 100644 --- a/services/graph/pkg/server/http/server.go +++ b/services/graph/pkg/server/http/server.go @@ -15,10 +15,13 @@ import ( "github.com/owncloud/ocis/v2/ocis-pkg/account" "github.com/owncloud/ocis/v2/ocis-pkg/cors" ociscrypto "github.com/owncloud/ocis/v2/ocis-pkg/crypto" + "github.com/owncloud/ocis/v2/ocis-pkg/keycloak" "github.com/owncloud/ocis/v2/ocis-pkg/middleware" "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" + ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" "github.com/owncloud/ocis/v2/ocis-pkg/service/http" "github.com/owncloud/ocis/v2/ocis-pkg/version" + ehsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/eventhistory/v0" searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" graphMiddleware "github.com/owncloud/ocis/v2/services/graph/pkg/middleware" @@ -132,6 +135,18 @@ func Server(opts ...Option) (http.Service, error) { // no gatewayclient needed } + // Keycloak client is optional, so if it stays nil, it's fine. + var keyCloakClient keycloak.Client + if options.Config.Keycloak.BasePath != "" { + kcc := options.Config.Keycloak + if kcc.ClientID == "" || kcc.ClientSecret == "" || kcc.ClientRealm == "" || kcc.UserRealm == "" { + return nil, errors.New("keycloak client id, secret, client realm and user realm must be set") + } + keyCloakClient = keycloak.New(kcc.BasePath, kcc.ClientID, kcc.ClientSecret, kcc.ClientRealm, kcc.InsecureSkipVerify) + } + + hClient := ehsvc.NewEventHistoryService("com.owncloud.api.eventhistory", ogrpc.DefaultClient()) + var handle svc.Service handle, err = svc.NewService( svc.Logger(options.Logger), @@ -142,6 +157,7 @@ func Server(opts ...Option) (http.Service, error) { svc.WithRequireAdminMiddleware(requireAdminMiddleware), svc.WithGatewayClient(gatewayClient), svc.WithSearchService(searchsvc.NewSearchProviderService("com.owncloud.api.search", grpc.DefaultClient())), + svc.KeycloakCient(keyCloakClient), ) if err != nil { diff --git a/services/graph/pkg/service/v0/graph.go b/services/graph/pkg/service/v0/graph.go index e4fc0d8f5..d5bd379b3 100644 --- a/services/graph/pkg/service/v0/graph.go +++ b/services/graph/pkg/service/v0/graph.go @@ -13,7 +13,9 @@ import ( "github.com/go-chi/chi/v5" "github.com/jellydator/ttlcache/v3" libregraph "github.com/owncloud/libre-graph-api-go" + "github.com/owncloud/ocis/v2/ocis-pkg/keycloak" "github.com/owncloud/ocis/v2/ocis-pkg/log" + ehsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/eventhistory/v0" searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" "github.com/owncloud/ocis/v2/services/graph/pkg/config" @@ -68,6 +70,8 @@ type Graph struct { groupsCache *ttlcache.Cache[string, libregraph.Group] eventsPublisher events.Publisher searchService searchsvc.SearchProviderService + keycloakClient keycloak.Client + historyClient ehsvc.EventHistoryService } // ServeHTTP implements the Service interface. diff --git a/services/graph/pkg/service/v0/option.go b/services/graph/pkg/service/v0/option.go index ccbde28b4..16ca102a0 100644 --- a/services/graph/pkg/service/v0/option.go +++ b/services/graph/pkg/service/v0/option.go @@ -5,8 +5,10 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" "github.com/cs3org/reva/v2/pkg/events" + "github.com/owncloud/ocis/v2/ocis-pkg/keycloak" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/roles" + ehsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/eventhistory/v0" searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" "github.com/owncloud/ocis/v2/services/graph/pkg/config" @@ -30,6 +32,8 @@ type Options struct { RoleManager *roles.Manager EventsPublisher events.Publisher SearchService searchsvc.SearchProviderService + KeycloakClient keycloak.Client + EventHistoryClient ehsvc.EventHistoryService } // newOptions initializes the available default options. @@ -126,3 +130,17 @@ func EventsPublisher(val events.Publisher) Option { o.EventsPublisher = val } } + +// KeycloakCient provides a function to set the KeycloakCient option. +func KeycloakCient(val keycloak.Client) Option { + return func(o *Options) { + o.KeycloakClient = val + } +} + +// EventHistoryClient provides a function to set the EventHistoryClient option. +func KeycloakCient(val keycloak.Client) Option { + return func(o *Options) { + o.KeycloakClient = val + } +} diff --git a/services/graph/pkg/service/v0/personaldata.go b/services/graph/pkg/service/v0/personaldata.go index b934acb96..a8ae63a9d 100644 --- a/services/graph/pkg/service/v0/personaldata.go +++ b/services/graph/pkg/service/v0/personaldata.go @@ -68,19 +68,28 @@ func (g Graph) ExportPersonalData(w http.ResponseWriter, r *http.Request) { } // go start gathering - go g.GatherPersonalData(u, ref, r.Header.Get(revactx.TokenHeader), marsh) + go g.GatherPersonalData(ctx, u, ref, r.Header.Get(revactx.TokenHeader), marsh) w.WriteHeader(http.StatusCreated) } // GatherPersonalData will all gather all personal data of the user and save it to a file in the users personal space -func (g Graph) GatherPersonalData(usr *user.User, ref *provider.Reference, token string, marsh Marshaller) { +func (g Graph) GatherPersonalData(ctx context.Context, usr *user.User, ref *provider.Reference, token string, marsh Marshaller) { // create data data := make(map[string]interface{}) // reva user data["user"] = usr + // Check if we have a keycloak client, and if so, get the keycloak export. + if g.keycloakClient != nil { + kcd, err := g.keycloakClient.GetPIIReport(ctx, g.config.Keycloak.ClientID, usr) + if err != nil { + g.logger.Error().Err(err).Str("userID").Msg("cannot get keycloak personal data") + } + data["keycloak"] = kcd + } + // marshal by, err := marsh(data) if err != nil { @@ -198,7 +207,6 @@ func createFolders(ctx context.Context, ref *provider.Reference, gwc gateway.Gat } } return nil - } func getLocation(r *http.Request) string { diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go index 8541167af..08c345aeb 100644 --- a/services/graph/pkg/service/v0/service.go +++ b/services/graph/pkg/service/v0/service.go @@ -143,6 +143,7 @@ func NewService(opts ...Option) (Graph, error) { gatewayClient: options.GatewayClient, searchService: options.SearchService, identityEducationBackend: options.IdentityEducationBackend, + keycloakClient: options.KeycloakClient, } if err := setIdentityBackends(options, &svc); err != nil { diff --git a/services/invitations/pkg/config/config.go b/services/invitations/pkg/config/config.go index ca499aeae..944813329 100644 --- a/services/invitations/pkg/config/config.go +++ b/services/invitations/pkg/config/config.go @@ -26,10 +26,10 @@ type Config struct { // Keycloak configuration type Keycloak struct { - BasePath string `yaml:"base_path" env:"INVITATIONS_KEYCLOAK_BASE_PATH" desc:"The URL to access keycloak."` - ClientID string `yaml:"client_id" env:"INVITATIONS_KEYCLOAK_CLIENT_ID" desc:"The client id to authenticate with keycloak."` - ClientSecret string `yaml:"client_secret" env:"INVITATIONS_KEYCLOAK_CLIENT_SECRET" desc:"The client secret to use in authentication."` - ClientRealm string `yaml:"client_realm" env:"INVITATIONS_KEYCLOAK_CLIENT_REALM" desc:"The realm the client is defined in."` - UserRealm string `yaml:"user_realm" env:"INVITATIONS_KEYCLOAK_USER_REALM" desc:"The realm users are defined."` - InsecureSkipVerify bool `yaml:"insecure_skip_verify" env:"INVITATIONS_KEYCLOAK_INSECURE_SKIP_VERIFY" desc:"Disable TLS certificate validation for Keycloak connections. Do not set this in production environments."` + BasePath string `yaml:"base_path" env:"KEYCLOAK_BASE_PATH;INVITATIONS_KEYCLOAK_BASE_PATH" desc:"The URL to access keycloak."` + ClientID string `yaml:"client_id" env:"KEYCLOAK_CLIENT_ID;INVITATIONS_KEYCLOAK_CLIENT_ID" desc:"The client id to authenticate with keycloak."` + ClientSecret string `yaml:"client_secret" env:"KEYCLOAK_CLIENT_SECRET;INVITATIONS_KEYCLOAK_CLIENT_SECRET" desc:"The client secret to use in authentication."` + ClientRealm string `yaml:"client_realm" env:"KEYCLOAK_CLIENT_REALM;INVITATIONS_KEYCLOAK_CLIENT_REALM" desc:"The realm the client is defined in."` + UserRealm string `yaml:"user_realm" env:"KEYCLOAK_USER_REALM;INVITATIONS_KEYCLOAK_USER_REALM" desc:"The realm users are defined."` + InsecureSkipVerify bool `yaml:"insecure_skip_verify" env:"KEYCLOAK_INSECURE_SKIP_VERIFY;INVITATIONS_KEYCLOAK_INSECURE_SKIP_VERIFY" desc:"Disable TLS certificate validation for Keycloak connections. Do not set this in production environments."` }