fix: idps (#777)

* fix: update client secret, skip passwordsteps only if login not if linking

* fix: global policy for register

* fix: scope handling

* fix: back after error

* fix: change org id scope to primary domain

* fix: check if primarydomain empty

* fix: local sh

* fix: disable buttons on org login policy
This commit is contained in:
Fabi 2020-09-28 09:29:41 +02:00 committed by GitHub
parent 3e1204524e
commit 83b0ac1fdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 196 additions and 55 deletions

View File

@ -2,18 +2,19 @@
[timestamp]="idpResult?.viewTimestamp" [selection]="selection"> [timestamp]="idpResult?.viewTimestamp" [selection]="selection">
<ng-template appHasRole [appHasRole]="['iam.write']" actions> <ng-template appHasRole [appHasRole]="['iam.write']" actions>
<button (click)="deactivateSelectedIdps()" matTooltip="{{'IDP.DEACTIVATE' | translate}}" class="icon-button" <button (click)="deactivateSelectedIdps()" matTooltip="{{'IDP.DEACTIVATE' | translate}}" class="icon-button"
mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled"> mat-icon-button *ngIf="selection.hasValue() && serviceType!=PolicyComponentServiceType.MGMT" [disabled]="disabled">
<mat-icon svgIcon="mdi_account_cancel"></mat-icon> <mat-icon svgIcon="mdi_account_cancel"></mat-icon>
</button> </button>
<button (click)="reactivateSelectedIdps()" matTooltip="{{'IDP.ACTIVATE' | translate}}" class="icon-button" <button (click)="reactivateSelectedIdps()" matTooltip="{{'IDP.ACTIVATE' | translate}}" class="icon-button"
mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled"> mat-icon-button *ngIf="selection.hasValue() && serviceType!=PolicyComponentServiceType.MGMT" [disabled]="disabled">
<mat-icon svgIcon="mdi_account_check_outline"></mat-icon> <mat-icon svgIcon="mdi_account_check_outline"></mat-icon>
</button> </button>
<button color="warn" (click)="removeSelectedIdps()" matTooltip="{{'IDP.DELETE' | translate}}" <button color="warn" (click)="removeSelectedIdps()" matTooltip="{{'IDP.DELETE' | translate}}"
class="icon-button" mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled"> class="icon-button" mat-icon-button *ngIf="selection.hasValue() && serviceType!=PolicyComponentServiceType.MGMT" [disabled]="disabled">
<i class="las la-trash"></i> <i class="las la-trash"></i>
</button> </button>
<a class="add-button" [routerLink]="createRouterLink" color="primary" mat-raised-button [disabled]="disabled"> <a class="add-button" [routerLink]="createRouterLink" color="primary" mat-raised-button [disabled]="disabled"
*ngIf="serviceType!=PolicyComponentServiceType.MGMT">
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }} <mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a> </a>
</ng-template> </ng-template>
@ -82,4 +83,4 @@
<mat-paginator #paginator class="paginator" [length]="idpResult?.totalResult || 0" [pageSize]="10" <mat-paginator #paginator class="paginator" [length]="idpResult?.totalResult || 0" [pageSize]="10"
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator> [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
</div> </div>
</app-refresh-table> </app-refresh-table>

View File

@ -1,10 +1,10 @@
<app-detail-layout [backRouterLink]="backroutes" [title]="'ORG.POLICY.LOGIN_POLICY.TITLECREATE' | translate" <app-detail-layout [backRouterLink]="backroutes" [title]="'ORG.POLICY.LOGIN_POLICY.TITLECREATE' | translate"
[description]="'ORG.POLICY.LOGIN_POLICY.DESCRIPTIONCREATE' | translate"> [description]="'ORG.POLICY.LOGIN_POLICY.DESCRIPTIONCREATE' | translate">
<ng-container *ngIf="(['policy.delete'] | hasRole | async) && serviceType == PolicyComponentServiceType.MGMT"> <ng-container *ngIf="(['policy.delete'] | hasRole | async) && serviceType == PolicyComponentServiceType.MGMT">
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" color="warn" (click)="deletePolicy()" <!--<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" color="warn" (click)="deletePolicy()"
mat-stroked-button> mat-stroked-button>
{{'ORG.POLICY.DELETE' | translate}} {{'ORG.POLICY.DELETE' | translate}}
</button> </button>-->
</ng-container> </ng-container>
<div class="content" *ngIf="loginData"> <div class="content" *ngIf="loginData">
@ -12,19 +12,20 @@
<span class="left-desc">{{'ORG.POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}}</span> <span class="left-desc">{{'ORG.POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}}</span>
<span class="fill-space"></span> <span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl <mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="loginData.allowUsernamePassword"> [(ngModel)]="loginData.allowUsernamePassword" [disabled]="serviceType==PolicyComponentServiceType.MGMT">
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
<div class="row"> <div class="row">
<span class="left-desc">{{'ORG.POLICY.DATA.ALLOWREGISTER' | translate}}</span> <span class="left-desc">{{'ORG.POLICY.DATA.ALLOWREGISTER' | translate}}</span>
<span class="fill-space"></span> <span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="loginData.allowRegister"> <mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="loginData.allowRegister"
[disabled]="serviceType==PolicyComponentServiceType.MGMT">
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
<div class="row"> <div class="row">
<span class="left-desc">{{'ORG.POLICY.DATA.ALLOWEXTERNALIDP' | translate}}</span> <span class="left-desc">{{'ORG.POLICY.DATA.ALLOWEXTERNALIDP' | translate}}</span>
<span class="fill-space"></span> <span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl <mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [disabled]="serviceType==PolicyComponentServiceType.MGMT"
[(ngModel)]="loginData.allowExternalIdp"> [(ngModel)]="loginData.allowExternalIdp">
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
@ -34,18 +35,18 @@
<div class="idps"> <div class="idps">
<div class="idp" *ngFor="let idp of idps"> <div class="idp" *ngFor="let idp of idps">
<mat-icon (click)="removeIdp(idp)" class="rm">remove_circle</mat-icon> <mat-icon *ngIf="serviceType!=PolicyComponentServiceType.MGMT" (click)="removeIdp(idp)" class="rm">remove_circle</mat-icon>
<span>{{idp.name}}</span> <span>{{idp.name}}</span>
<span class="meta">{{ 'IDP.TYPE' | translate }}: {{ 'IDP.TYPES.'+idp.type | translate }}</span> <span class="meta">{{ 'IDP.TYPE' | translate }}: {{ 'IDP.TYPES.'+idp.type | translate }}</span>
<span class="meta">{{ 'IDP.ID' | translate }}: {{idp.idpConfigId}}</span> <span class="meta">{{ 'IDP.ID' | translate }}: {{idp.idpConfigId}}</span>
</div> </div>
<div class="new-idp" (click)="openDialog()"> <div *ngIf="serviceType!=PolicyComponentServiceType.MGMT" class="new-idp" (click)="openDialog()">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</div> </div>
</div> </div>
<div class="btn-container"> <div class="btn-container">
<button (click)="savePolicy()" color="primary" type="submit" <button (click)="savePolicy()" color="primary" type="submit" [disabled]="serviceType==PolicyComponentServiceType.MGMT"
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button> mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
</div> </div>

View File

@ -23,4 +23,6 @@ type AuthRequestRepository interface {
VerifyMfaOTP(ctx context.Context, agentID, authRequestID, code, userAgentID string, info *model.BrowserInfo) error VerifyMfaOTP(ctx context.Context, agentID, authRequestID, code, userAgentID string, info *model.BrowserInfo) error
LinkExternalUsers(ctx context.Context, authReqID, userAgentID string) error LinkExternalUsers(ctx context.Context, authReqID, userAgentID string) error
AutoRegisterExternalUser(ctx context.Context, user *user_model.User, externalIDP *user_model.ExternalIDP, member *org_model.OrgMember, authReqID, userAgentID, resourceOwner string) error AutoRegisterExternalUser(ctx context.Context, user *user_model.User, externalIDP *user_model.ExternalIDP, member *org_model.OrgMember, authReqID, userAgentID, resourceOwner string) error
ResetLinkingUsers(ctx context.Context, authReqID, userAgentID string) error
GetOrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error)
} }

View File

@ -74,6 +74,7 @@ type userEventProvider interface {
type orgViewProvider interface { type orgViewProvider interface {
OrgByID(string) (*org_view_model.OrgView, error) OrgByID(string) (*org_view_model.OrgView, error)
OrgByPrimaryDomain(string) (*org_view_model.OrgView, error)
} }
func (repo *AuthRequestRepo) Health(ctx context.Context) error { func (repo *AuthRequestRepo) Health(ctx context.Context) error {
@ -231,6 +232,16 @@ func (repo *AuthRequestRepo) LinkExternalUsers(ctx context.Context, authReqID, u
return repo.AuthRequests.UpdateAuthRequest(ctx, request) return repo.AuthRequests.UpdateAuthRequest(ctx, request)
} }
func (repo *AuthRequestRepo) ResetLinkingUsers(ctx context.Context, authReqID, userAgentID string) error {
request, err := repo.getAuthRequest(ctx, authReqID, userAgentID)
if err != nil {
return err
}
request.LinkingUsers = nil
request.SelectedIDPConfigID = ""
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
}
func (repo *AuthRequestRepo) AutoRegisterExternalUser(ctx context.Context, registerUser *user_model.User, externalIDP *user_model.ExternalIDP, orgMember *org_model.OrgMember, authReqID, userAgentID, resourceOwner string) error { func (repo *AuthRequestRepo) AutoRegisterExternalUser(ctx context.Context, registerUser *user_model.User, externalIDP *user_model.ExternalIDP, orgMember *org_model.OrgMember, authReqID, userAgentID, resourceOwner string) error {
request, err := repo.getAuthRequest(ctx, authReqID, userAgentID) request, err := repo.getAuthRequest(ctx, authReqID, userAgentID)
if err != nil { if err != nil {
@ -317,7 +328,14 @@ func (repo *AuthRequestRepo) getLoginPolicyAndIDPProviders(ctx context.Context,
func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model.AuthRequest) error { func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model.AuthRequest) error {
orgID := request.UserOrgID orgID := request.UserOrgID
if orgID == "" { if orgID == "" {
orgID = request.GetScopeOrgID() primaryDomain := request.GetScopeOrgPrimaryDomain()
if primaryDomain != "" {
org, err := repo.GetOrgByPrimaryDomain(primaryDomain)
if err != nil {
return err
}
orgID = org.ID
}
} }
if orgID == "" { if orgID == "" {
orgID = repo.IAMID orgID = repo.IAMID
@ -335,7 +353,16 @@ func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model
} }
func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model.AuthRequest, loginName string) (err error) { func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model.AuthRequest, loginName string) (err error) {
orgID := request.GetScopeOrgID() primaryDomain := request.GetScopeOrgPrimaryDomain()
orgID := ""
if primaryDomain != "" {
org, err := repo.GetOrgByPrimaryDomain(primaryDomain)
if err != nil {
return err
}
orgID = org.ID
}
user := new(user_view_model.UserView) user := new(user_view_model.UserView)
if orgID != "" { if orgID != "" {
user, err = repo.View.UserByLoginNameAndResourceOwner(loginName, orgID) user, err = repo.View.UserByLoginNameAndResourceOwner(loginName, orgID)
@ -356,6 +383,14 @@ func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model.
return nil return nil
} }
func (repo AuthRequestRepo) GetOrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error) {
org, err := repo.OrgViewProvider.OrgByPrimaryDomain(primaryDomain)
if err != nil {
return nil, err
}
return org_view_model.OrgToModel(org), nil
}
func (repo AuthRequestRepo) checkLoginPolicyWithResourceOwner(ctx context.Context, request *model.AuthRequest, user *user_view_model.UserView) error { func (repo AuthRequestRepo) checkLoginPolicyWithResourceOwner(ctx context.Context, request *model.AuthRequest, user *user_view_model.UserView) error {
loginPolicy, idpProviders, err := repo.getLoginPolicyAndIDPProviders(ctx, user.ResourceOwner) loginPolicy, idpProviders, err := repo.getLoginPolicyAndIDPProviders(ctx, user.ResourceOwner)
if err != nil { if err != nil {
@ -386,10 +421,15 @@ func (repo *AuthRequestRepo) checkSelectedExternalIDP(request *model.AuthRequest
} }
func (repo *AuthRequestRepo) checkExternalUserLogin(request *model.AuthRequest, idpConfigID, externalUserID string) (err error) { func (repo *AuthRequestRepo) checkExternalUserLogin(request *model.AuthRequest, idpConfigID, externalUserID string) (err error) {
orgID := request.GetScopeOrgID() primaryDomain := request.GetScopeOrgPrimaryDomain()
externalIDP := new(user_view_model.ExternalIDPView) externalIDP := new(user_view_model.ExternalIDPView)
if orgID != "" { org := new(org_model.OrgView)
externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigIDAndResourceOwner(externalUserID, idpConfigID, orgID) if primaryDomain != "" {
org, err = repo.GetOrgByPrimaryDomain(primaryDomain)
if err != nil {
return err
}
externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigIDAndResourceOwner(externalUserID, idpConfigID, org.ID)
} else { } else {
externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigID(externalUserID, idpConfigID) externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigID(externalUserID, idpConfigID)
} }
@ -435,7 +475,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthR
return nil, err return nil, err
} }
if request.SelectedIDPConfigID == "" { if request.SelectedIDPConfigID == "" || (request.SelectedIDPConfigID != "" && request.LinkingUsers != nil && len(request.LinkingUsers) > 0) {
if user.InitRequired { if user.InitRequired {
return append(steps, &model.InitUserStep{PasswordSet: user.PasswordSet}), nil return append(steps, &model.InitUserStep{PasswordSet: user.PasswordSet}), nil
} }

View File

@ -139,12 +139,22 @@ func (m *mockViewOrg) OrgByID(string) (*org_view_model.OrgView, error) {
}, nil }, nil
} }
func (m *mockViewOrg) OrgByPrimaryDomain(string) (*org_view_model.OrgView, error) {
return &org_view_model.OrgView{
State: int32(m.State),
}, nil
}
type mockViewErrOrg struct{} type mockViewErrOrg struct{}
func (m *mockViewErrOrg) OrgByID(string) (*org_view_model.OrgView, error) { func (m *mockViewErrOrg) OrgByID(string) (*org_view_model.OrgView, error) {
return nil, errors.ThrowInternal(nil, "id", "internal error") return nil, errors.ThrowInternal(nil, "id", "internal error")
} }
func (m *mockViewErrOrg) OrgByPrimaryDomain(string) (*org_view_model.OrgView, error) {
return nil, errors.ThrowInternal(nil, "id", "internal error")
}
func TestAuthRequestRepo_nextSteps(t *testing.T) { func TestAuthRequestRepo_nextSteps(t *testing.T) {
type fields struct { type fields struct {
UserEvents *user_event.UserEventstore UserEvents *user_event.UserEventstore
@ -582,7 +592,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
nil, nil,
}, },
{ {
"linking users, link users step", "linking users, password step",
fields{ fields{
userSessionViewProvider: &mockViewUserSession{ userSessionViewProvider: &mockViewUserSession{
MfaSoftwareVerification: time.Now().UTC().Add(-5 * time.Minute), MfaSoftwareVerification: time.Now().UTC().Add(-5 * time.Minute),
@ -596,6 +606,32 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
MfaSoftwareCheckLifeTime: 18 * time.Hour, MfaSoftwareCheckLifeTime: 18 * time.Hour,
}, },
args{
&model.AuthRequest{
UserID: "UserID",
SelectedIDPConfigID: "IDPConfigID",
LinkingUsers: []*model.ExternalUser{{IDPConfigID: "IDPConfigID", ExternalUserID: "UserID", DisplayName: "DisplayName"}},
}, false},
[]model.NextStep{&model.PasswordStep{}},
nil,
},
{
"linking users, linking step",
fields{
userSessionViewProvider: &mockViewUserSession{
PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
MfaSoftwareVerification: time.Now().UTC().Add(-5 * time.Minute),
},
userViewProvider: &mockViewUser{
PasswordSet: true,
IsEmailVerified: true,
MfaMaxSetUp: int32(model.MfaLevelSoftware),
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
MfaSoftwareCheckLifeTime: 18 * time.Hour,
PasswordCheckLifeTime: 10 * 24 * time.Hour,
},
args{ args{
&model.AuthRequest{ &model.AuthRequest{
UserID: "UserID", UserID: "UserID",

View File

@ -46,6 +46,17 @@ func (o *Org) Reduce(event *es_models.Event) (err error) {
return err return err
} }
err = org.AppendEvent(event) err = org.AppendEvent(event)
case model.OrgDomainPrimarySet:
domain := new(org_model.OrgDomainView)
err = domain.SetData(event)
if err != nil {
return err
}
org, err = o.view.OrgByID(event.AggregateID)
if err != nil {
return err
}
org.Domain = domain.Domain
default: default:
return o.view.ProcessedOrgSequence(event.Sequence) return o.view.ProcessedOrgSequence(event.Sequence)
} }

View File

@ -15,6 +15,10 @@ func (v *View) OrgByID(orgID string) (*org_model.OrgView, error) {
return org_view.OrgByID(v.Db, orgTable, orgID) return org_view.OrgByID(v.Db, orgTable, orgID)
} }
func (v *View) OrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error) {
return org_view.OrgByPrimaryDomain(v.Db, orgTable, primaryDomain)
}
func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_model.OrgView, uint64, error) { func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_model.OrgView, uint64, error) {
return org_view.SearchOrgs(v.Db, orgTable, req) return org_view.SearchOrgs(v.Db, orgTable, req)
} }

View File

@ -126,12 +126,12 @@ func (a *AuthRequest) SetUserInfo(userID, loginName, displayName, userOrgID stri
a.UserOrgID = userOrgID a.UserOrgID = userOrgID
} }
func (a *AuthRequest) GetScopeOrgID() string { func (a *AuthRequest) GetScopeOrgPrimaryDomain() string {
switch request := a.Request.(type) { switch request := a.Request.(type) {
case *AuthRequestOIDC: case *AuthRequestOIDC:
for _, scope := range request.Scopes { for _, scope := range request.Scopes {
if strings.HasPrefix(scope, OrgIDScope) { if strings.HasPrefix(scope, OrgDomainPrimaryScope) {
strings.TrimPrefix(scope, OrgIDScope) return strings.TrimPrefix(scope, OrgDomainPrimaryScope)
} }
} }
} }

View File

@ -19,7 +19,7 @@ const (
) )
const ( const (
OrgIDScope = "urn:zitadel:organisation:id:" OrgDomainPrimaryScope = "urn:zitadel:org:domain:primary:"
) )
type AuthRequestOIDC struct { type AuthRequestOIDC struct {

View File

@ -27,7 +27,7 @@ func (c *OIDCIDPConfig) Changes(changed *OIDCIDPConfig) map[string]interface{} {
if c.ClientID != changed.ClientID { if c.ClientID != changed.ClientID {
changes["clientId"] = changed.ClientID changes["clientId"] = changed.ClientID
} }
if c.ClientSecret != nil && c.ClientSecret != changed.ClientSecret { if changed.ClientSecret != nil && c.ClientSecret != changed.ClientSecret {
changes["clientSecret"] = changed.ClientSecret changes["clientSecret"] = changed.ClientSecret
} }
if c.Issuer != changed.Issuer { if c.Issuer != changed.Issuer {

View File

@ -18,6 +18,16 @@ func OrgByID(db *gorm.DB, table, orgID string) (*model.OrgView, error) {
return org, err return org, err
} }
func OrgByPrimaryDomain(db *gorm.DB, table, primaryDomain string) (*model.OrgView, error) {
org := new(model.OrgView)
query := repository.PrepareGetByKey(table, model.OrgSearchKey(org_model.OrgSearchKeyOrgDomain), primaryDomain)
err := query(db, org)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-GEwea", "Errors.Org.NotFound")
}
return org, err
}
func SearchOrgs(db *gorm.DB, table string, req *org_model.OrgSearchRequest) ([]*model.OrgView, uint64, error) { func SearchOrgs(db *gorm.DB, table string, req *org_model.OrgSearchRequest) ([]*model.OrgView, uint64, error) {
orgs := make([]*model.OrgView, 0) orgs := make([]*model.OrgView, 0)
query := repository.PrepareSearchQuery(table, model.OrgSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries}) query := repository.PrepareSearchQuery(table, model.OrgSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})

View File

@ -33,6 +33,7 @@ type externalIDPCallbackData struct {
type externalNotFoundOptionFormData struct { type externalNotFoundOptionFormData struct {
Link bool `schema:"link"` Link bool `schema:"link"`
AutoRegister bool `schema:"autoregister"` AutoRegister bool `schema:"autoregister"`
ResetLinking bool `schema:"resetlinking"`
} }
type externalNotFoundOptionData struct { type externalNotFoundOptionData struct {
@ -139,35 +140,52 @@ func (l *Login) handleExternalNotFoundOptionCheck(w http.ResponseWriter, r *http
data := new(externalNotFoundOptionFormData) data := new(externalNotFoundOptionFormData)
authReq, err := l.getAuthRequestAndParseData(r, data) authReq, err := l.getAuthRequestAndParseData(r, data)
if err != nil { if err != nil {
l.renderError(w, r, authReq, err) l.renderExternalNotFoundOption(w, r, authReq, err)
return return
} }
if data.Link { if data.Link {
l.renderLogin(w, r, authReq, nil) l.renderLogin(w, r, authReq, nil)
return return
} else if data.ResetLinking {
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.ResetLinkingUsers(r.Context(), authReq.ID, userAgentID)
if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err)
}
l.handleLogin(w, r)
return
} }
l.handleAutoRegister(w, r, authReq) l.handleAutoRegister(w, r, authReq)
} }
func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) { func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) {
orgIamPolicy, err := l.getOrgIamPolicy(r, authReq.GetScopeOrgID())
if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err)
return
}
iam, err := l.authRepo.GetIAM(r.Context()) iam, err := l.authRepo.GetIAM(r.Context())
if err != nil { if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err) l.renderExternalNotFoundOption(w, r, authReq, err)
return return
} }
resourceOwner := iam.GlobalOrgID resourceOwner := iam.GlobalOrgID
member := &org_model.OrgMember{ member := &org_model.OrgMember{
ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID}, ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID},
Roles: []string{orgProjectCreatorRole}, Roles: []string{orgProjectCreatorRole},
} }
if authReq.GetScopeOrgID() != iam.GlobalOrgID && authReq.GetScopeOrgID() != "" { if authReq.GetScopeOrgPrimaryDomain() != "" {
member = nil primaryDomain := authReq.GetScopeOrgPrimaryDomain()
resourceOwner = authReq.GetScopeOrgID() org, err := l.authRepo.GetOrgByPrimaryDomain(primaryDomain)
if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err)
}
if org.ID != iam.GlobalOrgID {
member = nil
resourceOwner = org.ID
}
}
orgIamPolicy, err := l.getOrgIamPolicy(r, resourceOwner)
if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err)
return
} }
idpConfig, err := l.authRepo.GetIDPConfigByID(r.Context(), authReq.SelectedIDPConfigID) idpConfig, err := l.authRepo.GetIDPConfigByID(r.Context(), authReq.SelectedIDPConfigID)
@ -216,7 +234,6 @@ func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.ID
} }
return externalUser return externalUser
} }
func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *org_model.OrgIAMPolicy, linkingUser *model.ExternalUser, idpConfig *iam_model.IDPConfigView) (*usr_model.User, *usr_model.ExternalIDP) { func (l *Login) mapExternalUserToLoginUser(orgIamPolicy *org_model.OrgIAMPolicy, linkingUser *model.ExternalUser, idpConfig *iam_model.IDPConfigView) (*usr_model.User, *usr_model.ExternalIDP) {
username := linkingUser.PreferredUsername username := linkingUser.PreferredUsername
switch idpConfig.OIDCUsernameMapping { switch idpConfig.OIDCUsernameMapping {

View File

@ -71,11 +71,6 @@ func (l *Login) handleExternalRegisterCallback(w http.ResponseWriter, r *http.Re
} }
func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) { func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
orgIamPolicy, err := l.getOrgIamPolicy(r, authReq.GetScopeOrgID())
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
}
iam, err := l.authRepo.GetIAM(r.Context()) iam, err := l.authRepo.GetIAM(r.Context())
if err != nil { if err != nil {
l.renderRegisterOption(w, r, authReq, err) l.renderRegisterOption(w, r, authReq, err)
@ -86,11 +81,24 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques
ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID}, ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID},
Roles: []string{orgProjectCreatorRole}, Roles: []string{orgProjectCreatorRole},
} }
if authReq.GetScopeOrgID() != iam.GlobalOrgID && authReq.GetScopeOrgID() != "" {
member = nil
resourceOwner = authReq.GetScopeOrgID()
}
if authReq.GetScopeOrgPrimaryDomain() != "" {
primaryDomain := authReq.GetScopeOrgPrimaryDomain()
org, err := l.authRepo.GetOrgByPrimaryDomain(primaryDomain)
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
}
if org.ID != iam.GlobalOrgID {
member = nil
resourceOwner = org.ID
}
}
orgIamPolicy, err := l.getOrgIamPolicy(r, resourceOwner)
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
}
user, externalIDP := l.mapTokenToLoginUserAndExternalIDP(orgIamPolicy, tokens, idpConfig) user, externalIDP := l.mapTokenToLoginUserAndExternalIDP(orgIamPolicy, tokens, idpConfig)
_, err = l.authRepo.RegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, member, resourceOwner) _, err = l.authRepo.RegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, member, resourceOwner)
if err != nil { if err != nil {

View File

@ -71,9 +71,17 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID}, ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID},
Roles: []string{orgProjectCreatorRole}, Roles: []string{orgProjectCreatorRole},
} }
if authRequest.GetScopeOrgID() != "" && authRequest.GetScopeOrgID() != iam.GlobalOrgID { if authRequest.GetScopeOrgPrimaryDomain() != "" {
member = nil primaryDomain := authRequest.GetScopeOrgPrimaryDomain()
resourceOwner = authRequest.GetScopeOrgID() org, err := l.authRepo.GetOrgByPrimaryDomain(primaryDomain)
if err != nil {
l.renderRegisterOption(w, r, authRequest, err)
return
}
if org.ID != iam.GlobalOrgID {
member = nil
resourceOwner = org.ID
}
} }
user, err := l.authRepo.Register(setContext(r.Context(), resourceOwner), data.toUserModel(), member, resourceOwner) user, err := l.authRepo.Register(setContext(r.Context(), resourceOwner), data.toUserModel(), member, resourceOwner)
if err != nil { if err != nil {

View File

@ -292,7 +292,14 @@ func (l *Login) getOrgID(authReq *model.AuthRequest) string {
if authReq.Request == nil { if authReq.Request == nil {
return "" return ""
} }
return authReq.GetScopeOrgID() primaryDomain := authReq.GetScopeOrgPrimaryDomain()
if primaryDomain != "" {
org, _ := l.authRepo.GetOrgByPrimaryDomain(primaryDomain)
if org != nil {
return org.ID
}
}
return ""
} }
func getRequestID(authReq *model.AuthRequest, r *http.Request) string { func getRequestID(authReq *model.AuthRequest, r *http.Request) string {

View File

@ -1,9 +1,7 @@
{{ define "error-message" }} {{ define "error-message" }}
{{if .ErrMessage }} {{if .ErrMessage }}
<div class="field"> <div class="error">
<div class="error"> {{ if .ErrType }}{{ .ErrType }} - {{end}}{{ .ErrMessage }}
{{ if .ErrType }}{{ .ErrType }} - {{end}}{{ .ErrMessage }}
</div>
</div> </div>
{{end}} {{end}}
{{ end }} {{ end }}

View File

@ -15,9 +15,7 @@
<div class="actions"> <div class="actions">
<button class="secondary right" name="link" value="true" formnovalidate>{{t "ExternalNotFoundOption.Link"}}</button> <button class="secondary right" name="link" value="true" formnovalidate>{{t "ExternalNotFoundOption.Link"}}</button>
<button class="secondary right" name="autoregister" value="true" formnovalidate>{{t "ExternalNotFoundOption.AutoRegister"}}</button> <button class="secondary right" name="autoregister" value="true" formnovalidate>{{t "ExternalNotFoundOption.AutoRegister"}}</button>
<a class="button secondary" href="{{ loginUrl .AuthReqID }}"> <button class="secondary right" name="resetlinking" value="true" formnovalidate>{{t "Actions.Back"}}</button>
{{t "Actions.Back"}}
</a>
</div> </div>
{{template "error-message" .}} {{template "error-message" .}}