generic http backend provisioning requests

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
Jörn Friedrich Dreyer
2023-03-02 17:16:05 +01:00
committed by Daniël Franke
parent 5d7a19997b
commit fc5e4ea7d1
3 changed files with 69 additions and 15 deletions
+9 -7
View File
@@ -18,16 +18,18 @@ type Config struct {
HTTP HTTP `yaml:"http"` HTTP HTTP `yaml:"http"`
Endpoint Endpoint `yaml:"enpoint"`
TokenManager *TokenManager `yaml:"token_manager"` TokenManager *TokenManager `yaml:"token_manager"`
Context context.Context `yaml:"-"` Context context.Context `yaml:"-"`
} }
// Instance to use with a matching rule and titles // Endpoint to use
type Instance struct { type Endpoint struct {
Claim string `yaml:"claim"` URL string `yaml:"url" env:"INVITATIONS_PROVISIONING_URL" desc:"The endpoint provisioning requests are sent to."`
Regex string `yaml:"regex"` Method string `yaml:"method" env:"INVITATIONS_PROVISIONING_METHOD" desc:"The method to use when making provisioning requests."`
Href string `yaml:"href"` BodyTemplate string `yaml:"body_template" env:"INVITATIONS_PROVISIONING_BODY_TEMPLATE" desc:"The template to use as body of a provisioning request."`
Titles map[string]string `yaml:"titles"` 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."`
Break bool `yaml:"break"` Token string `yaml:"authorization" env:"INVITATIONS_PROVISIONING_AUTH" desc:"The bearer token to send in provisioning requests."`
} }
@@ -32,6 +32,17 @@ func DefaultConfig() *config.Config {
Service: config.Service{ Service: config.Service{
Name: "invitations", 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
},
} }
} }
+49 -8
View File
@@ -5,7 +5,10 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"strings"
"text/template"
libregraph "github.com/owncloud/libre-graph-api-go" libregraph "github.com/owncloud/libre-graph-api-go"
"github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/log"
@@ -41,15 +44,24 @@ type Service interface {
func New(opts ...Option) (Service, error) { func New(opts ...Option) (Service, error) {
options := newOptions(opts...) 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{ return svc{
log: options.Logger, log: options.Logger,
config: options.Config, config: options.Config,
urlTemplate: urlTemplate,
bodyTemplate: bodyTemplate,
}, nil }, nil
} }
type svc struct { type svc struct {
config *config.Config config *config.Config
log log.Logger log log.Logger
urlTemplate *template.Template
bodyTemplate *template.Template
} }
// Invite implements the service interface // 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 // 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? // what if later a user is provisioned with a guest accounts email address?
data, err := json.Marshal(user) templateVars := map[string]string{
if err != nil { "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 return nil, err
} }
@@ -85,13 +110,20 @@ func (s svc) Invite(ctx context.Context, invitation *invitations.Invitation) (*i
} }
client := &http.Client{Transport: tr} 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 { if err != nil {
return nil, err return nil, err
} }
// TODO either forward current user token or use bearer token? // 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) res, err := client.Do(req)
if err != nil { if err != nil {
@@ -99,6 +131,15 @@ func (s svc) Invite(ctx context.Context, invitation *invitations.Invitation) (*i
} }
defer res.Body.Close() 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{} invitedUser := &libregraph.User{}
err = json.NewDecoder(res.Body).Decode(invitedUser) err = json.NewDecoder(res.Body).Decode(invitedUser)
if err != nil { if err != nil {