// Package keycloak offers an invitation backend for the invitation service. package keycloak import ( "context" "github.com/google/uuid" "github.com/qsfera/server/pkg/keycloak" "github.com/qsfera/server/pkg/log" "github.com/qsfera/server/services/invitations/pkg/invitations" libregraph "github.com/opencloud-eu/libre-graph-api-go" ) const ( userType = "Guest" ) var userRequiredActions = []keycloak.UserAction{ keycloak.UserActionUpdatePassword, keycloak.UserActionVerifyEmail, } // Backend represents the keycloak backend. type Backend struct { logger log.Logger client keycloak.Client userRealm string } // New instantiates a new keycloak.Backend with a default gocloak client. func New( logger log.Logger, baseURL, clientID, clientSecret, clientRealm, userRealm string, insecureSkipVerify bool, ) *Backend { logger = log.Logger{ Logger: logger.With(). Str("invitationBackend", "keycloak"). Str("clientID", clientID). Str("clientRealm", clientRealm). Str("userRealm", userRealm). Logger(), } client := keycloak.New(baseURL, clientID, clientSecret, clientRealm, insecureSkipVerify) return NewWithClient(logger, client, userRealm) } // NewWithClient creates a new backend with the supplied keycloak client. func NewWithClient( logger log.Logger, client keycloak.Client, userRealm string, ) *Backend { return &Backend{ logger: logger, client: client, userRealm: userRealm, } } // CreateUser creates a user in the keycloak backend. func (b Backend) CreateUser(ctx context.Context, invitation *invitations.Invitation) (string, error) { u := uuid.New() b.logger.Info(). Str("email", invitation.InvitedUserEmailAddress). Msg("Creating new user") user := &libregraph.User{ Mail: &invitation.InvitedUserEmailAddress, AccountEnabled: boolP(true), OnPremisesSamAccountName: invitation.InvitedUserEmailAddress, Id: stringP(u.String()), UserType: stringP(userType), } id, err := b.client.CreateUser(ctx, b.userRealm, user, userRequiredActions) if err != nil { b.logger.Error(). Str("userID", u.String()). Str("email", invitation.InvitedUserEmailAddress). Err(err). Msg("Failed to create user") return "", err } return id, nil } // CanSendMail returns true because keycloak does allow sending mail. func (b Backend) CanSendMail() bool { return true } // SendMail sends a mail to the user with details on how to redeem the invitation. func (b Backend) SendMail(ctx context.Context, id string) error { return b.client.SendActionsMail(ctx, b.userRealm, id, userRequiredActions) } func boolP(b bool) *bool { return &b } func stringP(s string) *string { return &s }