zitadel/internal/api/grpc/user/schema/v3alpha/schema.go
Livio Spring 0e181b218c
feat: implement user schema management (#7416)
This PR adds the functionality to manage user schemas through the new user schema service.
It includes the possibility to create a basic JSON schema and also provides a way on defining permissions (read, write) for owner and self context with an annotation.

Further annotations for OIDC claims and SAML attribute mappings will follow.

A guide on how to create a schema and assign permissions has been started. It will be extended though out the process of implementing the schema and users based on those.

Note:
This feature is in an early stage and therefore not enabled by default. To test it out, please enable the UserSchema feature flag on your instance / system though the feature service.
2024-03-12 13:50:13 +00:00

151 lines
5.3 KiB
Go

package schema
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/zerrors"
schema "github.com/zitadel/zitadel/pkg/grpc/user/schema/v3alpha"
)
func (s *Server) CreateUserSchema(ctx context.Context, req *schema.CreateUserSchemaRequest) (*schema.CreateUserSchemaResponse, error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
userSchema, err := createUserSchemaToCommand(req, authz.GetInstance(ctx).InstanceID())
if err != nil {
return nil, err
}
id, details, err := s.command.CreateUserSchema(ctx, userSchema)
if err != nil {
return nil, err
}
return &schema.CreateUserSchemaResponse{
Id: id,
Details: object.DomainToDetailsPb(details),
}, nil
}
func (s *Server) UpdateUserSchema(ctx context.Context, req *schema.UpdateUserSchemaRequest) (*schema.UpdateUserSchemaResponse, error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
userSchema, err := updateUserSchemaToCommand(req, authz.GetInstance(ctx).InstanceID())
if err != nil {
return nil, err
}
details, err := s.command.UpdateUserSchema(ctx, userSchema)
if err != nil {
return nil, err
}
return &schema.UpdateUserSchemaResponse{
Details: object.DomainToDetailsPb(details),
}, nil
}
func (s *Server) DeactivateUserSchema(ctx context.Context, req *schema.DeactivateUserSchemaRequest) (*schema.DeactivateUserSchemaResponse, error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
details, err := s.command.DeactivateUserSchema(ctx, req.GetId(), authz.GetInstance(ctx).InstanceID())
if err != nil {
return nil, err
}
return &schema.DeactivateUserSchemaResponse{
Details: object.DomainToDetailsPb(details),
}, nil
}
func (s *Server) ReactivateUserSchema(ctx context.Context, req *schema.ReactivateUserSchemaRequest) (*schema.ReactivateUserSchemaResponse, error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
details, err := s.command.ReactivateUserSchema(ctx, req.GetId(), authz.GetInstance(ctx).InstanceID())
if err != nil {
return nil, err
}
return &schema.ReactivateUserSchemaResponse{
Details: object.DomainToDetailsPb(details),
}, nil
}
func (s *Server) DeleteUserSchema(ctx context.Context, req *schema.DeleteUserSchemaRequest) (*schema.DeleteUserSchemaResponse, error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
details, err := s.command.DeleteUserSchema(ctx, req.GetId(), authz.GetInstance(ctx).InstanceID())
if err != nil {
return nil, err
}
return &schema.DeleteUserSchemaResponse{
Details: object.DomainToDetailsPb(details),
}, nil
}
func checkUserSchemaEnabled(ctx context.Context) error {
if authz.GetInstance(ctx).Features().UserSchema {
return nil
}
return zerrors.ThrowPreconditionFailed(nil, "SCHEMA-SFjk3", "Errors.UserSchema.NotEnabled")
}
func createUserSchemaToCommand(req *schema.CreateUserSchemaRequest, resourceOwner string) (*command.CreateUserSchema, error) {
schema, err := req.GetSchema().MarshalJSON()
if err != nil {
return nil, err
}
return &command.CreateUserSchema{
ResourceOwner: resourceOwner,
Type: req.GetType(),
Schema: schema,
PossibleAuthenticators: authenticatorsToDomain(req.GetPossibleAuthenticators()),
}, nil
}
func updateUserSchemaToCommand(req *schema.UpdateUserSchemaRequest, resourceOwner string) (*command.UpdateUserSchema, error) {
schema, err := req.GetSchema().MarshalJSON()
if err != nil {
return nil, err
}
return &command.UpdateUserSchema{
ID: req.GetId(),
ResourceOwner: resourceOwner,
Type: req.Type,
Schema: schema,
PossibleAuthenticators: authenticatorsToDomain(req.GetPossibleAuthenticators()),
}, nil
}
func authenticatorsToDomain(authenticators []schema.AuthenticatorType) []domain.AuthenticatorType {
types := make([]domain.AuthenticatorType, len(authenticators))
for i, authenticator := range authenticators {
types[i] = authenticatorTypeToDomain(authenticator)
}
return types
}
func authenticatorTypeToDomain(authenticator schema.AuthenticatorType) domain.AuthenticatorType {
switch authenticator {
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED:
return domain.AuthenticatorTypeUnspecified
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME:
return domain.AuthenticatorTypeUsername
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_PASSWORD:
return domain.AuthenticatorTypePassword
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_WEBAUTHN:
return domain.AuthenticatorTypeWebAuthN
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_TOTP:
return domain.AuthenticatorTypeTOTP
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_OTP_EMAIL:
return domain.AuthenticatorTypeOTPEmail
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_OTP_SMS:
return domain.AuthenticatorTypeOTPSMS
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_AUTHENTICATION_KEY:
return domain.AuthenticatorTypeAuthenticationKey
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_IDENTITY_PROVIDER:
return domain.AuthenticatorTypeIdentityProvider
default:
return domain.AuthenticatorTypeUnspecified
}
}