mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:47:33 +00:00
feat: features (#1427)
* features * features * features * fix json tags * add features handler to auth * mocks for tests * add setup step * fixes * add featurelist to auth api * grandfather state and typos * typo * merge new-eventstore * fix login policy tests * label policy in features * audit log retention
This commit is contained in:
88
internal/api/grpc/admin/features.go
Normal file
88
internal/api/grpc/admin/features.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
features_grpc "github.com/caos/zitadel/internal/api/grpc/features"
|
||||
object_grpc "github.com/caos/zitadel/internal/api/grpc/object"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
|
||||
)
|
||||
|
||||
func (s *Server) GetDefaultFeatures(ctx context.Context, _ *admin_pb.GetDefaultFeaturesRequest) (*admin_pb.GetDefaultFeaturesResponse, error) {
|
||||
features, err := s.features.GetDefaultFeatures(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetDefaultFeaturesResponse{
|
||||
Features: features_grpc.FeaturesFromModel(features),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetDefaultFeatures(ctx context.Context, req *admin_pb.SetDefaultFeaturesRequest) (*admin_pb.SetDefaultFeaturesResponse, error) {
|
||||
details, err := s.command.SetDefaultFeatures(ctx, setDefaultFeaturesRequestToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.SetDefaultFeaturesResponse{
|
||||
Details: object_grpc.DomainToChangeDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetOrgFeatures(ctx context.Context, req *admin_pb.GetOrgFeaturesRequest) (*admin_pb.GetOrgFeaturesResponse, error) {
|
||||
features, err := s.features.GetOrgFeatures(ctx, req.OrgId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetOrgFeaturesResponse{
|
||||
Features: features_grpc.FeaturesFromModel(features),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetOrgFeatures(ctx context.Context, req *admin_pb.SetOrgFeaturesRequest) (*admin_pb.SetOrgFeaturesResponse, error) {
|
||||
details, err := s.command.SetOrgFeatures(ctx, req.OrgId, setOrgFeaturesRequestToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.SetOrgFeaturesResponse{
|
||||
Details: object_grpc.DomainToChangeDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ResetOrgFeatures(ctx context.Context, req *admin_pb.ResetOrgFeaturesRequest) (*admin_pb.ResetOrgFeaturesResponse, error) {
|
||||
details, err := s.command.RemoveOrgFeatures(ctx, req.OrgId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.ResetOrgFeaturesResponse{
|
||||
Details: object_grpc.DomainToChangeDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func setDefaultFeaturesRequestToDomain(req *admin_pb.SetDefaultFeaturesRequest) *domain.Features {
|
||||
return &domain.Features{
|
||||
TierName: req.TierName,
|
||||
TierDescription: req.Description,
|
||||
AuditLogRetention: req.AuditLogRetention.AsDuration(),
|
||||
LoginPolicyFactors: req.LoginPolicyFactors,
|
||||
LoginPolicyIDP: req.LoginPolicyIdp,
|
||||
LoginPolicyPasswordless: req.LoginPolicyPasswordless,
|
||||
LoginPolicyRegistration: req.LoginPolicyRegistration,
|
||||
LoginPolicyUsernameLogin: req.LoginPolicyUsernameLogin,
|
||||
}
|
||||
}
|
||||
|
||||
func setOrgFeaturesRequestToDomain(req *admin_pb.SetOrgFeaturesRequest) *domain.Features {
|
||||
return &domain.Features{
|
||||
TierName: req.TierName,
|
||||
TierDescription: req.Description,
|
||||
State: features_grpc.FeaturesStateToDomain(req.State),
|
||||
StateDescription: req.StateDescription,
|
||||
AuditLogRetention: req.AuditLogRetention.AsDuration(),
|
||||
LoginPolicyFactors: req.LoginPolicyFactors,
|
||||
LoginPolicyIDP: req.LoginPolicyIdp,
|
||||
LoginPolicyPasswordless: req.LoginPolicyPasswordless,
|
||||
LoginPolicyRegistration: req.LoginPolicyRegistration,
|
||||
LoginPolicyUsernameLogin: req.LoginPolicyUsernameLogin,
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/caos/zitadel/internal/admin/repository"
|
||||
"github.com/caos/zitadel/internal/admin/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
@@ -8,7 +10,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/pkg/grpc/admin"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,6 +26,7 @@ type Server struct {
|
||||
iam repository.IAMRepository
|
||||
administrator repository.AdministratorRepository
|
||||
repo repository.Repository
|
||||
features repository.FeaturesRepository
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@@ -39,6 +41,7 @@ func CreateServer(command *command.Commands, query *query.Queries, repo reposito
|
||||
iam: repo,
|
||||
administrator: repo,
|
||||
repo: repo,
|
||||
features: repo,
|
||||
}
|
||||
}
|
||||
|
||||
|
18
internal/api/grpc/auth/features.go
Normal file
18
internal/api/grpc/auth/features.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
auth_pb "github.com/caos/zitadel/pkg/grpc/auth"
|
||||
)
|
||||
|
||||
func (s *Server) ListMyZitadelFeatures(ctx context.Context, _ *auth_pb.ListMyZitadelFeaturesRequest) (*auth_pb.ListMyZitadelFeaturesResponse, error) {
|
||||
features, err := s.repo.GetOrgFeatures(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &auth_pb.ListMyZitadelFeaturesResponse{
|
||||
Result: features.FeatureList(),
|
||||
}, nil
|
||||
}
|
@@ -24,7 +24,11 @@ func (s *Server) GetMyUser(ctx context.Context, _ *auth_pb.GetMyUserRequest) (*a
|
||||
|
||||
func (s *Server) ListMyUserChanges(ctx context.Context, req *auth_pb.ListMyUserChangesRequest) (*auth_pb.ListMyUserChangesResponse, error) {
|
||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||
changes, err := s.repo.MyUserChanges(ctx, offset, limit, asc)
|
||||
features, err := s.repo.GetOrgFeatures(ctx, authz.GetCtxData(ctx).ResourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changes, err := s.repo.MyUserChanges(ctx, offset, limit, asc, features.AuditLogRetention)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
64
internal/api/grpc/features/features.go
Normal file
64
internal/api/grpc/features/features.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package features
|
||||
|
||||
import (
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
object_grpc "github.com/caos/zitadel/internal/api/grpc/object"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
features_model "github.com/caos/zitadel/internal/features/model"
|
||||
features_pb "github.com/caos/zitadel/pkg/grpc/features"
|
||||
)
|
||||
|
||||
func FeaturesFromModel(features *features_model.FeaturesView) *features_pb.Features {
|
||||
return &features_pb.Features{
|
||||
Details: object_grpc.ToViewDetailsPb(features.Sequence, features.CreationDate, features.ChangeDate, features.AggregateID),
|
||||
Tier: FeatureTierToPb(features.TierName, features.TierDescription, features.State, features.StateDescription),
|
||||
IsDefault: features.Default,
|
||||
|
||||
AuditLogRetention: durationpb.New(features.AuditLogRetention),
|
||||
LoginPolicyFactors: features.LoginPolicyFactors,
|
||||
LoginPolicyIdp: features.LoginPolicyIDP,
|
||||
LoginPolicyPasswordless: features.LoginPolicyPasswordless,
|
||||
LoginPolicyRegistration: features.LoginPolicyRegistration,
|
||||
LoginPolicyUsernameLogin: features.LoginPolicyUsernameLogin,
|
||||
}
|
||||
}
|
||||
|
||||
func FeatureTierToPb(name, description string, status domain.FeaturesState, statusDescription string) *features_pb.FeatureTier {
|
||||
return &features_pb.FeatureTier{
|
||||
Name: name,
|
||||
Description: description,
|
||||
State: FeaturesStateToPb(status),
|
||||
StatusInfo: statusDescription,
|
||||
}
|
||||
}
|
||||
|
||||
func FeaturesStateToPb(status domain.FeaturesState) features_pb.FeaturesState {
|
||||
switch status {
|
||||
case domain.FeaturesStateActive:
|
||||
return features_pb.FeaturesState_FEATURES_STATE_ACTIVE
|
||||
case domain.FeaturesStateActionRequired:
|
||||
return features_pb.FeaturesState_FEATURES_STATE_ACTION_REQUIRED
|
||||
case domain.FeaturesStateCanceled:
|
||||
return features_pb.FeaturesState_FEATURES_STATE_CANCELED
|
||||
case domain.FeaturesStateGrandfathered:
|
||||
return features_pb.FeaturesState_FEATURES_STATE_GRANDFATHERED
|
||||
default:
|
||||
return features_pb.FeaturesState_FEATURES_STATE_ACTIVE
|
||||
}
|
||||
}
|
||||
|
||||
func FeaturesStateToDomain(status features_pb.FeaturesState) domain.FeaturesState {
|
||||
switch status {
|
||||
case features_pb.FeaturesState_FEATURES_STATE_ACTIVE:
|
||||
return domain.FeaturesStateActive
|
||||
case features_pb.FeaturesState_FEATURES_STATE_ACTION_REQUIRED:
|
||||
return domain.FeaturesStateActionRequired
|
||||
case features_pb.FeaturesState_FEATURES_STATE_CANCELED:
|
||||
return domain.FeaturesStateCanceled
|
||||
case features_pb.FeaturesState_FEATURES_STATE_GRANDFATHERED:
|
||||
return domain.FeaturesStateGrandfathered
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
19
internal/api/grpc/management/features.go
Normal file
19
internal/api/grpc/management/features.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
features_grpc "github.com/caos/zitadel/internal/api/grpc/features"
|
||||
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
func (s *Server) GetFeatures(ctx context.Context, req *mgmt_pb.GetFeaturesRequest) (*mgmt_pb.GetFeaturesResponse, error) {
|
||||
features, err := s.features.GetOrgFeatures(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetFeaturesResponse{
|
||||
Features: features_grpc.FeaturesFromModel(features),
|
||||
}, nil
|
||||
}
|
@@ -33,7 +33,11 @@ func (s *Server) GetOrgByDomainGlobal(ctx context.Context, req *mgmt_pb.GetOrgBy
|
||||
|
||||
func (s *Server) ListOrgChanges(ctx context.Context, req *mgmt_pb.ListOrgChangesRequest) (*mgmt_pb.ListOrgChangesResponse, error) {
|
||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||
response, err := s.org.OrgChanges(ctx, authz.GetCtxData(ctx).OrgID, offset, limit, asc)
|
||||
features, err := s.features.GetOrgFeatures(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := s.org.OrgChanges(ctx, authz.GetCtxData(ctx).OrgID, offset, limit, asc, features.AuditLogRetention)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -73,7 +73,11 @@ func (s *Server) ListGrantedProjects(ctx context.Context, req *mgmt_pb.ListGrant
|
||||
|
||||
func (s *Server) ListProjectChanges(ctx context.Context, req *mgmt_pb.ListProjectChangesRequest) (*mgmt_pb.ListProjectChangesResponse, error) {
|
||||
offset, limit, asc := object_grpc.ListQueryToModel(req.Query)
|
||||
res, err := s.project.ProjectChanges(ctx, req.ProjectId, offset, limit, asc)
|
||||
features, err := s.features.GetOrgFeatures(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.project.ProjectChanges(ctx, req.ProjectId, offset, limit, asc, features.AuditLogRetention)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -42,7 +42,11 @@ func (s *Server) ListApps(ctx context.Context, req *mgmt_pb.ListAppsRequest) (*m
|
||||
|
||||
func (s *Server) ListAppChanges(ctx context.Context, req *mgmt_pb.ListAppChangesRequest) (*mgmt_pb.ListAppChangesResponse, error) {
|
||||
offset, limit, asc := object_grpc.ListQueryToModel(req.Query)
|
||||
res, err := s.project.ApplicationChanges(ctx, req.ProjectId, req.AppId, offset, limit, asc)
|
||||
features, err := s.features.GetOrgFeatures(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.project.ApplicationChanges(ctx, req.ProjectId, req.AppId, offset, limit, asc, features.AuditLogRetention)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package management
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/api/grpc/server"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
@@ -9,7 +11,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/management/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/pkg/grpc/management"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -27,6 +28,7 @@ type Server struct {
|
||||
user repository.UserRepository
|
||||
usergrant repository.UserGrantRepository
|
||||
iam repository.IamRepository
|
||||
features repository.FeaturesRepository
|
||||
authZ authz.Config
|
||||
systemDefaults systemdefaults.SystemDefaults
|
||||
}
|
||||
@@ -44,6 +46,7 @@ func CreateServer(command *command.Commands, query *query.Queries, repo reposito
|
||||
user: repo,
|
||||
usergrant: repo,
|
||||
iam: repo,
|
||||
features: repo,
|
||||
systemDefaults: sd,
|
||||
}
|
||||
}
|
||||
|
@@ -53,7 +53,11 @@ func (s *Server) ListUsers(ctx context.Context, req *mgmt_pb.ListUsersRequest) (
|
||||
|
||||
func (s *Server) ListUserChanges(ctx context.Context, req *mgmt_pb.ListUserChangesRequest) (*mgmt_pb.ListUserChangesResponse, error) {
|
||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||
res, err := s.user.UserChanges(ctx, req.UserId, offset, limit, asc)
|
||||
features, err := s.features.GetOrgFeatures(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.user.UserChanges(ctx, req.UserId, offset, limit, asc, features.AuditLogRetention)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ func ModelLoginPolicyToPb(policy *model.LoginPolicyView) *policy_pb.LoginPolicy
|
||||
IsDefault: policy.Default,
|
||||
AllowUsernamePassword: policy.AllowUsernamePassword,
|
||||
AllowRegister: policy.AllowRegister,
|
||||
AllowExternalIdp: policy.AllowRegister,
|
||||
AllowExternalIdp: policy.AllowExternalIDP,
|
||||
ForceMfa: policy.ForceMFA,
|
||||
PasswordlessType: ModelPasswordlessTypeToPb(policy.PasswordlessType),
|
||||
}
|
||||
|
@@ -37,6 +37,9 @@ func (v *verifierMock) ExistsOrg(ctx context.Context, orgID string) error {
|
||||
func (v *verifierMock) VerifierClientID(ctx context.Context, appName string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
func (v *verifierMock) CheckOrgFeatures(context.Context, string, ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_authorize(t *testing.T) {
|
||||
type args struct {
|
||||
|
Reference in New Issue
Block a user