mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 11:27:33 +00:00
feat: Instance create (#4502)
* feat(instance): implement create instance with direct machine user and credentials
* fix: deprecated add endpoint and variable declaration
* fix(instance): update logic for pats and machinekeys
* fix(instance): unit test corrections and additional unit test for pats and machinekeys
* fix(instance-create): include review changes
* fix(instance-create): linter fixes
* move iframe usage to solution scenarios configurations
* Revert "move iframe usage to solution scenarios configurations"
This reverts commit 9db31f3808
.
* fix merge
* fix: add review suggestions
Co-authored-by: Livio Spring <livio.a@gmail.com>
* fix: add review changes
* fix: add review changes for default definitions
* fix: add review changes for machinekey details
* fix: add machinekey output when setup with machineuser
* fix: add changes from review
* fix instance converter for machine and allow overwriting of further machine fields
Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
@@ -22,6 +22,7 @@ import (
|
||||
action_grpc "github.com/zitadel/zitadel/internal/api/grpc/action"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/authn"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/management"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
@@ -597,7 +598,7 @@ func (s *Server) importData(ctx context.Context, orgs []*admin_pb.DataOrg) (*adm
|
||||
if org.MachineKeys != nil {
|
||||
for _, key := range org.GetMachineKeys() {
|
||||
logging.Debugf("import machine_user_key: %s", key.KeyId)
|
||||
_, err := s.command.AddUserMachineKeyWithID(ctx, &domain.MachineKey{
|
||||
_, err := s.command.AddUserMachineKey(ctx, &command.MachineKey{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: key.UserId,
|
||||
ResourceOwner: org.GetOrgId(),
|
||||
@@ -606,7 +607,7 @@ func (s *Server) importData(ctx context.Context, orgs []*admin_pb.DataOrg) (*adm
|
||||
Type: authn.KeyTypeToDomain(key.Type),
|
||||
ExpirationDate: key.ExpirationDate.AsTime(),
|
||||
PublicKey: key.PublicKey,
|
||||
}, org.GetOrgId())
|
||||
})
|
||||
if err != nil {
|
||||
errors = append(errors, &admin_pb.ImportDataError{Type: "machine_user_key", Id: key.KeyId, Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
|
@@ -9,11 +9,11 @@ import (
|
||||
admin_grpc "github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
)
|
||||
|
||||
func setUpOrgHumanToCommand(human *admin_grpc.SetUpOrgRequest_Human) command.AddHuman {
|
||||
func setUpOrgHumanToCommand(human *admin_grpc.SetUpOrgRequest_Human) *command.AddHuman {
|
||||
var lang language.Tag
|
||||
lang, err := language.Parse(human.Profile.PreferredLanguage)
|
||||
logging.OnError(err).Debug("unable to parse language")
|
||||
return command.AddHuman{
|
||||
return &command.AddHuman{
|
||||
Username: human.UserName,
|
||||
FirstName: human.Profile.FirstName,
|
||||
LastName: human.Profile.LastName,
|
||||
|
@@ -2,7 +2,6 @@ package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
@@ -731,27 +730,24 @@ func (s *Server) ListMachineKeys(ctx context.Context, req *mgmt_pb.ListMachineKe
|
||||
}
|
||||
|
||||
func (s *Server) AddMachineKey(ctx context.Context, req *mgmt_pb.AddMachineKeyRequest) (*mgmt_pb.AddMachineKeyResponse, error) {
|
||||
key, err := s.command.AddUserMachineKey(ctx, AddMachineKeyRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
machineKey := AddMachineKeyRequestToCommand(req, authz.GetCtxData(ctx).OrgID)
|
||||
details, err := s.command.AddUserMachineKey(ctx, machineKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyDetails, err := key.Detail()
|
||||
keyDetails, err := machineKey.Detail()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddMachineKeyResponse{
|
||||
KeyId: key.KeyID,
|
||||
KeyId: machineKey.KeyID,
|
||||
KeyDetails: keyDetails,
|
||||
Details: obj_grpc.AddToDetailsPb(
|
||||
key.Sequence,
|
||||
key.ChangeDate,
|
||||
key.ResourceOwner,
|
||||
),
|
||||
Details: obj_grpc.DomainToAddDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveMachineKey(ctx context.Context, req *mgmt_pb.RemoveMachineKeyRequest) (*mgmt_pb.RemoveMachineKeyResponse, error) {
|
||||
objectDetails, err := s.command.RemoveUserMachineKey(ctx, req.UserId, req.KeyId, authz.GetCtxData(ctx).OrgID)
|
||||
objectDetails, err := s.command.RemoveUserMachineKey(ctx, RemoveMachineKeyRequestToCommand(req, authz.GetCtxData(ctx).OrgID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -794,28 +790,21 @@ func (s *Server) ListPersonalAccessTokens(ctx context.Context, req *mgmt_pb.List
|
||||
}
|
||||
|
||||
func (s *Server) AddPersonalAccessToken(ctx context.Context, req *mgmt_pb.AddPersonalAccessTokenRequest) (*mgmt_pb.AddPersonalAccessTokenResponse, error) {
|
||||
expDate := time.Time{}
|
||||
if req.ExpirationDate != nil {
|
||||
expDate = req.ExpirationDate.AsTime()
|
||||
}
|
||||
scopes := []string{oidc.ScopeOpenID, z_oidc.ScopeUserMetaData, z_oidc.ScopeResourceOwner}
|
||||
pat, token, err := s.command.AddPersonalAccessToken(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, expDate, scopes, domain.UserTypeMachine)
|
||||
pat := AddPersonalAccessTokenRequestToCommand(req, authz.GetCtxData(ctx).OrgID, scopes, domain.UserTypeMachine)
|
||||
details, err := s.command.AddPersonalAccessToken(ctx, pat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddPersonalAccessTokenResponse{
|
||||
TokenId: pat.TokenID,
|
||||
Token: token,
|
||||
Details: obj_grpc.AddToDetailsPb(
|
||||
pat.Sequence,
|
||||
pat.ChangeDate,
|
||||
pat.ResourceOwner,
|
||||
),
|
||||
Token: pat.Token,
|
||||
Details: obj_grpc.DomainToAddDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemovePersonalAccessToken(ctx context.Context, req *mgmt_pb.RemovePersonalAccessTokenRequest) (*mgmt_pb.RemovePersonalAccessTokenResponse, error) {
|
||||
objectDetails, err := s.command.RemovePersonalAccessToken(ctx, req.UserId, req.TokenId, authz.GetCtxData(ctx).OrgID)
|
||||
objectDetails, err := s.command.RemovePersonalAccessToken(ctx, RemovePersonalAccessTokenRequestToCommand(req, authz.GetCtxData(ctx).OrgID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -255,21 +255,59 @@ func ListMachineKeysRequestToQuery(ctx context.Context, req *mgmt_pb.ListMachine
|
||||
|
||||
}
|
||||
|
||||
func AddMachineKeyRequestToDomain(req *mgmt_pb.AddMachineKeyRequest) *domain.MachineKey {
|
||||
func AddMachineKeyRequestToCommand(req *mgmt_pb.AddMachineKeyRequest, resourceOwner string) *command.MachineKey {
|
||||
expDate := time.Time{}
|
||||
if req.ExpirationDate != nil {
|
||||
expDate = req.ExpirationDate.AsTime()
|
||||
}
|
||||
|
||||
return &domain.MachineKey{
|
||||
return &command.MachineKey{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: req.UserId,
|
||||
AggregateID: req.UserId,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
ExpirationDate: expDate,
|
||||
Type: authn.KeyTypeToDomain(req.Type),
|
||||
}
|
||||
}
|
||||
|
||||
func RemoveMachineKeyRequestToCommand(req *mgmt_pb.RemoveMachineKeyRequest, resourceOwner string) *command.MachineKey {
|
||||
return &command.MachineKey{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: req.UserId,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
KeyID: req.KeyId,
|
||||
}
|
||||
}
|
||||
|
||||
func AddPersonalAccessTokenRequestToCommand(req *mgmt_pb.AddPersonalAccessTokenRequest, resourceOwner string, scopes []string, allowedUserType domain.UserType) *command.PersonalAccessToken {
|
||||
expDate := time.Time{}
|
||||
if req.ExpirationDate != nil {
|
||||
expDate = req.ExpirationDate.AsTime()
|
||||
}
|
||||
|
||||
return &command.PersonalAccessToken{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: req.UserId,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
ExpirationDate: expDate,
|
||||
Scopes: scopes,
|
||||
AllowedUserType: allowedUserType,
|
||||
}
|
||||
}
|
||||
|
||||
func RemovePersonalAccessTokenRequestToCommand(req *mgmt_pb.RemovePersonalAccessTokenRequest, resourceOwner string) *command.PersonalAccessToken {
|
||||
return &command.PersonalAccessToken{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: req.UserId,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
TokenID: req.TokenId,
|
||||
}
|
||||
}
|
||||
|
||||
func ListPersonalAccessTokensRequestToQuery(ctx context.Context, req *mgmt_pb.ListPersonalAccessTokensRequest) (*query.PersonalAccessTokenSearchQueries, error) {
|
||||
resourceOwner, err := query.NewPersonalAccessTokenResourceOwnerSearchQuery(authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
|
@@ -41,7 +41,7 @@ func (s *Server) GetInstance(ctx context.Context, req *system_pb.GetInstanceRequ
|
||||
}
|
||||
|
||||
func (s *Server) AddInstance(ctx context.Context, req *system_pb.AddInstanceRequest) (*system_pb.AddInstanceResponse, error) {
|
||||
id, details, err := s.command.SetUpInstance(ctx, AddInstancePbToSetupInstance(req, s.defaultInstance, s.externalDomain))
|
||||
id, _, _, details, err := s.command.SetUpInstance(ctx, AddInstancePbToSetupInstance(req, s.defaultInstance, s.externalDomain))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -62,6 +62,28 @@ func (s *Server) UpdateInstance(ctx context.Context, req *system_pb.UpdateInstan
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) CreateInstance(ctx context.Context, req *system_pb.CreateInstanceRequest) (*system_pb.CreateInstanceResponse, error) {
|
||||
id, pat, key, details, err := s.command.SetUpInstance(ctx, CreateInstancePbToSetupInstance(req, s.defaultInstance, s.externalDomain))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var machineKey []byte
|
||||
if key != nil {
|
||||
machineKey, err = key.Detail()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &system_pb.CreateInstanceResponse{
|
||||
Pat: pat,
|
||||
MachineKey: machineKey,
|
||||
InstanceId: id,
|
||||
Details: object.AddToDetailsPb(details.Sequence, details.EventDate, details.ResourceOwner),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveInstance(ctx context.Context, req *system_pb.RemoveInstanceRequest) (*system_pb.RemoveInstanceResponse, error) {
|
||||
ctx = authz.WithInstanceID(ctx, req.InstanceId)
|
||||
details, err := s.command.RemoveInstance(ctx, req.InstanceId)
|
||||
|
@@ -3,10 +3,13 @@ package system
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/authn"
|
||||
instance_grpc "github.com/zitadel/zitadel/internal/api/grpc/instance"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
z_oidc "github.com/zitadel/zitadel/internal/api/oidc"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
@@ -14,6 +17,123 @@ import (
|
||||
system_pb "github.com/zitadel/zitadel/pkg/grpc/system"
|
||||
)
|
||||
|
||||
func CreateInstancePbToSetupInstance(req *system_pb.CreateInstanceRequest, defaultInstance command.InstanceSetup, externalDomain string) *command.InstanceSetup {
|
||||
if req.InstanceName != "" {
|
||||
defaultInstance.InstanceName = req.InstanceName
|
||||
defaultInstance.Org.Name = req.InstanceName
|
||||
}
|
||||
if req.CustomDomain != "" {
|
||||
defaultInstance.CustomDomain = req.CustomDomain
|
||||
}
|
||||
if req.FirstOrgName != "" {
|
||||
defaultInstance.Org.Name = req.FirstOrgName
|
||||
}
|
||||
|
||||
if user := req.GetMachine(); user != nil {
|
||||
defaultMachine := command.AddMachine{}
|
||||
if defaultInstance.Org.Machine != nil {
|
||||
defaultMachine = *defaultInstance.Org.Machine
|
||||
}
|
||||
|
||||
defaultInstance.Org.Machine = createInstancePbToAddMachine(user, defaultMachine)
|
||||
defaultInstance.Org.Human = nil
|
||||
}
|
||||
if user := req.GetHuman(); user != nil {
|
||||
defaultHuman := command.AddHuman{}
|
||||
if defaultInstance.Org.Human != nil {
|
||||
defaultHuman = *defaultInstance.Org.Human
|
||||
}
|
||||
|
||||
defaultInstance.Org.Human = createInstancePbToAddHuman(user, defaultHuman, defaultInstance.DomainPolicy.UserLoginMustBeDomain, defaultInstance.Org.Name, externalDomain)
|
||||
defaultInstance.Org.Machine = nil
|
||||
}
|
||||
|
||||
if lang := language.Make(req.DefaultLanguage); lang != language.Und {
|
||||
defaultInstance.DefaultLanguage = lang
|
||||
}
|
||||
|
||||
return &defaultInstance
|
||||
}
|
||||
|
||||
func createInstancePbToAddHuman(user *system_pb.CreateInstanceRequest_Human, defaultHuman command.AddHuman, userLoginMustBeDomain bool, org, externalDomain string) *command.AddHuman {
|
||||
if user.Email != nil {
|
||||
defaultHuman.Email.Address = user.Email.Email
|
||||
defaultHuman.Email.Verified = user.Email.IsEmailVerified
|
||||
}
|
||||
if user.Profile != nil {
|
||||
if user.Profile.FirstName != "" {
|
||||
defaultHuman.FirstName = user.Profile.FirstName
|
||||
}
|
||||
if user.Profile.LastName != "" {
|
||||
defaultHuman.LastName = user.Profile.LastName
|
||||
}
|
||||
if user.Profile.PreferredLanguage != "" {
|
||||
lang, err := language.Parse(user.Profile.PreferredLanguage)
|
||||
if err == nil {
|
||||
defaultHuman.PreferredLanguage = lang
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if default username is email style or else append @<orgname>.<custom-domain>
|
||||
// this way we have the same value as before changing `UserLoginMustBeDomain` to false
|
||||
if !userLoginMustBeDomain && !strings.Contains(defaultHuman.Username, "@") {
|
||||
defaultHuman.Username = defaultHuman.Username + "@" + domain.NewIAMDomainName(org, externalDomain)
|
||||
}
|
||||
if user.UserName != "" {
|
||||
defaultHuman.Username = user.UserName
|
||||
}
|
||||
|
||||
if user.Password != nil {
|
||||
defaultHuman.Password = user.Password.Password
|
||||
defaultHuman.PasswordChangeRequired = user.Password.PasswordChangeRequired
|
||||
}
|
||||
return &defaultHuman
|
||||
}
|
||||
|
||||
func createInstancePbToAddMachine(user *system_pb.CreateInstanceRequest_Machine, defaultMachine command.AddMachine) *command.AddMachine {
|
||||
machine := command.Machine{}
|
||||
if defaultMachine.Machine != nil {
|
||||
machine = *defaultMachine.Machine
|
||||
}
|
||||
if user.UserName != "" {
|
||||
machine.Username = user.UserName
|
||||
}
|
||||
if user.Name != "" {
|
||||
machine.Name = user.Name
|
||||
}
|
||||
defaultMachine.Machine = &machine
|
||||
|
||||
if defaultMachine.Pat != nil || user.PersonalAccessToken != nil {
|
||||
pat := command.AddPat{}
|
||||
if defaultMachine.Pat != nil {
|
||||
pat = *defaultMachine.Pat
|
||||
}
|
||||
// scopes are currently static and can not be overwritten
|
||||
pat.Scopes = []string{oidc.ScopeOpenID, z_oidc.ScopeUserMetaData, z_oidc.ScopeResourceOwner}
|
||||
if user.PersonalAccessToken.ExpirationDate != nil {
|
||||
pat.ExpirationDate = user.PersonalAccessToken.ExpirationDate.AsTime()
|
||||
}
|
||||
defaultMachine.Pat = &pat
|
||||
}
|
||||
|
||||
if defaultMachine.MachineKey != nil || user.MachineKey != nil {
|
||||
machineKey := command.AddMachineKey{}
|
||||
if defaultMachine.MachineKey != nil {
|
||||
machineKey = *defaultMachine.MachineKey
|
||||
}
|
||||
if user.MachineKey != nil {
|
||||
if user.MachineKey.Type != 0 {
|
||||
machineKey.Type = authn.KeyTypeToDomain(user.MachineKey.Type)
|
||||
}
|
||||
if user.MachineKey.ExpirationDate != nil {
|
||||
machineKey.ExpirationDate = user.MachineKey.ExpirationDate.AsTime()
|
||||
}
|
||||
}
|
||||
defaultMachine.MachineKey = &machineKey
|
||||
}
|
||||
return &defaultMachine
|
||||
}
|
||||
|
||||
func AddInstancePbToSetupInstance(req *system_pb.AddInstanceRequest, defaultInstance command.InstanceSetup, externalDomain string) *command.InstanceSetup {
|
||||
if req.InstanceName != "" {
|
||||
defaultInstance.InstanceName = req.InstanceName
|
||||
|
Reference in New Issue
Block a user