diff --git a/services/invitations/pkg/config/config.go b/services/invitations/pkg/config/config.go index 5e9aab9c60..6606338c16 100644 --- a/services/invitations/pkg/config/config.go +++ b/services/invitations/pkg/config/config.go @@ -18,16 +18,18 @@ type Config struct { HTTP HTTP `yaml:"http"` + Endpoint Endpoint `yaml:"enpoint"` + TokenManager *TokenManager `yaml:"token_manager"` Context context.Context `yaml:"-"` } -// Instance to use with a matching rule and titles -type Instance struct { - Claim string `yaml:"claim"` - Regex string `yaml:"regex"` - Href string `yaml:"href"` - Titles map[string]string `yaml:"titles"` - Break bool `yaml:"break"` +// Endpoint to use +type Endpoint struct { + URL string `yaml:"url" env:"INVITATIONS_PROVISIONING_URL" desc:"The endpoint provisioning requests are sent to."` + Method string `yaml:"method" env:"INVITATIONS_PROVISIONING_METHOD" desc:"The method to use when making provisioning requests."` + BodyTemplate string `yaml:"body_template" env:"INVITATIONS_PROVISIONING_BODY_TEMPLATE" desc:"The template to use as body of a provisioning request."` + Authorization string `yaml:"authorization" env:"INVITATIONS_PROVISIONING_AUTH" desc:"The authorization to use. Can be 'token' to reuse the access token or 'bearer' to send a static api token."` + Token string `yaml:"authorization" env:"INVITATIONS_PROVISIONING_AUTH" desc:"The bearer token to send in provisioning requests."` } diff --git a/services/invitations/pkg/config/defaults/defaultconfig.go b/services/invitations/pkg/config/defaults/defaultconfig.go index 5249cef136..527be752ec 100644 --- a/services/invitations/pkg/config/defaults/defaultconfig.go +++ b/services/invitations/pkg/config/defaults/defaultconfig.go @@ -32,6 +32,17 @@ func DefaultConfig() *config.Config { Service: config.Service{ Name: "invitations", }, + Endpoint: config.Endpoint{ + URL: "{{.OCIS_URL}}/graph/v1.0/users", + Method: "POST", + BodyTemplate: `{ + "inviteRedirectUrl": "{{.redirectUrl}}", + "invitedUserEmailAddress": "{{.mail}}", + "invitedUserDisplayName": "{{.displayName}}", + "sendInvitationMessage": true + }`, + Authorization: "token", // reuse existing token + }, } } diff --git a/services/invitations/pkg/service/v0/service.go b/services/invitations/pkg/service/v0/service.go index b72194c88c..d774f65061 100644 --- a/services/invitations/pkg/service/v0/service.go +++ b/services/invitations/pkg/service/v0/service.go @@ -5,7 +5,10 @@ import ( "context" "crypto/tls" "encoding/json" + "fmt" "net/http" + "strings" + "text/template" libregraph "github.com/owncloud/libre-graph-api-go" "github.com/owncloud/ocis/v2/ocis-pkg/log" @@ -41,15 +44,24 @@ type Service interface { func New(opts ...Option) (Service, error) { options := newOptions(opts...) + urlTemplate, err := template.New("invitations-provisioning-endpoint-url").Parse(options.Config.Endpoint.URL) + bodyTemplate, err := template.New("invitations-provisioning-endpoint-url").Parse(options.Config.Endpoint.BodyTemplate) + if err != nil { + return nil, err + } return svc{ - log: options.Logger, - config: options.Config, + log: options.Logger, + config: options.Config, + urlTemplate: urlTemplate, + bodyTemplate: bodyTemplate, }, nil } type svc struct { - config *config.Config - log log.Logger + config *config.Config + log log.Logger + urlTemplate *template.Template + bodyTemplate *template.Template } // Invite implements the service interface @@ -74,8 +86,21 @@ func (s svc) Invite(ctx context.Context, invitation *invitations.Invitation) (*i // we don't really need a username as guests have to log in with their email address anyway // what if later a user is provisioned with a guest accounts email address? - data, err := json.Marshal(user) - if err != nil { + templateVars := map[string]string{ + "redirectUrl": invitation.InviteRedirectUrl, + // TODO message and other options + "mail": invitation.InvitedUserEmailAddress, + "displayName": invitation.InvitedUserDisplayName, + "userType": invitation.InvitedUserType, + } + + var urlWriter strings.Builder + if err := s.urlTemplate.Execute(&urlWriter, templateVars); err != nil { + return nil, err + } + + var bodyWriter strings.Builder + if err := s.bodyTemplate.Execute(&bodyWriter, templateVars); err != nil { return nil, err } @@ -85,13 +110,20 @@ func (s svc) Invite(ctx context.Context, invitation *invitations.Invitation) (*i } client := &http.Client{Transport: tr} - req, err := http.NewRequest("POST", "/graph/v1.0/users", bytes.NewReader(data)) + req, err := http.NewRequest(s.config.Endpoint.Method, urlWriter.String(), bytes.NewBufferString(bodyWriter.String())) if err != nil { return nil, err } // TODO either forward current user token or use bearer token? - req.Header.Set("Authorization", "Bearer some-token") + switch s.config.Endpoint.Authorization { + case "token": + // TODO forward current reva access token + case "bearer": + req.Header.Set("Authorization", "Bearer "+s.config.Endpoint.Token) + default: + return nil, fmt.Errorf("unknown authorization: " + s.config.Endpoint.Authorization) + } res, err := client.Do(req) if err != nil { @@ -99,6 +131,15 @@ func (s svc) Invite(ctx context.Context, invitation *invitations.Invitation) (*i } defer res.Body.Close() + // TODO hm ok so we expect the rosponse to be a libregraph user ... so much for a generic endpoint + // we could try parsing into a map[string]interface{} .... hm ... maybe better to be specific about + // the actual backend: libregraph, keycloak, scim or even oc10? + + // Or we remember the mail of the user in memory and try to check if the user is already avilable via + // a local user api ... hm ... graph or cs3 user backend now? + + // in any case this will require an additional endpoint to keep track of the ongoing invitations + invitedUser := &libregraph.User{} err = json.NewDecoder(res.Body).Decode(invitedUser) if err != nil {