mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-08 19:17:40 +00:00
feat: restrict login to specific org by id (scope) (#4294)
* feat: add new org scope * change default of UserLoginMustBeDomain to false * return resource owner claims * fix: use email style for first user * fix: ensure email style for default users (backwards compatibility) * change to external domain (as it was before UserLoginMustBeDomain change) * update e2e tests to use email style usernames * document new scope * lint e2e Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com>
This commit is contained in:
parent
c98170c19b
commit
7dfa1925cc
@ -310,6 +310,9 @@ DefaultInstance:
|
|||||||
Org:
|
Org:
|
||||||
Name:
|
Name:
|
||||||
Human:
|
Human:
|
||||||
|
# in case that UserLoginMustBeDomain is false (default) and if you don't overwrite the username with an email,
|
||||||
|
# it will be suffixed by the org domain (org-name + domain from config).
|
||||||
|
# for example: zitadel-admin in org `My Org` on domain.tld -> zitadel-admin@my-org.domain.tld
|
||||||
UserName: zitadel-admin
|
UserName: zitadel-admin
|
||||||
FirstName: ZITADEL
|
FirstName: ZITADEL
|
||||||
LastName: Admin
|
LastName: Admin
|
||||||
@ -383,7 +386,7 @@ DefaultInstance:
|
|||||||
ExpireWarnDays: 0
|
ExpireWarnDays: 0
|
||||||
MaxAgeDays: 0
|
MaxAgeDays: 0
|
||||||
DomainPolicy:
|
DomainPolicy:
|
||||||
UserLoginMustBeDomain: true
|
UserLoginMustBeDomain: false
|
||||||
ValidateOrgDomains: true
|
ValidateOrgDomains: true
|
||||||
SMTPSenderAddressMatchesInstanceDomain: false
|
SMTPSenderAddressMatchesInstanceDomain: false
|
||||||
LoginPolicy:
|
LoginPolicy:
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
crypto_db "github.com/zitadel/zitadel/internal/crypto/database"
|
crypto_db "github.com/zitadel/zitadel/internal/crypto/database"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -83,9 +84,17 @@ func (mig *FirstInstance) Execute(ctx context.Context) error {
|
|||||||
mig.instanceSetup.CustomDomain = mig.externalDomain
|
mig.instanceSetup.CustomDomain = mig.externalDomain
|
||||||
mig.instanceSetup.DefaultLanguage = mig.DefaultLanguage
|
mig.instanceSetup.DefaultLanguage = mig.DefaultLanguage
|
||||||
mig.instanceSetup.Org = mig.Org
|
mig.instanceSetup.Org = mig.Org
|
||||||
|
// check if username is email style or else append @<orgname>.<custom-domain>
|
||||||
|
//this way we have the same value as before changing `UserLoginMustBeDomain` to false
|
||||||
|
if !mig.instanceSetup.DomainPolicy.UserLoginMustBeDomain && !strings.Contains(mig.instanceSetup.Org.Human.Username, "@") {
|
||||||
|
mig.instanceSetup.Org.Human.Username = mig.instanceSetup.Org.Human.Username + "@" + domain.NewIAMDomainName(mig.instanceSetup.Org.Name, mig.instanceSetup.CustomDomain)
|
||||||
|
}
|
||||||
mig.instanceSetup.Org.Human.Email.Address = strings.TrimSpace(mig.instanceSetup.Org.Human.Email.Address)
|
mig.instanceSetup.Org.Human.Email.Address = strings.TrimSpace(mig.instanceSetup.Org.Human.Email.Address)
|
||||||
if mig.instanceSetup.Org.Human.Email.Address == "" {
|
if mig.instanceSetup.Org.Human.Email.Address == "" {
|
||||||
mig.instanceSetup.Org.Human.Email.Address = "admin@" + mig.instanceSetup.CustomDomain
|
mig.instanceSetup.Org.Human.Email.Address = mig.instanceSetup.Org.Human.Username
|
||||||
|
if !strings.Contains(mig.instanceSetup.Org.Human.Email.Address, "@") {
|
||||||
|
mig.instanceSetup.Org.Human.Email.Address = mig.instanceSetup.Org.Human.Username + "@" + domain.NewIAMDomainName(mig.instanceSetup.Org.Name, mig.instanceSetup.CustomDomain)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err = cmd.SetUpInstance(ctx, &mig.instanceSetup)
|
_, _, err = cmd.SetUpInstance(ctx, &mig.instanceSetup)
|
||||||
|
@ -4,13 +4,16 @@ FirstInstance:
|
|||||||
Org:
|
Org:
|
||||||
Name: ZITADEL
|
Name: ZITADEL
|
||||||
Human:
|
Human:
|
||||||
|
# in case that UserLoginMustBeDomain is false (default) and you don't overwrite the username with an email,
|
||||||
|
# it will be suffixed by the org domain (org-name + domain from config).
|
||||||
|
# for example: zitadel-admin in org ZITADEL on domain.tld -> zitadel-admin@zitadel.domain.tld
|
||||||
UserName: zitadel-admin
|
UserName: zitadel-admin
|
||||||
FirstName: ZITADEL
|
FirstName: ZITADEL
|
||||||
LastName: Admin
|
LastName: Admin
|
||||||
NickName:
|
NickName:
|
||||||
DisplayName:
|
DisplayName:
|
||||||
Email:
|
Email:
|
||||||
Address: #autogenerated if empty. uses domain from config and prefixes admin@. for example: admin@domain.tdl
|
Address: #uses the username if empty
|
||||||
Verified: true
|
Verified: true
|
||||||
PreferredLanguage: en
|
PreferredLanguage: en
|
||||||
Gender:
|
Gender:
|
||||||
|
@ -181,7 +181,7 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting admin repo: %w", err)
|
return fmt.Errorf("error starting admin repo: %w", err)
|
||||||
}
|
}
|
||||||
if err := apis.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.Database.Database(), config.DefaultInstance)); err != nil {
|
if err := apis.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.Database.Database(), config.DefaultInstance, config.ExternalDomain)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := apis.RegisterServer(ctx, admin.CreateServer(config.Database.Database(), commands, queries, config.SystemDefaults, adminRepo, config.ExternalSecure, keys.User)); err != nil {
|
if err := apis.RegisterServer(ctx, admin.CreateServer(config.Database.Database(), commands, queries, config.SystemDefaults, adminRepo, config.ExternalSecure, keys.User)); err != nil {
|
||||||
|
@ -25,6 +25,7 @@ In addition to the standard compliant scopes we utilize the following scopes.
|
|||||||
| Scopes | Example | Description |
|
| Scopes | Example | Description |
|
||||||
|:--------------------------------------------------|:-------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|:--------------------------------------------------|:-------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `urn:zitadel:iam:org:project:role:{rolekey}` | `urn:zitadel:iam:org:project:role:user` | By using this scope a client can request the claim urn:zitadel:iam:roles to be asserted when possible. As an alternative approach you can enable all roles to be asserted from the [project](../../guides/manage/console/projects) a client belongs to. |
|
| `urn:zitadel:iam:org:project:role:{rolekey}` | `urn:zitadel:iam:org:project:role:user` | By using this scope a client can request the claim urn:zitadel:iam:roles to be asserted when possible. As an alternative approach you can enable all roles to be asserted from the [project](../../guides/manage/console/projects) a client belongs to. |
|
||||||
|
| `urn:zitadel:iam:org:id:{id}` | `urn:zitadel:iam:org:id:178204173316174381` | When requesting this scope **ZITADEL** will enforce that the user is a member of the selected organization. If the organization does not exist a failure is displayed. It will assert the `urn:zitadel:iam:user:resourceowner` claims. |
|
||||||
| `urn:zitadel:iam:org:domain:primary:{domainname}` | `urn:zitadel:iam:org:domain:primary:acme.ch` | When requesting this scope **ZITADEL** will enforce that the user is a member of the selected organization. If the organization does not exist a failure is displayed |
|
| `urn:zitadel:iam:org:domain:primary:{domainname}` | `urn:zitadel:iam:org:domain:primary:acme.ch` | When requesting this scope **ZITADEL** will enforce that the user is a member of the selected organization. If the organization does not exist a failure is displayed |
|
||||||
| `urn:zitadel:iam:role:{rolename}` | | |
|
| `urn:zitadel:iam:role:{rolename}` | | |
|
||||||
| `urn:zitadel:iam:org:project:id:{projectid}:aud` | `urn:zitadel:iam:org:project:id:69234237810729019:aud` | By adding this scope, the requested projectid will be added to the audience of the access token |
|
| `urn:zitadel:iam:org:project:id:{projectid}:aud` | `urn:zitadel:iam:org:project:id:69234237810729019:aud` | By adding this scope, the requested projectid will be added to the audience of the access token |
|
||||||
|
@ -21,7 +21,7 @@ describe('humans', () => {
|
|||||||
cy.url().should('contain', 'users/create');
|
cy.url().should('contain', 'users/create');
|
||||||
cy.get('[formcontrolname="email"]').type(loginname('e2ehuman', Cypress.env('ORGANIZATION')));
|
cy.get('[formcontrolname="email"]').type(loginname('e2ehuman', Cypress.env('ORGANIZATION')));
|
||||||
//force needed due to the prefilled username prefix
|
//force needed due to the prefilled username prefix
|
||||||
cy.get('[formcontrolname="userName"]').type(testHumanUserNameAdd);
|
cy.get('[formcontrolname="userName"]').type(loginname(testHumanUserNameAdd, Cypress.env('ORGANIZATION')));
|
||||||
cy.get('[formcontrolname="firstName"]').type('e2ehumanfirstname');
|
cy.get('[formcontrolname="firstName"]').type('e2ehumanfirstname');
|
||||||
cy.get('[formcontrolname="lastName"]').type('e2ehumanlastname');
|
cy.get('[formcontrolname="lastName"]').type('e2ehumanlastname');
|
||||||
cy.get('[formcontrolname="phone"]').type('+41 123456789');
|
cy.get('[formcontrolname="phone"]').type('+41 123456789');
|
||||||
@ -35,7 +35,7 @@ describe('humans', () => {
|
|||||||
describe('remove', () => {
|
describe('remove', () => {
|
||||||
before('ensure it exists', () => {
|
before('ensure it exists', () => {
|
||||||
apiAuth().then((api) => {
|
apiAuth().then((api) => {
|
||||||
ensureHumanUserExists(api, testHumanUserNameRemove).then(() => {
|
ensureHumanUserExists(api, loginname(testHumanUserNameRemove, Cypress.env('ORGANIZATION'))).then(() => {
|
||||||
cy.visit(humansPath);
|
cy.visit(humansPath);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -45,9 +45,7 @@ describe('machines', () => {
|
|||||||
// .trigger('mouseover')
|
// .trigger('mouseover')
|
||||||
.find('[data-e2e="enabled-delete-button"]')
|
.find('[data-e2e="enabled-delete-button"]')
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
cy.get('[data-e2e="confirm-dialog-input"]')
|
cy.get('[data-e2e="confirm-dialog-input"]').focus().type(testMachineUserNameRemove);
|
||||||
.focus()
|
|
||||||
.type(loginname(testMachineUserNameRemove, Cypress.env('ORGANIZATION')));
|
|
||||||
cy.get('[data-e2e="confirm-dialog-button"]').click();
|
cy.get('[data-e2e="confirm-dialog-button"]').click();
|
||||||
cy.get('.data-e2e-success');
|
cy.get('.data-e2e-success');
|
||||||
cy.wait(200);
|
cy.wait(200);
|
||||||
|
@ -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) {
|
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))
|
id, details, err := s.command.SetUpInstance(ctx, AddInstancePbToSetupInstance(req, s.defaultInstance, s.externalDomain))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
instance_grpc "github.com/zitadel/zitadel/internal/api/grpc/instance"
|
instance_grpc "github.com/zitadel/zitadel/internal/api/grpc/instance"
|
||||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||||
"github.com/zitadel/zitadel/internal/command"
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
instance_pb "github.com/zitadel/zitadel/pkg/grpc/instance"
|
instance_pb "github.com/zitadel/zitadel/pkg/grpc/instance"
|
||||||
system_pb "github.com/zitadel/zitadel/pkg/grpc/system"
|
system_pb "github.com/zitadel/zitadel/pkg/grpc/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddInstancePbToSetupInstance(req *system_pb.AddInstanceRequest, defaultInstance command.InstanceSetup) *command.InstanceSetup {
|
func AddInstancePbToSetupInstance(req *system_pb.AddInstanceRequest, defaultInstance command.InstanceSetup, externalDomain string) *command.InstanceSetup {
|
||||||
if req.InstanceName != "" {
|
if req.InstanceName != "" {
|
||||||
defaultInstance.InstanceName = req.InstanceName
|
defaultInstance.InstanceName = req.InstanceName
|
||||||
defaultInstance.Org.Name = req.InstanceName
|
defaultInstance.Org.Name = req.InstanceName
|
||||||
@ -40,6 +43,11 @@ func AddInstancePbToSetupInstance(req *system_pb.AddInstanceRequest, defaultInst
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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 !defaultInstance.DomainPolicy.UserLoginMustBeDomain && !strings.Contains(defaultInstance.Org.Human.Username, "@") {
|
||||||
|
defaultInstance.Org.Human.Username = defaultInstance.Org.Human.Username + "@" + domain.NewIAMDomainName(defaultInstance.Org.Name, externalDomain)
|
||||||
|
}
|
||||||
if req.OwnerUserName != "" {
|
if req.OwnerUserName != "" {
|
||||||
defaultInstance.Org.Human.Username = req.OwnerUserName
|
defaultInstance.Org.Human.Username = req.OwnerUserName
|
||||||
}
|
}
|
||||||
|
@ -25,25 +25,29 @@ type Server struct {
|
|||||||
command *command.Commands
|
command *command.Commands
|
||||||
query *query.Queries
|
query *query.Queries
|
||||||
administrator repository.AdministratorRepository
|
administrator repository.AdministratorRepository
|
||||||
DefaultInstance command.InstanceSetup
|
defaultInstance command.InstanceSetup
|
||||||
|
externalDomain string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Repository eventsourcing.Config
|
Repository eventsourcing.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateServer(command *command.Commands,
|
func CreateServer(
|
||||||
|
command *command.Commands,
|
||||||
query *query.Queries,
|
query *query.Queries,
|
||||||
repo repository.Repository,
|
repo repository.Repository,
|
||||||
database string,
|
database string,
|
||||||
defaultInstance command.InstanceSetup,
|
defaultInstance command.InstanceSetup,
|
||||||
|
externalDomain string,
|
||||||
) *Server {
|
) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
command: command,
|
command: command,
|
||||||
query: query,
|
query: query,
|
||||||
administrator: repo,
|
administrator: repo,
|
||||||
database: database,
|
database: database,
|
||||||
DefaultInstance: defaultInstance,
|
defaultInstance: defaultInstance,
|
||||||
|
externalDomain: externalDomain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +95,13 @@ func (o *OPStorage) ValidateJWTProfileScopes(ctx context.Context, subject string
|
|||||||
scopes = scopes[:len(scopes)-1]
|
scopes = scopes[:len(scopes)-1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(scope, domain.OrgIDScope) {
|
||||||
|
if strings.TrimPrefix(scope, domain.OrgIDScope) != user.ResourceOwner {
|
||||||
|
scopes[i] = scopes[len(scopes)-1]
|
||||||
|
scopes[len(scopes)-1] = ""
|
||||||
|
scopes = scopes[:len(scopes)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return scopes, nil
|
return scopes, nil
|
||||||
}
|
}
|
||||||
@ -251,6 +258,16 @@ func (o *OPStorage) setUserinfo(ctx context.Context, userInfo oidc.UserInfoSette
|
|||||||
if strings.HasPrefix(scope, domain.OrgDomainPrimaryScope) {
|
if strings.HasPrefix(scope, domain.OrgDomainPrimaryScope) {
|
||||||
userInfo.AppendClaims(domain.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, domain.OrgDomainPrimaryScope))
|
userInfo.AppendClaims(domain.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, domain.OrgDomainPrimaryScope))
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(scope, domain.OrgIDScope) {
|
||||||
|
userInfo.AppendClaims(domain.OrgIDClaim, strings.TrimPrefix(scope, domain.OrgIDScope))
|
||||||
|
resourceOwnerClaims, err := o.assertUserResourceOwner(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for claim, value := range resourceOwnerClaims {
|
||||||
|
userInfo.AppendClaims(claim, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(roles) == 0 || applicationID == "" {
|
if len(roles) == 0 || applicationID == "" {
|
||||||
@ -289,9 +306,20 @@ func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clie
|
|||||||
}
|
}
|
||||||
if strings.HasPrefix(scope, ScopeProjectRolePrefix) {
|
if strings.HasPrefix(scope, ScopeProjectRolePrefix) {
|
||||||
roles = append(roles, strings.TrimPrefix(scope, ScopeProjectRolePrefix))
|
roles = append(roles, strings.TrimPrefix(scope, ScopeProjectRolePrefix))
|
||||||
} else if strings.HasPrefix(scope, domain.OrgDomainPrimaryScope) {
|
}
|
||||||
|
if strings.HasPrefix(scope, domain.OrgDomainPrimaryScope) {
|
||||||
claims = appendClaim(claims, domain.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, domain.OrgDomainPrimaryScope))
|
claims = appendClaim(claims, domain.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, domain.OrgDomainPrimaryScope))
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(scope, domain.OrgIDScope) {
|
||||||
|
claims = appendClaim(claims, domain.OrgIDClaim, strings.TrimPrefix(scope, domain.OrgIDScope))
|
||||||
|
resourceOwnerClaims, err := o.assertUserResourceOwner(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for claim, value := range resourceOwnerClaims {
|
||||||
|
claims = appendClaim(claims, claim, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(roles) == 0 || clientID == "" {
|
if len(roles) == 0 || clientID == "" {
|
||||||
return claims, nil
|
return claims, nil
|
||||||
|
@ -103,6 +103,9 @@ func (c *Client) IsScopeAllowed(scope string) bool {
|
|||||||
if strings.HasPrefix(scope, domain.OrgDomainPrimaryScope) {
|
if strings.HasPrefix(scope, domain.OrgDomainPrimaryScope) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(scope, domain.OrgIDScope) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if strings.HasPrefix(scope, domain.ProjectIDScope) {
|
if strings.HasPrefix(scope, domain.ProjectIDScope) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -507,7 +507,7 @@ func (l *Login) isDisplayLoginNameSuffix(authReq *domain.AuthRequest) bool {
|
|||||||
if authReq == nil {
|
if authReq == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if authReq.RequestedOrgID == "" {
|
if authReq.RequestedOrgID == "" || !authReq.RequestedOrgDomain {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return authReq.LabelPolicy != nil && !authReq.LabelPolicy.HideLoginNameSuffix
|
return authReq.LabelPolicy != nil && !authReq.LabelPolicy.HideLoginNameSuffix
|
||||||
|
@ -632,9 +632,15 @@ func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain
|
|||||||
loginName = strings.TrimSpace(loginName)
|
loginName = strings.TrimSpace(loginName)
|
||||||
preferredLoginName := loginName
|
preferredLoginName := loginName
|
||||||
if request.RequestedOrgID != "" {
|
if request.RequestedOrgID != "" {
|
||||||
if request.RequestedOrgID != "" {
|
if request.RequestedOrgDomain {
|
||||||
|
domainPolicy, err := repo.getDomainPolicy(ctx, request.RequestedOrgID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if domainPolicy.UserLoginMustBeDomain {
|
||||||
preferredLoginName += "@" + request.RequestedPrimaryDomain
|
preferredLoginName += "@" + request.RequestedPrimaryDomain
|
||||||
}
|
}
|
||||||
|
}
|
||||||
user, err = repo.View.UserByLoginNameAndResourceOwner(preferredLoginName, request.RequestedOrgID, request.InstanceID)
|
user, err = repo.View.UserByLoginNameAndResourceOwner(preferredLoginName, request.RequestedOrgID, request.InstanceID)
|
||||||
} else {
|
} else {
|
||||||
user, err = repo.View.UserByLoginName(loginName, request.InstanceID)
|
user, err = repo.View.UserByLoginName(loginName, request.InstanceID)
|
||||||
@ -1058,7 +1064,23 @@ func (repo *AuthRequestRepo) hasSucceededPage(ctx context.Context, request *doma
|
|||||||
return app.OIDCConfig.AppType == domain.OIDCApplicationTypeNative, nil
|
return app.OIDCConfig.AppType == domain.OIDCApplicationTypeNative, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *AuthRequestRepo) getDomainPolicy(ctx context.Context, orgID string) (*query.DomainPolicy, error) {
|
||||||
|
return repo.Query.DomainPolicyByOrg(ctx, false, orgID)
|
||||||
|
}
|
||||||
|
|
||||||
func setOrgID(ctx context.Context, orgViewProvider orgViewProvider, request *domain.AuthRequest) error {
|
func setOrgID(ctx context.Context, orgViewProvider orgViewProvider, request *domain.AuthRequest) error {
|
||||||
|
orgID := request.GetScopeOrgID()
|
||||||
|
if orgID != "" {
|
||||||
|
org, err := orgViewProvider.OrgByID(ctx, false, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
request.RequestedOrgID = org.ID
|
||||||
|
request.RequestedOrgName = org.Name
|
||||||
|
request.RequestedPrimaryDomain = org.Domain
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
primaryDomain := request.GetScopeOrgPrimaryDomain()
|
primaryDomain := request.GetScopeOrgPrimaryDomain()
|
||||||
if primaryDomain == "" {
|
if primaryDomain == "" {
|
||||||
return nil
|
return nil
|
||||||
@ -1071,6 +1093,7 @@ func setOrgID(ctx context.Context, orgViewProvider orgViewProvider, request *dom
|
|||||||
request.RequestedOrgID = org.ID
|
request.RequestedOrgID = org.ID
|
||||||
request.RequestedOrgName = org.Name
|
request.RequestedOrgName = org.Name
|
||||||
request.RequestedPrimaryDomain = primaryDomain
|
request.RequestedPrimaryDomain = primaryDomain
|
||||||
|
request.RequestedOrgDomain = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ type AuthRequest struct {
|
|||||||
RequestedOrgID string
|
RequestedOrgID string
|
||||||
RequestedOrgName string
|
RequestedOrgName string
|
||||||
RequestedPrimaryDomain string
|
RequestedPrimaryDomain string
|
||||||
|
RequestedOrgDomain bool
|
||||||
ApplicationResourceOwner string
|
ApplicationResourceOwner string
|
||||||
PrivateLabelingSetting PrivateLabelingSetting
|
PrivateLabelingSetting PrivateLabelingSetting
|
||||||
SelectedIDPConfigID string
|
SelectedIDPConfigID string
|
||||||
@ -164,3 +165,15 @@ func (a *AuthRequest) GetScopeOrgPrimaryDomain() string {
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AuthRequest) GetScopeOrgID() string {
|
||||||
|
switch request := a.Request.(type) {
|
||||||
|
case *AuthRequestOIDC:
|
||||||
|
for _, scope := range request.Scopes {
|
||||||
|
if strings.HasPrefix(scope, OrgIDScope) {
|
||||||
|
return strings.TrimPrefix(scope, OrgIDScope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
@ -32,7 +32,7 @@ func (domain *OrgDomain) GenerateVerificationCode(codeGenerator crypto.Generator
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewIAMDomainName(orgName, iamDomain string) string {
|
func NewIAMDomainName(orgName, iamDomain string) string {
|
||||||
return strings.ToLower(strings.ReplaceAll(orgName, " ", "-") + "." + iamDomain)
|
return strings.ToLower(strings.ReplaceAll(strings.TrimSpace(orgName), " ", "-") + "." + iamDomain)
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrgDomainValidationType int32
|
type OrgDomainValidationType int32
|
||||||
|
@ -2,7 +2,9 @@ package domain
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
OrgDomainPrimaryScope = "urn:zitadel:iam:org:domain:primary:"
|
OrgDomainPrimaryScope = "urn:zitadel:iam:org:domain:primary:"
|
||||||
|
OrgIDScope = "urn:zitadel:iam:org:id:"
|
||||||
OrgDomainPrimaryClaim = "urn:zitadel:iam:org:domain:primary"
|
OrgDomainPrimaryClaim = "urn:zitadel:iam:org:domain:primary"
|
||||||
|
OrgIDClaim = "urn:zitadel:iam:org:id"
|
||||||
ProjectIDScope = "urn:zitadel:iam:org:project:id:"
|
ProjectIDScope = "urn:zitadel:iam:org:project:id:"
|
||||||
ProjectIDScopeZITADEL = "zitadel"
|
ProjectIDScopeZITADEL = "zitadel"
|
||||||
AudSuffix = ":aud"
|
AudSuffix = ":aud"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user