Merge branch 'master' into new-eventstore

This commit is contained in:
Fabiennne 2020-12-09 13:13:47 +01:00
commit 5dd60f01e0
65 changed files with 985 additions and 704 deletions

View File

@ -47,7 +47,7 @@ SystemDefaults:
MachineKeySize: 2048 MachineKeySize: 2048
Multifactors: Multifactors:
OTP: OTP:
Issuer: 'Zitadel' Issuer: 'ZITADEL'
VerificationKey: VerificationKey:
EncryptionKeyID: $ZITADEL_OTP_VERIFICATION_KEY EncryptionKeyID: $ZITADEL_OTP_VERIFICATION_KEY
VerificationLifetimes: VerificationLifetimes:
@ -86,9 +86,9 @@ SystemDefaults:
FromName: $EMAIL_SENDER_NAME FromName: $EMAIL_SENDER_NAME
Tls: $SMTP_TLS Tls: $SMTP_TLS
Twilio: Twilio:
SID: $TWILIO_SERVICE_SID SID: $TWILIO_SERVICE_SID
Token: $TWILIO_TOKEN Token: $TWILIO_TOKEN
From: $TWILIO_SENDER_NAME From: $TWILIO_SENDER_NAME
TemplateData: TemplateData:
InitCode: InitCode:
Title: 'InitCode.Title' Title: 'InitCode.Title'
@ -127,5 +127,6 @@ SystemDefaults:
ButtonText: 'DomainClaimed.ButtonText' ButtonText: 'DomainClaimed.ButtonText'
WebAuthN: WebAuthN:
ID: $ZITADEL_COOKIE_DOMAIN ID: $ZITADEL_COOKIE_DOMAIN
Origin: $ZITADEL_ACCOUNTS OriginLogin: $ZITADEL_ACCOUNTS
OriginConsole: $ZITADEL_CONSOLE
DisplayName: ZITADEL DisplayName: ZITADEL

File diff suppressed because it is too large Load Diff

View File

@ -44,13 +44,13 @@
"zone.js": "~0.11.3" "zone.js": "~0.11.3"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "~0.1100.2", "@angular-devkit/build-angular": "~0.1100.3",
"@angular/cli": "~11.0.2", "@angular/cli": "~11.0.3",
"@angular/compiler-cli": "~11.0.0", "@angular/compiler-cli": "~11.0.0",
"@types/jasmine": "~3.6.2", "@types/jasmine": "~3.6.2",
"@angular/language-service": "~11.0.2", "@angular/language-service": "~11.0.3",
"@types/jasminewd2": "~2.0.3", "@types/jasminewd2": "~2.0.3",
"@types/node": "^14.14.9", "@types/node": "^14.14.10",
"codelyzer": "^6.0.0", "codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0", "jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~6.0.0", "jasmine-spec-reporter": "~6.0.0",
@ -59,12 +59,12 @@
"karma-coverage-istanbul-reporter": "~3.0.2", "karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0", "karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0", "karma-jasmine-html-reporter": "^1.5.0",
"prettier": "^2.2.0", "prettier": "^2.2.1",
"protractor": "~7.0.0", "protractor": "~7.0.0",
"stylelint": "^13.8.0", "stylelint": "^13.8.0",
"stylelint-config-standard": "^20.0.0", "stylelint-config-standard": "^20.0.0",
"stylelint-scss": "^3.18.0", "stylelint-scss": "^3.18.0",
"ts-node": "~9.0.0", "ts-node": "~9.1.0",
"tslint": "~6.1.3", "tslint": "~6.1.3",
"typescript": "^4.0.5" "typescript": "^4.0.5"
} }

View File

@ -1,4 +1,4 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam' : '/org']" <app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.LOGIN_POLICY.TITLE' | translate" [title]="'POLICY.LOGIN_POLICY.TITLE' | translate"
[description]="(serviceType==PolicyComponentServiceType.MGMT ? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEMGMT' : PolicyComponentServiceType.ADMIN ? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEADMIN' : '') | translate"> [description]="(serviceType==PolicyComponentServiceType.MGMT ? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEMGMT' : PolicyComponentServiceType.ADMIN ? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEADMIN' : '') | translate">
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p> <p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>

View File

@ -1,5 +1,5 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam' : '/org']" <app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.IAM_POLICY.TITLECREATE' | translate" [description]="'POLICY.IAM_POLICY.DESCRIPTION' | translate"> [title]="'POLICY.IAM_POLICY.TITLE' | translate" [description]="'POLICY.IAM_POLICY.DESCRIPTION' | translate">
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p> <p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>
<ng-template appHasRole [appHasRole]="['iam.policy.delete']"> <ng-template appHasRole [appHasRole]="['iam.policy.delete']">

View File

@ -1,4 +1,4 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam' : '/org']" <app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.PWD_AGE.TITLE' | translate" [description]="'POLICY.PWD_AGE.DESCRIPTION' | translate"> [title]="'POLICY.PWD_AGE.TITLE' | translate" [description]="'POLICY.PWD_AGE.DESCRIPTION' | translate">
<ng-template appHasRole [appHasRole]="['policy.delete']"> <ng-template appHasRole [appHasRole]="['policy.delete']">
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault" <button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"

View File

@ -1,4 +1,4 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam' : '/org']" <app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.PWD_COMPLEXITY.TITLE' | translate" [description]="'POLICY.PWD_COMPLEXITY.DESCRIPTION' | translate"> [title]="'POLICY.PWD_COMPLEXITY.TITLE' | translate" [description]="'POLICY.PWD_COMPLEXITY.DESCRIPTION' | translate">
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p> <p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>

View File

@ -1,4 +1,4 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam' : '/org']" <app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.PWD_LOCKOUT.TITLE' | translate" [description]="'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate"> [title]="'POLICY.PWD_LOCKOUT.TITLE' | translate" [description]="'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate">
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p> <p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>

View File

@ -4,7 +4,7 @@
<div class="row-lyt"> <div class="row-lyt">
<ng-template appHasRole <ng-template appHasRole
[appHasRole]="PolicyGridType.IAM ? ['iam.policy.read'] : PolicyGridType.ORG ? ['policy.read'] : []"> [appHasRole]="type == PolicyGridType.IAM ? ['iam.policy.read'] : type == PolicyGridType.ORG ? ['policy.read'] : []">
<div class="p-item card"> <div class="p-item card">
<div class="avatar"> <div class="avatar">
<mat-icon class="icon" svgIcon="mdi_textbox_password"></mat-icon> <mat-icon class="icon" svgIcon="mdi_textbox_password"></mat-icon>
@ -21,7 +21,8 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="btn-wrapper"> <div class="btn-wrapper">
<button [routerLink]="[ 'policy', PolicyComponentType.COMPLEXITY ]" <button
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '', 'policy', PolicyComponentType.COMPLEXITY ]"
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button> mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
</div> </div>
</div> </div>
@ -45,7 +46,8 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="btn-wrapper"> <div class="btn-wrapper">
<ng-template appHasRole [appHasRole]="['iam.policy.write']"> <ng-template appHasRole [appHasRole]="['iam.policy.write']">
<button [routerLink]="[ 'policy', PolicyComponentType.IAM ]" <button
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.IAM ]"
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button> mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
</ng-template> </ng-template>
</div> </div>
@ -53,7 +55,7 @@
</ng-template> </ng-template>
<ng-template appHasRole <ng-template appHasRole
[appHasRole]="PolicyGridType.IAM ? ['iam.policy.read'] : PolicyGridType.ORG ? ['policy.read'] : []"> [appHasRole]="type == PolicyGridType.IAM ? ['iam.policy.read'] : type == PolicyGridType.ORG ? ['policy.read'] : []">
<div class="p-item card"> <div class="p-item card">
<div class="avatar"> <div class="avatar">
<i class="icon las la-sign-in-alt"></i> <i class="icon las la-sign-in-alt"></i>
@ -73,7 +75,8 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="btn-wrapper"> <div class="btn-wrapper">
<ng-template appHasRole [appHasRole]="['policy.write']"> <ng-template appHasRole [appHasRole]="['policy.write']">
<button [routerLink]="[ 'policy', PolicyComponentType.LOGIN ]" <button
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.LOGIN ]"
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button> mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
</ng-template> </ng-template>
</div> </div>
@ -99,7 +102,8 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="btn-wrapper"> <div class="btn-wrapper">
<ng-template appHasRole [appHasRole]="['iam.policy.write']"> <ng-template appHasRole [appHasRole]="['iam.policy.write']">
<button [routerLink]="[ 'policy', PolicyComponentType.LABEL ]" <button
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.LABEL ]"
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button> mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
</ng-template> </ng-template>
</div> </div>

View File

@ -382,6 +382,7 @@ func mfaFromModel(mfa *usr_model.MultiFactor) *auth.MultiFactor {
State: mfaStateFromModel(mfa.State), State: mfaStateFromModel(mfa.State),
Type: mfaTypeFromModel(mfa.Type), Type: mfaTypeFromModel(mfa.Type),
Attribute: mfa.Attribute, Attribute: mfa.Attribute,
Id: mfa.ID,
} }
} }
@ -431,7 +432,7 @@ func userChangesToAPI(changes *usr_model.UserChanges) (_ []*auth.Change) {
func verifyWebAuthNFromModel(u2f *usr_model.WebAuthNToken) *auth.WebAuthNResponse { func verifyWebAuthNFromModel(u2f *usr_model.WebAuthNToken) *auth.WebAuthNResponse {
return &auth.WebAuthNResponse{ return &auth.WebAuthNResponse{
Id: u2f.WebAuthNTokenID, Id: u2f.WebAuthNTokenID,
PublicKey: u2f.PublicKey, PublicKey: u2f.CredentialCreationData,
State: mfaStateFromModel(u2f.State), State: mfaStateFromModel(u2f.State),
} }
} }

View File

@ -501,8 +501,9 @@ func mfasFromModel(mfas []*usr_model.MultiFactor) []*management.UserMultiFactor
func mfaFromModel(mfa *usr_model.MultiFactor) *management.UserMultiFactor { func mfaFromModel(mfa *usr_model.MultiFactor) *management.UserMultiFactor {
return &management.UserMultiFactor{ return &management.UserMultiFactor{
State: mfaStateFromModel(mfa.State), State: mfaStateFromModel(mfa.State),
Type: mfaTypeFromModel(mfa.Type), Type: mfaTypeFromModel(mfa.Type),
Attribute: mfa.Attribute,
} }
} }

View File

@ -30,6 +30,7 @@ const (
XContentTypeOptions = "x-content-type-options" XContentTypeOptions = "x-content-type-options"
ReferrerPolicy = "referrer-policy" ReferrerPolicy = "referrer-policy"
FeaturePolicy = "feature-policy" FeaturePolicy = "feature-policy"
PermissionsPolicy = "permissions-policy"
ZitadelOrgID = "x-zitadel-orgid" ZitadelOrgID = "x-zitadel-orgid"
) )

View File

@ -70,6 +70,7 @@ func (h *headers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
headers.Set(http_utils.XContentTypeOptions, "nosniff") headers.Set(http_utils.XContentTypeOptions, "nosniff")
headers.Set(http_utils.ReferrerPolicy, "same-origin") headers.Set(http_utils.ReferrerPolicy, "same-origin")
headers.Set(http_utils.FeaturePolicy, "payment 'none'") headers.Set(http_utils.FeaturePolicy, "payment 'none'")
headers.Set(http_utils.PermissionsPolicy, "payment=()")
//PLANNED: add expect-ct //PLANNED: add expect-ct
h.handler.ServeHTTP(w, r) h.handler.ServeHTTP(w, r)

View File

@ -270,7 +270,7 @@ func (repo *AuthRequestRepo) BeginMFAU2FLogin(ctx context.Context, userID, authR
if err != nil { if err != nil {
return nil, err return nil, err
} }
return repo.UserEvents.BeginU2FLogin(ctx, userID, request) return repo.UserEvents.BeginU2FLogin(ctx, userID, request, true)
} }
func (repo *AuthRequestRepo) VerifyMFAU2F(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) (err error) { func (repo *AuthRequestRepo) VerifyMFAU2F(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) (err error) {
@ -280,7 +280,7 @@ func (repo *AuthRequestRepo) VerifyMFAU2F(ctx context.Context, userID, authReque
if err != nil { if err != nil {
return err return err
} }
return repo.UserEvents.VerifyMFAU2F(ctx, userID, credentialData, request) return repo.UserEvents.VerifyMFAU2F(ctx, userID, credentialData, request, true)
} }
func (repo *AuthRequestRepo) BeginPasswordlessLogin(ctx context.Context, userID, authRequestID, userAgentID string) (login *user_model.WebAuthNLogin, err error) { func (repo *AuthRequestRepo) BeginPasswordlessLogin(ctx context.Context, userID, authRequestID, userAgentID string) (login *user_model.WebAuthNLogin, err error) {
@ -290,7 +290,7 @@ func (repo *AuthRequestRepo) BeginPasswordlessLogin(ctx context.Context, userID,
if err != nil { if err != nil {
return nil, err return nil, err
} }
return repo.UserEvents.BeginPasswordlessLogin(ctx, userID, request) return repo.UserEvents.BeginPasswordlessLogin(ctx, userID, request, true)
} }
func (repo *AuthRequestRepo) VerifyPasswordless(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) (err error) { func (repo *AuthRequestRepo) VerifyPasswordless(ctx context.Context, userID, authRequestID, userAgentID string, credentialData []byte, info *model.BrowserInfo) (err error) {
@ -300,7 +300,7 @@ func (repo *AuthRequestRepo) VerifyPasswordless(ctx context.Context, userID, aut
if err != nil { if err != nil {
return err return err
} }
return repo.UserEvents.VerifyPasswordless(ctx, userID, credentialData, request) return repo.UserEvents.VerifyPasswordless(ctx, userID, credentialData, request, true)
} }
func (repo *AuthRequestRepo) LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *model.BrowserInfo) (err error) { func (repo *AuthRequestRepo) LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *model.BrowserInfo) (err error) {
@ -786,7 +786,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
if !errors.IsNotFound(err) { if !errors.IsNotFound(err) {
return nil, err return nil, err
} }
session = &user_view_model.UserSessionView{} session = &user_view_model.UserSessionView{UserAgentID: agentID, UserID: user.ID}
} }
events, err := eventProvider.UserEventsByID(ctx, user.ID, session.Sequence) events, err := eventProvider.UserEventsByID(ctx, user.ID, session.Sequence)
if err != nil { if err != nil {
@ -824,7 +824,9 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
case es_model.UserRemoved: case es_model.UserRemoved:
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dG2fe", "Errors.User.NotActive") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dG2fe", "Errors.User.NotActive")
} }
sessionCopy.AppendEvent(event) if err := sessionCopy.AppendEvent(event); err != nil {
return user_view_model.UserSessionToModel(&sessionCopy), nil
}
} }
return user_view_model.UserSessionToModel(&sessionCopy), nil return user_view_model.UserSessionToModel(&sessionCopy), nil
} }

View File

@ -1208,7 +1208,7 @@ func Test_userSessionByIDs(t *testing.T) {
eventProvider: &mockEventErrUser{}, eventProvider: &mockEventErrUser{},
user: &user_model.UserView{ID: "id"}, user: &user_model.UserView{ID: "id"},
}, },
&user_model.UserSessionView{}, &user_model.UserSessionView{UserID: "id"},
nil, nil,
}, },
{ {

View File

@ -234,11 +234,11 @@ func (repo *UserRepo) ChangeMyPassword(ctx context.Context, old, new string) err
return err return err
} }
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy) pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
_, err = repo.UserEvents.ChangePassword(ctx, pwPolicyView, authz.GetCtxData(ctx).UserID, old, new) _, err = repo.UserEvents.ChangePassword(ctx, pwPolicyView, authz.GetCtxData(ctx).UserID, old, new, "")
return err return err
} }
func (repo *UserRepo) ChangePassword(ctx context.Context, userID, old, new string) (err error) { func (repo *UserRepo) ChangePassword(ctx context.Context, userID, old, new, userAgentID string) (err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID) policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
@ -249,7 +249,7 @@ func (repo *UserRepo) ChangePassword(ctx context.Context, userID, old, new strin
return err return err
} }
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy) pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
_, err = repo.UserEvents.ChangePassword(ctx, pwPolicyView, userID, old, new) _, err = repo.UserEvents.ChangePassword(ctx, pwPolicyView, userID, old, new, userAgentID)
return err return err
} }
@ -263,7 +263,7 @@ func (repo *UserRepo) MyUserMFAs(ctx context.Context) ([]*model.MultiFactor, err
mfas = append(mfas, &model.MultiFactor{Type: model.MFATypeOTP, State: user.OTPState}) mfas = append(mfas, &model.MultiFactor{Type: model.MFATypeOTP, State: user.OTPState})
} }
for _, u2f := range user.U2FTokens { for _, u2f := range user.U2FTokens {
mfas = append(mfas, &model.MultiFactor{Type: model.MFATypeU2F, State: u2f.State, Attribute: u2f.Name}) mfas = append(mfas, &model.MultiFactor{Type: model.MFATypeU2F, State: u2f.State, Attribute: u2f.Name, ID: u2f.TokenID})
} }
return mfas, nil return mfas, nil
} }
@ -290,12 +290,12 @@ func (repo *UserRepo) AddMyMFAOTP(ctx context.Context) (*model.OTP, error) {
return repo.UserEvents.AddOTP(ctx, authz.GetCtxData(ctx).UserID, accountName) return repo.UserEvents.AddOTP(ctx, authz.GetCtxData(ctx).UserID, accountName)
} }
func (repo *UserRepo) VerifyMFAOTPSetup(ctx context.Context, userID, code string) error { func (repo *UserRepo) VerifyMFAOTPSetup(ctx context.Context, userID, code, userAgentID string) error {
return repo.UserEvents.CheckMFAOTPSetup(ctx, userID, code) return repo.UserEvents.CheckMFAOTPSetup(ctx, userID, code, userAgentID)
} }
func (repo *UserRepo) VerifyMyMFAOTPSetup(ctx context.Context, code string) error { func (repo *UserRepo) VerifyMyMFAOTPSetup(ctx context.Context, code string) error {
return repo.UserEvents.CheckMFAOTPSetup(ctx, authz.GetCtxData(ctx).UserID, code) return repo.UserEvents.CheckMFAOTPSetup(ctx, authz.GetCtxData(ctx).UserID, code, "")
} }
func (repo *UserRepo) RemoveMyMFAOTP(ctx context.Context) error { func (repo *UserRepo) RemoveMyMFAOTP(ctx context.Context) error {
@ -303,19 +303,19 @@ func (repo *UserRepo) RemoveMyMFAOTP(ctx context.Context) error {
} }
func (repo *UserRepo) AddMFAU2F(ctx context.Context, userID string) (*model.WebAuthNToken, error) { func (repo *UserRepo) AddMFAU2F(ctx context.Context, userID string) (*model.WebAuthNToken, error) {
return repo.UserEvents.AddU2F(ctx, userID) return repo.UserEvents.AddU2F(ctx, userID, true)
} }
func (repo *UserRepo) AddMyMFAU2F(ctx context.Context) (*model.WebAuthNToken, error) { func (repo *UserRepo) AddMyMFAU2F(ctx context.Context) (*model.WebAuthNToken, error) {
return repo.UserEvents.AddU2F(ctx, authz.GetCtxData(ctx).UserID) return repo.UserEvents.AddU2F(ctx, authz.GetCtxData(ctx).UserID, false)
} }
func (repo *UserRepo) VerifyMFAU2FSetup(ctx context.Context, userID, tokenName string, credentialData []byte) error { func (repo *UserRepo) VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error {
return repo.UserEvents.VerifyU2FSetup(ctx, userID, tokenName, credentialData) return repo.UserEvents.VerifyU2FSetup(ctx, userID, tokenName, userAgentID, credentialData)
} }
func (repo *UserRepo) VerifyMyMFAU2FSetup(ctx context.Context, tokenName string, credentialData []byte) error { func (repo *UserRepo) VerifyMyMFAU2FSetup(ctx context.Context, tokenName string, credentialData []byte) error {
return repo.UserEvents.VerifyU2FSetup(ctx, authz.GetCtxData(ctx).UserID, tokenName, credentialData) return repo.UserEvents.VerifyU2FSetup(ctx, authz.GetCtxData(ctx).UserID, tokenName, "", credentialData)
} }
func (repo *UserRepo) RemoveMFAU2F(ctx context.Context, userID, webAuthNTokenID string) error { func (repo *UserRepo) RemoveMFAU2F(ctx context.Context, userID, webAuthNTokenID string) error {
@ -327,19 +327,19 @@ func (repo *UserRepo) RemoveMyMFAU2F(ctx context.Context, webAuthNTokenID string
} }
func (repo *UserRepo) AddPasswordless(ctx context.Context, userID string) (*model.WebAuthNToken, error) { func (repo *UserRepo) AddPasswordless(ctx context.Context, userID string) (*model.WebAuthNToken, error) {
return repo.UserEvents.AddPasswordless(ctx, userID) return repo.UserEvents.AddPasswordless(ctx, userID, true)
} }
func (repo *UserRepo) AddMyPasswordless(ctx context.Context) (*model.WebAuthNToken, error) { func (repo *UserRepo) AddMyPasswordless(ctx context.Context) (*model.WebAuthNToken, error) {
return repo.UserEvents.AddPasswordless(ctx, authz.GetCtxData(ctx).UserID) return repo.UserEvents.AddPasswordless(ctx, authz.GetCtxData(ctx).UserID, false)
} }
func (repo *UserRepo) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName string, credentialData []byte) error { func (repo *UserRepo) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error {
return repo.UserEvents.VerifyPasswordlessSetup(ctx, userID, tokenName, credentialData) return repo.UserEvents.VerifyPasswordlessSetup(ctx, userID, tokenName, userAgentID, credentialData)
} }
func (repo *UserRepo) VerifyMyPasswordlessSetup(ctx context.Context, tokenName string, credentialData []byte) error { func (repo *UserRepo) VerifyMyPasswordlessSetup(ctx context.Context, tokenName string, credentialData []byte) error {
return repo.UserEvents.VerifyPasswordlessSetup(ctx, authz.GetCtxData(ctx).UserID, tokenName, credentialData) return repo.UserEvents.VerifyPasswordlessSetup(ctx, authz.GetCtxData(ctx).UserID, tokenName, "", credentialData)
} }
func (repo *UserRepo) RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error { func (repo *UserRepo) RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error {
@ -391,7 +391,7 @@ func (repo *UserRepo) RequestPasswordReset(ctx context.Context, loginname string
return repo.UserEvents.RequestSetPassword(ctx, user.ID, model.NotificationTypeEmail) return repo.UserEvents.RequestSetPassword(ctx, user.ID, model.NotificationTypeEmail)
} }
func (repo *UserRepo) SetPassword(ctx context.Context, userID, code, password string) error { func (repo *UserRepo) SetPassword(ctx context.Context, userID, code, password, userAgentID string) error {
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID) policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID) policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
@ -400,7 +400,7 @@ func (repo *UserRepo) SetPassword(ctx context.Context, userID, code, password st
return err return err
} }
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy) pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
return repo.UserEvents.SetPassword(ctx, pwPolicyView, userID, code, password) return repo.UserEvents.SetPassword(ctx, pwPolicyView, userID, code, password, userAgentID)
} }
func (repo *UserRepo) SignOut(ctx context.Context, agentID string) error { func (repo *UserRepo) SignOut(ctx context.Context, agentID string) error {

View File

@ -93,7 +93,9 @@ func (u *UserSession) Reduce(event *models.Event) (err error) {
return u.view.ProcessedUserSessionSequence(event.Sequence, event.CreationDate) return u.view.ProcessedUserSessionSequence(event.Sequence, event.CreationDate)
} }
for _, session := range sessions { for _, session := range sessions {
session.AppendEvent(event) if err := session.AppendEvent(event); err != nil {
return err
}
if err := u.fillUserInfo(session, event.AggregateID); err != nil { if err := u.fillUserInfo(session, event.AggregateID); err != nil {
return err return err
} }
@ -116,7 +118,9 @@ func (u *UserSession) OnSuccess() error {
} }
func (u *UserSession) updateSession(session *view_model.UserSessionView, event *models.Event) error { func (u *UserSession) updateSession(session *view_model.UserSessionView, event *models.Event) error {
session.AppendEvent(event) if err := session.AppendEvent(event); err != nil {
return err
}
if err := u.fillUserInfo(session, event.AggregateID); err != nil { if err := u.fillUserInfo(session, event.AggregateID); err != nil {
return err return err
} }

View File

@ -16,8 +16,8 @@ type UserRepository interface {
SkipMFAInit(ctx context.Context, userID string) error SkipMFAInit(ctx context.Context, userID string) error
RequestPasswordReset(ctx context.Context, username string) error RequestPasswordReset(ctx context.Context, username string) error
SetPassword(ctx context.Context, userID, code, password string) error SetPassword(ctx context.Context, userID, code, password, userAgentID string) error
ChangePassword(ctx context.Context, userID, old, new string) error ChangePassword(ctx context.Context, userID, old, new, userAgentID string) error
VerifyEmail(ctx context.Context, userID, code string) error VerifyEmail(ctx context.Context, userID, code string) error
ResendEmailVerificationMail(ctx context.Context, userID string) error ResendEmailVerificationMail(ctx context.Context, userID string) error
@ -26,14 +26,14 @@ type UserRepository interface {
ResendInitVerificationMail(ctx context.Context, userID string) error ResendInitVerificationMail(ctx context.Context, userID string) error
AddMFAOTP(ctx context.Context, userID string) (*model.OTP, error) AddMFAOTP(ctx context.Context, userID string) (*model.OTP, error)
VerifyMFAOTPSetup(ctx context.Context, userID, code string) error VerifyMFAOTPSetup(ctx context.Context, userID, code, userAgentID string) error
AddMFAU2F(ctx context.Context, id string) (*model.WebAuthNToken, error) AddMFAU2F(ctx context.Context, id string) (*model.WebAuthNToken, error)
VerifyMFAU2FSetup(ctx context.Context, userID, tokenName string, credentialData []byte) error VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error
RemoveMFAU2F(ctx context.Context, userID, webAuthNTokenID string) error RemoveMFAU2F(ctx context.Context, userID, webAuthNTokenID string) error
AddPasswordless(ctx context.Context, id string) (*model.WebAuthNToken, error) AddPasswordless(ctx context.Context, id string) (*model.WebAuthNToken, error)
VerifyPasswordlessSetup(ctx context.Context, userID, tokenName string, credentialData []byte) error VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error
RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error
ChangeUsername(ctx context.Context, userID, username string) error ChangeUsername(ctx context.Context, userID, username string) error

View File

@ -92,7 +92,8 @@ type TemplateData struct {
} }
type WebAuthN struct { type WebAuthN struct {
ID string ID string
Origin string OriginLogin string
DisplayName string OriginConsole string
DisplayName string
} }

View File

@ -217,10 +217,14 @@ func (repo *UserRepo) UserMFAs(ctx context.Context, userID string) ([]*usr_model
if user.HumanView == nil { if user.HumanView == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-xx0hV", "Errors.User.NotHuman") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-xx0hV", "Errors.User.NotHuman")
} }
if user.OTPState == usr_model.MFAStateUnspecified { mfas := make([]*usr_model.MultiFactor, 0)
return []*usr_model.MultiFactor{}, nil if user.OTPState != usr_model.MFAStateUnspecified {
mfas = append(mfas, &usr_model.MultiFactor{Type: usr_model.MFATypeOTP, State: user.OTPState})
} }
return []*usr_model.MultiFactor{{Type: usr_model.MFATypeOTP, State: user.OTPState}}, nil for _, u2f := range user.U2FTokens {
mfas = append(mfas, &usr_model.MultiFactor{Type: usr_model.MFATypeU2F, State: u2f.State, Attribute: u2f.Name, ID: u2f.TokenID})
}
return mfas, nil
} }
func (repo *UserRepo) RemoveOTP(ctx context.Context, userID string) error { func (repo *UserRepo) RemoveOTP(ctx context.Context, userID string) error {

View File

@ -107,6 +107,7 @@ func (step *Step1) orgs(ctx context.Context, orgs []Org) error {
return err return err
} }
step.createdOrgs[iamOrg.Name] = org step.createdOrgs[iamOrg.Name] = org
logging.LogWithFields("SETUP-HR2gh", "name", org.Name, "ID", org.AggregateID).Info("created organisation")
var policy *iam_model.OrgIAMPolicyView var policy *iam_model.OrgIAMPolicyView
if iamOrg.OrgIamPolicy { if iamOrg.OrgIamPolicy {

View File

@ -320,8 +320,32 @@ EventTypes:
check: check:
succeeded: Multifaktor OTP Verifikation erfolgreich succeeded: Multifaktor OTP Verifikation erfolgreich
failed: Multifaktor OTP Verifikation fehlgeschlagen failed: Multifaktor OTP Verifikation fehlgeschlagen
u2f:
token:
added: Multifaktor U2F Token hinzugefügt
verified: Multifaktor U2F Token verifiziert
removed: Multifaktor U2F Token entfernt
begin:
login: Multifaktor U2F Verifikation begonnen
check:
succeeded: Multifaktor U2F Verifikation erfolgreich
failed: Multifaktor U2F Verifikation fehlgeschlagen
signcount:
changed: Prüfsumme des Multifaktor U2F Tokens wurde verändert
init: init:
skipped: Multifaktor Initialisierung übersprungen skipped: Multifaktor Initialisierung übersprungen
passwordless:
token:
added: Token für Passwortlos Login hinzugefügt
verified: Token für Passwortlos Login verifiziert
removed: Token für Passwortlos Login entfernt
begin:
login: Verifikation für Passwortlos Login begonnen
check:
succeeded: Verifikation für Passwortlos Login erfolgreich
failed: Verifikation für Passwortlos Login fehlgeschlagen
signcount:
changed: Prüfsumme des Tokens für Passwortlos Login wurde verändert
signed: signed:
out: Benutzer erfolgreich abgemeldet out: Benutzer erfolgreich abgemeldet
locked: Benutzer gesperrt locked: Benutzer gesperrt

View File

@ -101,6 +101,7 @@ Errors:
IDP: IDP:
InvalidSearchQuery: Ungültiger Suchparameter InvalidSearchQuery: Ungültiger Suchparameter
LoginPolicy: LoginPolicy:
NotFound: Login Policy not found
Invalid: Login Policy is invalid Invalid: Login Policy is invalid
NotExisting: Login Policy not existig NotExisting: Login Policy not existig
AlreadyExists: Login Policy already exists AlreadyExists: Login Policy already exists
@ -111,9 +112,20 @@ Errors:
NotExisting: Multifactor not existing NotExisting: Multifactor not existing
Unspecified: Multifactor invalid Unspecified: Multifactor invalid
PasswordComplexity: PasswordComplexity:
Empty: Passwort Complexity Policy is empty NotFound: Password Complexity Policy not found
NotExisting: Passwort Complexity Policy doesn't exist Empty: Password Complexity Policy is empty
AlreadyExists: Passwort Complexity Policy already exists NotExisting: Password Complexity Policy doesn't exist
AlreadyExists: Password Complexity Policy already exists
PasswordLockout:
NotFound: Password Lockout Policy not found
Empty: Passwort Lockout Policy is empty
NotExisting: Passwort Lockout Policy doesn't exist
AlreadyExists: Passwort Lockout Policy already exists
PasswordAge:
NotFound: Password Age Policy not found
Empty: Password Age Policy is empty
NotExisting: Password Age Policy doesn't exist
AlreadyExists: Password Age Policy already exists
OrgIAM: OrgIAM:
Empty: Org IAM Policy is empty Empty: Org IAM Policy is empty
NotExisting: Org IAM Policy doesn't exist NotExisting: Org IAM Policy doesn't exist
@ -308,8 +320,32 @@ EventTypes:
check: check:
succeeded: Multifactor OTP check succeeded succeeded: Multifactor OTP check succeeded
failed: Multifactor OTP check failed failed: Multifactor OTP check failed
u2f:
token:
added: Multifactor U2F Token added
verified: Multifactor U2F Token verified
removed: Multifactor U2F Token removed
begin:
login: Multifactor U2F check started
check:
succeeded: Multifactor U2F check succeeded
failed: Multifactor U2F check failed
signcount:
changed: Checksum of the Multifactor U2F Token has been changed
init: init:
skipped: Multifactor initialisation skipped skipped: Multifactor initialisation skipped
passwordless:
token:
added: Token for Passwordless Login added
verified: Token for Passwordless Login verified
removed: Token for Passwordless Login removed
begin:
login: Passwordless Login check started
check:
succeeded: Passwordless Login check succeeded
failed: Passwordless Login check failed
signcount:
changed: Checksum of the Passwordless Login Token has been changed
signed: signed:
out: User signed out out: User signed out
locked: User locked locked: User locked

View File

@ -3,6 +3,7 @@ package handler
import ( import (
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/auth_request/model"
) )
@ -24,7 +25,8 @@ func (l *Login) handleChangePassword(w http.ResponseWriter, r *http.Request) {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
err = l.authRepo.ChangePassword(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.OldPassword, data.NewPassword) userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.ChangePassword(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.OldPassword, data.NewPassword, userAgentID)
if err != nil { if err != nil {
l.renderChangePassword(w, r, authReq, err) l.renderChangePassword(w, r, authReq, err)
return return

View File

@ -3,6 +3,7 @@ package handler
import ( import (
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
) )
@ -67,7 +68,8 @@ func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *mod
if authReq != nil { if authReq != nil {
userOrg = authReq.UserOrgID userOrg = authReq.UserOrgID
} }
err = l.authRepo.SetPassword(setContext(r.Context(), userOrg), data.UserID, data.Code, data.Password) userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.SetPassword(setContext(r.Context(), userOrg), data.UserID, data.Code, data.Password, userAgentID)
if err != nil { if err != nil {
l.renderInitPassword(w, r, authReq, data.UserID, "", err) l.renderInitPassword(w, r, authReq, data.UserID, "", err)
return return

View File

@ -4,6 +4,7 @@ import (
"encoding/base64" "encoding/base64"
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/auth_request/model"
user_model "github.com/caos/zitadel/internal/user/model" user_model "github.com/caos/zitadel/internal/user/model"
) )
@ -12,6 +13,11 @@ const (
tmplMFAU2FInit = "mfainitu2f" tmplMFAU2FInit = "mfainitu2f"
) )
type u2fInitData struct {
webAuthNData
MFAType model.MFAType
}
func (l *Login) renderRegisterU2F(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) { func (l *Login) renderRegisterU2F(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
var errType, errMessage, credentialData string var errType, errMessage, credentialData string
var u2f *user_model.WebAuthNToken var u2f *user_model.WebAuthNToken
@ -24,9 +30,12 @@ func (l *Login) renderRegisterU2F(w http.ResponseWriter, r *http.Request, authRe
if u2f != nil { if u2f != nil {
credentialData = base64.RawURLEncoding.EncodeToString(u2f.CredentialCreationData) credentialData = base64.RawURLEncoding.EncodeToString(u2f.CredentialCreationData)
} }
data := &webAuthNData{ data := &u2fInitData{
userData: l.getUserData(r, authReq, "Register WebAuthNToken", errType, errMessage), webAuthNData: webAuthNData{
CredentialCreationData: credentialData, userData: l.getUserData(r, authReq, "Register WebAuthNToken", errType, errMessage),
CredentialCreationData: credentialData,
},
MFAType: model.MFATypeU2F,
} }
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMFAU2FInit], data, nil) l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMFAU2FInit], data, nil)
} }
@ -38,17 +47,14 @@ func (l *Login) handleRegisterU2F(w http.ResponseWriter, r *http.Request) {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
if data.Recreate {
l.renderRegisterU2F(w, r, authReq, nil)
return
}
credData, err := base64.URLEncoding.DecodeString(data.CredentialData) credData, err := base64.URLEncoding.DecodeString(data.CredentialData)
if err != nil { if err != nil {
l.renderRegisterU2F(w, r, authReq, err) l.renderRegisterU2F(w, r, authReq, err)
return return
} }
if err = l.authRepo.VerifyMFAU2FSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Name, credData); err != nil { userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
if err = l.authRepo.VerifyMFAU2FSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Name, userAgentID, credData); err != nil {
l.renderRegisterU2F(w, r, authReq, err) l.renderRegisterU2F(w, r, authReq, err)
return return
} }

View File

@ -7,6 +7,7 @@ import (
svg "github.com/ajstarks/svgo" svg "github.com/ajstarks/svgo"
"github.com/boombuler/barcode/qr" "github.com/boombuler/barcode/qr"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/qrcode" "github.com/caos/zitadel/internal/qrcode"
) )
@ -47,7 +48,8 @@ func (l *Login) handleMFAInitVerify(w http.ResponseWriter, r *http.Request) {
} }
func (l *Login) handleOTPVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaInitVerifyData) *mfaVerifyData { func (l *Login) handleOTPVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaInitVerifyData) *mfaVerifyData {
err := l.authRepo.VerifyMFAOTPSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Code) userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err := l.authRepo.VerifyMFAOTPSetup(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, data.Code, userAgentID)
if err == nil { if err == nil {
return nil return nil
} }

View File

@ -35,6 +35,15 @@ func (l *Login) handleMFAVerify(w http.ResponseWriter, r *http.Request) {
} }
func (l *Login) renderMFAVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, verificationStep *model.MFAVerificationStep, err error) { func (l *Login) renderMFAVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, verificationStep *model.MFAVerificationStep, err error) {
if verificationStep == nil {
l.renderError(w, r, authReq, err)
return
}
provider := verificationStep.MFAProviders[len(verificationStep.MFAProviders)-1]
l.renderMFAVerifySelected(w, r, authReq, verificationStep, provider, err)
}
func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, verificationStep *model.MFAVerificationStep, selectedProvider model.MFAType, err error) {
var errType, errMessage string var errType, errMessage string
if err != nil { if err != nil {
errMessage = l.getErrorMessage(r, err) errMessage = l.getErrorMessage(r, err)
@ -44,13 +53,23 @@ func (l *Login) renderMFAVerify(w http.ResponseWriter, r *http.Request, authReq
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
switch verificationStep.MFAProviders[len(verificationStep.MFAProviders)-1] { switch selectedProvider {
case model.MFATypeU2F: case model.MFATypeU2F:
l.renderU2FVerification(w, r, authReq, nil) l.renderU2FVerification(w, r, authReq, removeSelectedProviderFromList(verificationStep.MFAProviders, model.MFATypeU2F), nil)
return return
case model.MFATypeOTP: case model.MFATypeOTP:
data.MFAProviders = verificationStep.MFAProviders data.MFAProviders = removeSelectedProviderFromList(verificationStep.MFAProviders, model.MFATypeOTP)
data.SelectedMFAProvider = model.MFATypeOTP data.SelectedMFAProvider = model.MFATypeOTP
} }
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMFAVerify], data, nil) l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMFAVerify], data, nil)
} }
func removeSelectedProviderFromList(providers []model.MFAType, selected model.MFAType) []model.MFAType {
for i := len(providers) - 1; i >= 0; i-- {
if providers[i] == selected {
copy(providers[i:], providers[i+1:])
return providers[:len(providers)-1]
}
}
return providers
}

View File

@ -13,7 +13,18 @@ const (
tmplU2FVerification = "u2fverification" tmplU2FVerification = "u2fverification"
) )
func (l *Login) renderU2FVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) { type mfaU2FData struct {
webAuthNData
MFAProviders []model.MFAType
SelectedProvider model.MFAType
}
type mfaU2FFormData struct {
webAuthNFormData
SelectedProvider model.MFAType `schema:"provider"`
}
func (l *Login) renderU2FVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, providers []model.MFAType, err error) {
var errType, errMessage, credentialData string var errType, errMessage, credentialData string
var webAuthNLogin *user_model.WebAuthNLogin var webAuthNLogin *user_model.WebAuthNLogin
if err == nil { if err == nil {
@ -26,33 +37,42 @@ func (l *Login) renderU2FVerification(w http.ResponseWriter, r *http.Request, au
if webAuthNLogin != nil { if webAuthNLogin != nil {
credentialData = base64.RawURLEncoding.EncodeToString(webAuthNLogin.CredentialAssertionData) credentialData = base64.RawURLEncoding.EncodeToString(webAuthNLogin.CredentialAssertionData)
} }
data := &webAuthNData{ data := &mfaU2FData{
userData: l.getUserData(r, authReq, "Login WebAuthNToken", errType, errMessage), webAuthNData: webAuthNData{
CredentialCreationData: credentialData, userData: l.getUserData(r, authReq, "Login WebAuthNToken", errType, errMessage),
CredentialCreationData: credentialData,
},
MFAProviders: providers,
SelectedProvider: -1,
} }
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplU2FVerification], data, nil) l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplU2FVerification], data, nil)
} }
func (l *Login) handleU2FVerification(w http.ResponseWriter, r *http.Request) { func (l *Login) handleU2FVerification(w http.ResponseWriter, r *http.Request) {
formData := new(webAuthNFormData) formData := new(mfaU2FFormData)
authReq, err := l.getAuthRequestAndParseData(r, formData) authReq, err := l.getAuthRequestAndParseData(r, formData)
if err != nil { if err != nil {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
if formData.Recreate { step, ok := authReq.PossibleSteps[0].(*model.MFAVerificationStep)
l.renderU2FVerification(w, r, authReq, nil) if !ok {
l.renderError(w, r, authReq, err)
return
}
if formData.CredentialData == "" {
l.renderMFAVerifySelected(w, r, authReq, step, formData.SelectedProvider, nil)
return return
} }
credData, err := base64.URLEncoding.DecodeString(formData.CredentialData) credData, err := base64.URLEncoding.DecodeString(formData.CredentialData)
if err != nil { if err != nil {
l.renderU2FVerification(w, r, authReq, err) l.renderU2FVerification(w, r, authReq, step.MFAProviders, err)
return return
} }
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.authRepo.VerifyMFAU2F(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.ID, userAgentID, credData, model.BrowserInfoFromRequest(r)) err = l.authRepo.VerifyMFAU2F(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.ID, userAgentID, credData, model.BrowserInfoFromRequest(r))
if err != nil { if err != nil {
l.renderU2FVerification(w, r, authReq, err) l.renderU2FVerification(w, r, authReq, step.MFAProviders, err)
return return
} }
l.renderNextStep(w, r, authReq) l.renderNextStep(w, r, authReq)

View File

@ -40,10 +40,6 @@ func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Re
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
if formData.Recreate {
l.renderPasswordlessVerification(w, r, authReq, nil)
return
}
credData, err := base64.URLEncoding.DecodeString(formData.CredentialData) credData, err := base64.URLEncoding.DecodeString(formData.CredentialData)
if err != nil { if err != nil {
l.renderPasswordlessVerification(w, r, authReq, err) l.renderPasswordlessVerification(w, r, authReq, err)

View File

@ -8,5 +8,4 @@ type webAuthNData struct {
type webAuthNFormData struct { type webAuthNFormData struct {
CredentialData string `schema:"credentialData"` CredentialData string `schema:"credentialData"`
Name string `schema:"name"` Name string `schema:"name"`
Recreate bool `schema:"recreate"`
} }

View File

@ -63,6 +63,11 @@ InitUserDone:
Title: User aktiviert Title: User aktiviert
Description: EMail verifiziert und Passwort erfolgreich gesetzt Description: EMail verifiziert und Passwort erfolgreich gesetzt
MFA:
Provider0: OTP (One Time Password)
Provider1: U2F (Universal 2nd Factor)
ChooseOther: oder wähle eine andere Option aus
MFAPrompt: MFAPrompt:
Title: Multifaktor hinzufügen Title: Multifaktor hinzufügen
Description: Möchtest du einen Mulitfaktor hinzufügen? Description: Möchtest du einen Mulitfaktor hinzufügen?
@ -78,7 +83,7 @@ MFAInitVerify:
MFAInitDone: MFAInitDone:
Title: Multifaktor Verifizierung erstellt Title: Multifaktor Verifizierung erstellt
Description: Multifikator Verifizierung erfolgreich abgeschlossen. Der Multifaktor muss bei jeder Anmeldung eingegeben werden, dies beinhaltet auch den aktuellen Authentifizierungs Prozess. Description: Multifikator Verifizierung erfolgreich abgeschlossen. Der Multifaktor muss bei jeder Anmeldung eingegeben werden.
MFAInitU2F: MFAInitU2F:
Title: Multifaktor U2F / WebAuthN hinzufügen Title: Multifaktor U2F / WebAuthN hinzufügen

View File

@ -63,6 +63,11 @@ InitUserDone:
Title: User activated Title: User activated
Description: Email verified and Password successfully set Description: Email verified and Password successfully set
MFA:
Provider0: OTP (One Time Password)
Provider1: U2F (Universal 2nd Factor)
ChooseOther: or choose an other option
MFAPrompt: MFAPrompt:
Title: Multifactor Setup Title: Multifactor Setup
Description: Would you like to setup multifactor authentication? Description: Would you like to setup multifactor authentication?
@ -78,7 +83,7 @@ MFAInitVerify:
MFAInitDone: MFAInitDone:
Title: Multifcator Verification done Title: Multifcator Verification done
Description: Multifactor verification successfully done. The multifactor has to be entered on each login, even in the actual authentification process. Description: Multifactor verification successfully done. The multifactor has to be entered on each login.
MFAInitU2F: MFAInitU2F:
Title: Multifactor Setup U2F / WebAuthN Title: Multifactor Setup U2F / WebAuthN

View File

@ -3,6 +3,7 @@ function disableSubmit(checks, button) {
let inputs = form.getElementsByTagName('input'); let inputs = form.getElementsByTagName('input');
button.disabled = true; button.disabled = true;
addRequiredEventListener(inputs, checks, form, button); addRequiredEventListener(inputs, checks, form, button);
disableDoubleSubmit(form, button);
} }
function addRequiredEventListener(inputs, checks, form, button) { function addRequiredEventListener(inputs, checks, form, button) {
@ -20,6 +21,13 @@ function addRequiredEventListener(inputs, checks, form, button) {
} }
} }
function disableDoubleSubmit(form, button) {
form.addEventListener('submit', function() {
document.body.classList.add('waiting');
button.disabled = true;
})
}
function toggleButton(checks, form, inputs, button) { function toggleButton(checks, form, inputs, button) {
if (checks !== undefined) { if (checks !== undefined) {
if (checks() === false) { if (checks() === false) {

View File

@ -75,11 +75,15 @@
font-family: Lato; font-family: Lato;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
cursor: default !important;
} }
body { body {
margin: 0 0 100px 0; margin: 0 0 100px 0;
} }
body.waiting * {
cursor: wait !important;
}
html { html {
width: 100%; width: 100%;

View File

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA,kBCDc;EDEd,OCDQ;EDER;EACA;EACA;;;AAMJ;EACI,OCXQ;EDYR,aClBS;EDmBT;EACA,WEzBS;EF0BT;;;AAGJ;EACI,OCnBQ;EDoBR,aC1BS;ED2BT;EACA,WEhCU;;;AFmCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OCvDW;EDwDX;EACA;;AAEA;EACI,OC3DY;;AD8DhB;EACI;;;AAIR;EACI,kBCvEc;EDwEd,OCtEW;EDuEX;EACA;EACA;EACA;EACA,QExFU;EFyFV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCpFY;EDqFZ,OCxFU;EDyFV;;AAGJ;EACI,kBC3FO;ED4FP,OC7FI;ED8FJ;;AACA;EACI,kBC9FQ;;ADkGhB;EACI,kBExFW;EFyFX;;AAEA;EACI,kBE5FO;EF6FP;;AAIR;EACI;EACA;EACA;EACA;EACA,OEhGa;EFiGb,kBEhGmB;;AFkGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBE9HmB;EF+HnB,OC7IQ;ED8IR,QE1JU;EF2JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EEzJN;;AACA;EFoJE;IEnJA;IACA;;;AF0JA;EE7JF;;AACA;EF4JE;IE3JA;IACA;;;;AFiKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WEvLE;EFwLF;;AAGJ;EACI;EACA;EACA;EACA,OE1KC;;;AFgLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC/NA;;ADmOR;EACI,OExNK;EFyNL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC9PI;ED+PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBExPW;;AF2Pf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cEjRO;EFkRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OE7SP;;AFoTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EEvUV;;AACA;EFkUM;IEjUJ;IACA;;;AFyUQ;EACI;EACA;EE9Ud;;AACA;EF2UU;IE1UR;IACA;;;AFgVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OErVN;;AF0VE;EACI,OE5VL;;AFiWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCxYI;;AD2YR;EACI,MC7YU;;;ADkZd;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OE5aO;;;AF+aX;EACI;;;AAGJ;EACI","file":"dark.css"} {"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCNc;EDOd,OCNQ;EDOR;EACA;EACA;;;AAMJ;EACI,OChBQ;EDiBR,aCvBS;EDwBT;EACA,WE9BS;EF+BT;;;AAGJ;EACI,OCxBQ;EDyBR,aC/BS;EDgCT;EACA,WErCU;;;AFwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC5DW;ED6DX;EACA;;AAEA;EACI,OChEY;;ADmEhB;EACI;;;AAIR;EACI,kBC5Ec;ED6Ed,OC3EW;ED4EX;EACA;EACA;EACA;EACA,QE7FU;EF8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCzFY;ED0FZ,OC7FU;ED8FV;;AAGJ;EACI,kBChGO;EDiGP,OClGI;EDmGJ;;AACA;EACI,kBCnGQ;;ADuGhB;EACI,kBE7FW;EF8FX;;AAEA;EACI,kBEjGO;EFkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OErGa;EFsGb,kBErGmB;;AFuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBEnImB;EFoInB,OClJQ;EDmJR,QE/JU;EFgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EE9JN;;AACA;EFyJE;IExJA;IACA;;;AF+JA;EElKF;;AACA;EFiKE;IEhKA;IACA;;;;AFsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WE5LE;EF6LF;;AAGJ;EACI;EACA;EACA;EACA,OE/KC;;;AFqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCpOA;;ADwOR;EACI,OE7NK;EF8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCnQI;EDoQJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBE7PW;;AFgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cEtRO;EFuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OElTP;;AFyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EE5UV;;AACA;EFuUM;IEtUJ;IACA;;;AF8UQ;EACI;EACA;EEnVd;;AACA;EFgVU;IE/UR;IACA;;;AFqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OE1VN;;AF+VE;EACI,OEjWL;;AFsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC7YI;;ADgZR;EACI,MClZU;;;ADuZd;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OEjbO;;;AFobX;EACI;;;AAGJ;EACI","file":"dark.css"}

View File

@ -75,11 +75,15 @@
font-family: Lato; font-family: Lato;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
cursor: default !important;
} }
body { body {
margin: 0 0 100px 0; margin: 0 0 100px 0;
} }
body.waiting * {
cursor: wait !important;
}
html { html {
width: 100%; width: 100%;

View File

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA,kBCDc;EDEd,OCDQ;EDER;EACA;EACA;;;AAMJ;EACI,OCXQ;EDYR,aClBS;EDmBT;EACA,WEzBS;EF0BT;;;AAGJ;EACI,OCnBQ;EDoBR,aC1BS;ED2BT;EACA,WEhCU;;;AFmCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OCvDW;EDwDX;EACA;;AAEA;EACI,OC3DY;;AD8DhB;EACI;;;AAIR;EACI,kBCvEc;EDwEd,OCtEW;EDuEX;EACA;EACA;EACA;EACA,QExFU;EFyFV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCpFY;EDqFZ,OCxFU;EDyFV;;AAGJ;EACI,kBC3FO;ED4FP,OC7FI;ED8FJ;;AACA;EACI,kBC9FQ;;ADkGhB;EACI,kBExFW;EFyFX;;AAEA;EACI,kBE5FO;EF6FP;;AAIR;EACI;EACA;EACA;EACA;EACA,OEhGa;EFiGb,kBEhGmB;;AFkGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBE9HmB;EF+HnB,OC7IQ;ED8IR,QE1JU;EF2JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EEzJN;;AACA;EFoJE;IEnJA;IACA;;;AF0JA;EE7JF;;AACA;EF4JE;IE3JA;IACA;;;;AFiKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WEvLE;EFwLF;;AAGJ;EACI;EACA;EACA;EACA,OE1KC;;;AFgLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC/NA;;ADmOR;EACI,OExNK;EFyNL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC9PI;ED+PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBExPW;;AF2Pf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cEjRO;EFkRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OE7SP;;AFoTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EEvUV;;AACA;EFkUM;IEjUJ;IACA;;;AFyUQ;EACI;EACA;EE9Ud;;AACA;EF2UU;IE1UR;IACA;;;AFgVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OErVN;;AF0VE;EACI,OE5VL;;AFiWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCxYI;;AD2YR;EACI,MC7YU;;;ADkZd;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OE5aO;;;AF+aX;EACI;;;AAGJ;EACI;;;AGzdJ;EACI,kBFeQ;EEdR,OFac;;AERd;EACI;;AAGJ;EACI,OFGU;;AEAd;EACI;EACA;EACA;;AAEA;EACI,kBFIa;EEHb;EACA,ODyBgB;;ACtBpB;EACI,kBFVG;EEWH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBFdI;;AEkBZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,OFhCM;;AEkCN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,OF9EU;;AEkFV;EACI,MFnFM;;AEsFV;EACI,MFtFA;;AE0FR;EAEQ;;;AAMR;EACI,OFpGU;;AEwGb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"} {"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCNc;EDOd,OCNQ;EDOR;EACA;EACA;;;AAMJ;EACI,OChBQ;EDiBR,aCvBS;EDwBT;EACA,WE9BS;EF+BT;;;AAGJ;EACI,OCxBQ;EDyBR,aC/BS;EDgCT;EACA,WErCU;;;AFwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC5DW;ED6DX;EACA;;AAEA;EACI,OChEY;;ADmEhB;EACI;;;AAIR;EACI,kBC5Ec;ED6Ed,OC3EW;ED4EX;EACA;EACA;EACA;EACA,QE7FU;EF8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCzFY;ED0FZ,OC7FU;ED8FV;;AAGJ;EACI,kBChGO;EDiGP,OClGI;EDmGJ;;AACA;EACI,kBCnGQ;;ADuGhB;EACI,kBE7FW;EF8FX;;AAEA;EACI,kBEjGO;EFkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OErGa;EFsGb,kBErGmB;;AFuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBEnImB;EFoInB,OClJQ;EDmJR,QE/JU;EFgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EE9JN;;AACA;EFyJE;IExJA;IACA;;;AF+JA;EElKF;;AACA;EFiKE;IEhKA;IACA;;;;AFsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WE5LE;EF6LF;;AAGJ;EACI;EACA;EACA;EACA,OE/KC;;;AFqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCpOA;;ADwOR;EACI,OE7NK;EF8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCnQI;EDoQJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBE7PW;;AFgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cEtRO;EFuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OElTP;;AFyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EE5UV;;AACA;EFuUM;IEtUJ;IACA;;;AF8UQ;EACI;EACA;EEnVd;;AACA;EFgVU;IE/UR;IACA;;;AFqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OE1VN;;AF+VE;EACI,OEjWL;;AFsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC7YI;;ADgZR;EACI,MClZU;;;ADuZd;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OEjbO;;;AFobX;EACI;;;AAGJ;EACI;;;AG9dJ;EACI,kBFeQ;EEdR,OFac;;AERd;EACI;;AAGJ;EACI,OFGU;;AEAd;EACI;EACA;EACA;;AAEA;EACI,kBFIa;EEHb;EACA,ODyBgB;;ACtBpB;EACI,kBFVG;EEWH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBFdI;;AEkBZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,OFhCM;;AEkCN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,OF9EU;;AEkFV;EACI,MFnFM;;AEsFV;EACI,MFtFA;;AE0FR;EAEQ;;;AAMR;EACI,OFpGU;;AEwGb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"}

View File

@ -5,10 +5,15 @@
font-family: $standardFont; font-family: $standardFont;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
cursor: default !important;
} }
body { body {
margin: 0 0 100px 0; margin: 0 0 100px 0;
&.waiting * {
cursor: wait !important;
}
} }
html { html {

View File

@ -75,11 +75,15 @@
font-family: Lato; font-family: Lato;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
cursor: default !important;
} }
body { body {
margin: 0 0 100px 0; margin: 0 0 100px 0;
} }
body.waiting * {
cursor: wait !important;
}
html { html {
width: 100%; width: 100%;

View File

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA,kBCQc;EDPd,OCQQ;EDPR;EACA;EACA;EAEI;;;AAIR;EACI,OCFQ;EDGR,aC3BS;ED4BT;EACA,WCzBS;ED0BT;;;AAGJ;EACI,OCVQ;EDWR,aCnCS;EDoCT;EACA,WChCU;;;ADmCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC9CW;ED+CX;EACA;;AAEA;EACI,OClDY;;ADqDhB;EACI;;;AAIR;EACI,kBC9Dc;ED+Dd,OC7DW;ED8DX;EACA;EACA;EACA;EACA,QCxFU;EDyFV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBC3EY;ED4EZ,OC/EU;EDgFV;;AAGJ;EACI,kBClFO;EDmFP,OCpFI;EDqFJ;;AACA;EACI,kBCrFQ;;ADyFhB;EACI,kBCxFW;EDyFX;;AAEA;EACI,kBC5FO;ED6FP;;AAIR;EACI;EACA;EACA;EACA;EACA,OChGa;EDiGb,kBChGmB;;ADkGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBC9HmB;ED+HnB,OCpIQ;EDqIR,QC1JU;ED2JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;ECzJN;;AACA;EDoJE;ICnJA;IACA;;;AD0JA;EC7JF;;AACA;ED4JE;IC3JA;IACA;;;;ADiKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WCvLE;EDwLF;;AAGJ;EACI;EACA;EACA;EACA,OC1KC;;;ADgLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCtNA;;AD0NR;EACI,OCxNK;EDyNL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCrPI;EDsPJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBCxPW;;AD2Pf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cCjRO;EDkRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OC7SP;;ADoTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;ECvUV;;AACA;EDkUM;ICjUJ;IACA;;;ADyUQ;EACI;EACA;EC9Ud;;AACA;ED2UU;IC1UR;IACA;;;ADgVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OCrVN;;AD0VE;EACI,OC5VL;;ADiWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC/XI;;ADkYR;EACI,MCpYU;;;ADyYd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OC5aO;;;AD+aX;EACI;;;AAGJ;EACI","file":"dark.css"} {"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCGc;EDFd,OCGQ;EDFR;EACA;EACA;EAEI;;;AAIR;EACI,OCPQ;EDQR,aChCS;EDiCT;EACA,WC9BS;ED+BT;;;AAGJ;EACI,OCfQ;EDgBR,aCxCS;EDyCT;EACA,WCrCU;;;ADwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OCnDW;EDoDX;EACA;;AAEA;EACI,OCvDY;;AD0DhB;EACI;;;AAIR;EACI,kBCnEc;EDoEd,OClEW;EDmEX;EACA;EACA;EACA;EACA,QC7FU;ED8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBChFY;EDiFZ,OCpFU;EDqFV;;AAGJ;EACI,kBCvFO;EDwFP,OCzFI;ED0FJ;;AACA;EACI,kBC1FQ;;AD8FhB;EACI,kBC7FW;ED8FX;;AAEA;EACI,kBCjGO;EDkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OCrGa;EDsGb,kBCrGmB;;ADuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBCnImB;EDoInB,OCzIQ;ED0IR,QC/JU;EDgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EC9JN;;AACA;EDyJE;ICxJA;IACA;;;AD+JA;EClKF;;AACA;EDiKE;IChKA;IACA;;;;ADsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WC5LE;ED6LF;;AAGJ;EACI;EACA;EACA;EACA,OC/KC;;;ADqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC3NA;;AD+NR;EACI,OC7NK;ED8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC1PI;ED2PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBC7PW;;ADgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cCtRO;EDuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OClTP;;ADyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EC5UV;;AACA;EDuUM;ICtUJ;IACA;;;AD8UQ;EACI;EACA;ECnVd;;AACA;EDgVU;IC/UR;IACA;;;ADqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OC1VN;;AD+VE;EACI,OCjWL;;ADsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCpYI;;ADuYR;EACI,MCzYU;;;AD8Yd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OCjbO;;;ADobX;EACI;;;AAGJ;EACI","file":"dark.css"}

View File

@ -75,11 +75,15 @@
font-family: Lato; font-family: Lato;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
cursor: default !important;
} }
body { body {
margin: 0 0 100px 0; margin: 0 0 100px 0;
} }
body.waiting * {
cursor: wait !important;
}
html { html {
width: 100%; width: 100%;

View File

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA,kBCQc;EDPd,OCQQ;EDPR;EACA;EACA;EAEI;;;AAIR;EACI,OCFQ;EDGR,aC3BS;ED4BT;EACA,WCzBS;ED0BT;;;AAGJ;EACI,OCVQ;EDWR,aCnCS;EDoCT;EACA,WChCU;;;ADmCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC9CW;ED+CX;EACA;;AAEA;EACI,OClDY;;ADqDhB;EACI;;;AAIR;EACI,kBC9Dc;ED+Dd,OC7DW;ED8DX;EACA;EACA;EACA;EACA,QCxFU;EDyFV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBC3EY;ED4EZ,OC/EU;EDgFV;;AAGJ;EACI,kBClFO;EDmFP,OCpFI;EDqFJ;;AACA;EACI,kBCrFQ;;ADyFhB;EACI,kBCxFW;EDyFX;;AAEA;EACI,kBC5FO;ED6FP;;AAIR;EACI;EACA;EACA;EACA;EACA,OChGa;EDiGb,kBChGmB;;ADkGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBC9HmB;ED+HnB,OCpIQ;EDqIR,QC1JU;ED2JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;ECzJN;;AACA;EDoJE;ICnJA;IACA;;;AD0JA;EC7JF;;AACA;ED4JE;IC3JA;IACA;;;;ADiKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WCvLE;EDwLF;;AAGJ;EACI;EACA;EACA;EACA,OC1KC;;;ADgLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCtNA;;AD0NR;EACI,OCxNK;EDyNL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCrPI;EDsPJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBCxPW;;AD2Pf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cCjRO;EDkRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OC7SP;;ADoTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;ECvUV;;AACA;EDkUM;ICjUJ;IACA;;;ADyUQ;EACI;EACA;EC9Ud;;AACA;ED2UU;IC1UR;IACA;;;ADgVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OCrVN;;AD0VE;EACI,OC5VL;;ADiWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC/XI;;ADkYR;EACI,MCpYU;;;ADyYd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OC5aO;;;AD+aX;EACI;;;AAGJ;EACI;;;AEzdJ;EACI,kBD2CmB;EC1CnB,ODsBc;ECpBV;;AAGJ;EACI;;AAGJ;EACI,ODYU;;ACTd;EACI,kBD4Be;EC3Bf,ODSO;ECRP;;AAEA;EACI,kBD0Ba;ECzBb;EACA,ODyBgB;;ACtBpB;EACI,kBDDG;ECEH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBDLI;;ACSZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,ODvBM;;ACyBN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,ODrEU;;ACyEV;EACI,MD1EM;;AC6EV;EACI,MD1DW;;ACsEnB;EACI,OD3FU;;AC+Fb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"} {"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCGc;EDFd,OCGQ;EDFR;EACA;EACA;EAEI;;;AAIR;EACI,OCPQ;EDQR,aChCS;EDiCT;EACA,WC9BS;ED+BT;;;AAGJ;EACI,OCfQ;EDgBR,aCxCS;EDyCT;EACA,WCrCU;;;ADwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OCnDW;EDoDX;EACA;;AAEA;EACI,OCvDY;;AD0DhB;EACI;;;AAIR;EACI,kBCnEc;EDoEd,OClEW;EDmEX;EACA;EACA;EACA;EACA,QC7FU;ED8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBChFY;EDiFZ,OCpFU;EDqFV;;AAGJ;EACI,kBCvFO;EDwFP,OCzFI;ED0FJ;;AACA;EACI,kBC1FQ;;AD8FhB;EACI,kBC7FW;ED8FX;;AAEA;EACI,kBCjGO;EDkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OCrGa;EDsGb,kBCrGmB;;ADuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBCnImB;EDoInB,OCzIQ;ED0IR,QC/JU;EDgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EC9JN;;AACA;EDyJE;ICxJA;IACA;;;AD+JA;EClKF;;AACA;EDiKE;IChKA;IACA;;;;ADsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WC5LE;ED6LF;;AAGJ;EACI;EACA;EACA;EACA,OC/KC;;;ADqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC3NA;;AD+NR;EACI,OC7NK;ED8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC1PI;ED2PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBC7PW;;ADgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cCtRO;EDuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OClTP;;ADyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EC5UV;;AACA;EDuUM;ICtUJ;IACA;;;AD8UQ;EACI;EACA;ECnVd;;AACA;EDgVU;IC/UR;IACA;;;ADqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OC1VN;;AD+VE;EACI,OCjWL;;ADsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCpYI;;ADuYR;EACI,MCzYU;;;AD8Yd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OCjbO;;;ADobX;EACI;;;AAGJ;EACI;;;AE9dJ;EACI,kBD2CmB;EC1CnB,ODsBc;ECpBV;;AAGJ;EACI;;AAGJ;EACI,ODYU;;ACTd;EACI,kBD4Be;EC3Bf,ODSO;ECRP;;AAEA;EACI,kBD0Ba;ECzBb;EACA,ODyBgB;;ACtBpB;EACI,kBDDG;ECEH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBDLI;;ACSZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,ODvBM;;ACyBN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,ODrEU;;ACyEV;EACI,MD1EM;;AC6EV;EACI,MD1DW;;ACsEnB;EACI,OD3FU;;AC+Fb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"}

View File

@ -15,9 +15,6 @@
<div class="actions"> <div class="actions">
<button class="primary right" type="submit">{{t "Actions.Next"}}</button> <button class="primary right" type="submit">{{t "Actions.Next"}}</button>
<a class="button secondary" href="{{ loginUrl }}">
{{t "Actions.Cancel"}}
</a>
</div> </div>
</form> </form>

View File

@ -30,7 +30,9 @@
{{ template "error-message" .}} {{ template "error-message" .}}
<div class="actions"> <div class="actions">
<button class="secondary right wa-support" id="submit-button" type="submit" name="recreate" value="true">{{t "Actions.Recreate"}}</button> <a class="button secondary" href="{{ mfaPromptChangeUrl .AuthReqID .MFAType }}">
{{t "Actions.Back"}}
</a>
</div> </div>
</form> </form>

View File

@ -36,7 +36,7 @@
{{end}} {{end}}
<div class="actions"> <div class="actions">
<button class="primary right" id="submit-button" type="submit">{{t "Actions.Next"}}</button> <button class="primary" id="submit-button" type="submit">{{t "Actions.Next"}}</button>
<a class="button secondary" href="{{ mfaPromptChangeUrl .AuthReqID .MFAType }}"> <a class="button secondary" href="{{ mfaPromptChangeUrl .AuthReqID .MFAType }}">
{{t "Actions.Back"}} {{t "Actions.Back"}}
</a> </a>

View File

@ -26,7 +26,15 @@
{{ template "error-message" .}} {{ template "error-message" .}}
<div class="actions"> <div class="actions">
<button class="secondary right wa-support" id="submit-button" type="submit" name="recreate" value="true">{{t "Actions.Recreate"}}</button> {{ if .MFAProviders }}
<div class="mfa-other">
<p>{{t "MFA.ChooseOther"}}</p>
{{ range $provider := .MFAProviders}}
{{ $providerName := (t (printf "MFA.Provider%v" $provider)) }}
<button class="secondary" type="submit" name="provider" value="{{$provider}}">{{$providerName}}</button>
{{ end }}
</div>
{{ end }}
</div> </div>
</form> </form>

View File

@ -23,7 +23,16 @@
{{ template "error-message" .}} {{ template "error-message" .}}
<div class="actions"> <div class="actions">
<button class="primary right" id="submit-button" type="submit">{{t "Actions.Next"}}</button> <button class="primary" id="submit-button" type="submit">{{t "Actions.Next"}}</button>
{{ if .MFAProviders }}
<div class="mfa-other">
<p>{{t "MFA.ChooseOther"}}</p>
{{ range $provider := .MFAProviders}}
{{ $providerName := (t (printf "MFA.Provider%v" $provider)) }}
<button class="secondary" type="submit" name="provider" value="{{$provider}}" formnovalidate>{{$providerName}}</button>
{{ end }}
</div>
{{ end }}
<a class="button secondary" href="{{ loginUrl }}"> <a class="button secondary" href="{{ loginUrl }}">
{{t "Actions.Cancel"}} {{t "Actions.Cancel"}}
</a> </a>

View File

@ -26,7 +26,9 @@
{{ template "error-message" .}} {{ template "error-message" .}}
<div class="actions"> <div class="actions">
<button class="secondary right wa-support" id="submit-button" type="submit" name="recreate" value="true">{{t "Actions.Recreate"}}</button> <a href="{{ loginNameChangeUrl .AuthReqID }}">
<button class="secondary" type="button">{{t "Actions.Back"}}</button>
</a>
</div> </div>
</form> </form>

View File

@ -26,6 +26,7 @@ type MultiFactor struct {
Type MFAType Type MFAType
State MFAState State MFAState
Attribute string Attribute string
ID string
} }
type MFAType int32 type MFAType int32

View File

@ -70,7 +70,7 @@ func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventsto
passwordVerificationCode := crypto.NewEncryptionGenerator(systemDefaults.SecretGenerators.PasswordVerificationCode, aesCrypto) passwordVerificationCode := crypto.NewEncryptionGenerator(systemDefaults.SecretGenerators.PasswordVerificationCode, aesCrypto)
aesOTPCrypto, err := crypto.NewAESCrypto(systemDefaults.Multifactors.OTP.VerificationKey) aesOTPCrypto, err := crypto.NewAESCrypto(systemDefaults.Multifactors.OTP.VerificationKey)
passwordAlg := crypto.NewBCrypt(systemDefaults.SecretGenerators.PasswordSaltCost) passwordAlg := crypto.NewBCrypt(systemDefaults.SecretGenerators.PasswordSaltCost)
web, err := webauthn_helper.StartServer(systemDefaults.WebAuthN.DisplayName, systemDefaults.WebAuthN.ID, systemDefaults.WebAuthN.Origin) web, err := webauthn_helper.StartServer(systemDefaults.WebAuthN)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -578,10 +578,10 @@ func (es *UserEventstore) SetOneTimePassword(ctx context.Context, policy *iam_mo
if err != nil { if err != nil {
return nil, err return nil, err
} }
return es.changedPassword(ctx, user, policy, password.SecretString, true) return es.changedPassword(ctx, user, policy, password.SecretString, true, "")
} }
func (es *UserEventstore) SetPassword(ctx context.Context, policy *iam_model.PasswordComplexityPolicyView, userID, code, password string) error { func (es *UserEventstore) SetPassword(ctx context.Context, policy *iam_model.PasswordComplexityPolicyView, userID, code, password, userAgentID string) error {
user, err := es.HumanByID(ctx, userID) user, err := es.HumanByID(ctx, userID)
if err != nil { if err != nil {
return err return err
@ -592,7 +592,7 @@ func (es *UserEventstore) SetPassword(ctx context.Context, policy *iam_model.Pas
if err := crypto.VerifyCode(user.PasswordCode.CreationDate, user.PasswordCode.Expiry, user.PasswordCode.Code, code, es.PasswordVerificationCode); err != nil { if err := crypto.VerifyCode(user.PasswordCode.CreationDate, user.PasswordCode.Expiry, user.PasswordCode.Code, code, es.PasswordVerificationCode); err != nil {
return err return err
} }
_, err = es.changedPassword(ctx, user, policy, password, false) _, err = es.changedPassword(ctx, user, policy, password, false, userAgentID)
return err return err
} }
@ -655,7 +655,7 @@ func (es *UserEventstore) ChangeMachine(ctx context.Context, machine *usr_model.
return model.MachineToModel(repoUser.Machine), nil return model.MachineToModel(repoUser.Machine), nil
} }
func (es *UserEventstore) ChangePassword(ctx context.Context, policy *iam_model.PasswordComplexityPolicyView, userID, old, new string) (_ *usr_model.Password, err error) { func (es *UserEventstore) ChangePassword(ctx context.Context, policy *iam_model.PasswordComplexityPolicyView, userID, old, new, userAgentID string) (_ *usr_model.Password, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
@ -672,10 +672,10 @@ func (es *UserEventstore) ChangePassword(ctx context.Context, policy *iam_model.
if err != nil { if err != nil {
return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-s56a3", "Errors.User.Password.Invalid") return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-s56a3", "Errors.User.Password.Invalid")
} }
return es.changedPassword(ctx, user, policy, new, false) return es.changedPassword(ctx, user, policy, new, false, userAgentID)
} }
func (es *UserEventstore) changedPassword(ctx context.Context, user *usr_model.User, policy *iam_model.PasswordComplexityPolicyView, password string, onetime bool) (_ *usr_model.Password, err error) { func (es *UserEventstore) changedPassword(ctx context.Context, user *usr_model.User, policy *iam_model.PasswordComplexityPolicyView, password string, onetime bool, userAgentID string) (_ *usr_model.Password, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
pw := &usr_model.Password{SecretString: password} pw := &usr_model.Password{SecretString: password}
@ -683,7 +683,7 @@ func (es *UserEventstore) changedPassword(ctx context.Context, user *usr_model.U
if err != nil { if err != nil {
return nil, err return nil, err
} }
repoPassword := model.PasswordFromModel(pw) repoPassword := model.PasswordChangeFromModel(pw, userAgentID)
repoUser := model.UserFromModel(user) repoUser := model.UserFromModel(user)
agg := PasswordChangeAggregate(es.AggregateCreator(), repoUser, repoPassword) agg := PasswordChangeAggregate(es.AggregateCreator(), repoUser, repoPassword)
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, agg) err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, agg)
@ -1235,7 +1235,7 @@ func (es *UserEventstore) RemoveOTP(ctx context.Context, userID string) error {
return nil return nil
} }
func (es *UserEventstore) CheckMFAOTPSetup(ctx context.Context, userID, code string) error { func (es *UserEventstore) CheckMFAOTPSetup(ctx context.Context, userID, code, userAgentID string) error {
user, err := es.HumanByID(ctx, userID) user, err := es.HumanByID(ctx, userID)
if err != nil { if err != nil {
return err return err
@ -1250,7 +1250,7 @@ func (es *UserEventstore) CheckMFAOTPSetup(ctx context.Context, userID, code str
return err return err
} }
repoUser := model.UserFromModel(user) repoUser := model.UserFromModel(user)
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, MFAOTPVerifyAggregate(es.AggregateCreator(), repoUser)) err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, MFAOTPVerifyAggregate(es.AggregateCreator(), repoUser, userAgentID))
if err != nil { if err != nil {
return err return err
} }
@ -1302,12 +1302,12 @@ func (es *UserEventstore) verifyMFAOTP(otp *usr_model.OTP, code string) error {
return nil return nil
} }
func (es *UserEventstore) AddU2F(ctx context.Context, userID string) (*usr_model.WebAuthNToken, error) { func (es *UserEventstore) AddU2F(ctx context.Context, userID string, isLoginUI bool) (*usr_model.WebAuthNToken, error) {
user, err := es.HumanByID(ctx, userID) user, err := es.HumanByID(ctx, userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
webAuthN, err := es.webauthn.BeginRegistration(user, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementDiscouraged, user.U2FTokens...) webAuthN, err := es.webauthn.BeginRegistration(user, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementDiscouraged, isLoginUI, user.U2FTokens...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1316,6 +1316,7 @@ func (es *UserEventstore) AddU2F(ctx context.Context, userID string) (*usr_model
return nil, err return nil, err
} }
webAuthN.WebAuthNTokenID = tokenID webAuthN.WebAuthNTokenID = tokenID
webAuthN.State = usr_model.MFAStateNotReady
repoUser := model.UserFromModel(user) repoUser := model.UserFromModel(user)
repoWebAuthN := model.WebAuthNFromModel(webAuthN) repoWebAuthN := model.WebAuthNFromModel(webAuthN)
@ -1326,18 +1327,18 @@ func (es *UserEventstore) AddU2F(ctx context.Context, userID string) (*usr_model
return webAuthN, nil return webAuthN, nil
} }
func (es *UserEventstore) VerifyU2FSetup(ctx context.Context, userID, tokenName string, credentialData []byte) error { func (es *UserEventstore) VerifyU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error {
user, err := es.HumanByID(ctx, userID) user, err := es.HumanByID(ctx, userID)
if err != nil { if err != nil {
return err return err
} }
_, token := user.Human.GetU2FToVerify() _, token := user.Human.GetU2FToVerify()
webAuthN, err := es.webauthn.FinishRegistration(user, token, tokenName, credentialData) webAuthN, err := es.webauthn.FinishRegistration(user, token, tokenName, credentialData, userAgentID != "")
if err != nil { if err != nil {
return err return err
} }
repoUser := model.UserFromModel(user) repoUser := model.UserFromModel(user)
repoWebAuthN := model.WebAuthNVerifyFromModel(webAuthN) repoWebAuthN := model.WebAuthNVerifyFromModel(webAuthN, userAgentID)
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, MFAU2FVerifyAggregate(es.AggregateCreator(), repoUser, repoWebAuthN)) err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, MFAU2FVerifyAggregate(es.AggregateCreator(), repoUser, repoWebAuthN))
if err != nil { if err != nil {
return err return err
@ -1363,7 +1364,7 @@ func (es *UserEventstore) RemoveU2FToken(ctx context.Context, userID, webAuthNTo
return nil return nil
} }
func (es *UserEventstore) BeginU2FLogin(ctx context.Context, userID string, authRequest *req_model.AuthRequest) (*usr_model.WebAuthNLogin, error) { func (es *UserEventstore) BeginU2FLogin(ctx context.Context, userID string, authRequest *req_model.AuthRequest, isLoginUI bool) (*usr_model.WebAuthNLogin, error) {
user, err := es.HumanByID(ctx, userID) user, err := es.HumanByID(ctx, userID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1372,7 +1373,7 @@ func (es *UserEventstore) BeginU2FLogin(ctx context.Context, userID string, auth
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-5Mk8s", "Errors.User.MFA.U2F.NotExisting") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-5Mk8s", "Errors.User.MFA.U2F.NotExisting")
} }
webAuthNLogin, err := es.webauthn.BeginLogin(user, usr_model.UserVerificationRequirementDiscouraged, user.U2FTokens...) webAuthNLogin, err := es.webauthn.BeginLogin(user, usr_model.UserVerificationRequirementDiscouraged, isLoginUI, user.U2FTokens...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1386,13 +1387,13 @@ func (es *UserEventstore) BeginU2FLogin(ctx context.Context, userID string, auth
return webAuthNLogin, nil return webAuthNLogin, nil
} }
func (es *UserEventstore) VerifyMFAU2F(ctx context.Context, userID string, credentialData []byte, authRequest *req_model.AuthRequest) error { func (es *UserEventstore) VerifyMFAU2F(ctx context.Context, userID string, credentialData []byte, authRequest *req_model.AuthRequest, isLoginUI bool) error {
user, err := es.HumanByID(ctx, userID) user, err := es.HumanByID(ctx, userID)
if err != nil { if err != nil {
return err return err
} }
_, u2f := user.GetU2FLogin(authRequest.ID) _, u2f := user.GetU2FLogin(authRequest.ID)
keyID, signCount, finishErr := es.webauthn.FinishLogin(user, u2f, credentialData, user.U2FTokens...) keyID, signCount, finishErr := es.webauthn.FinishLogin(user, u2f, credentialData, isLoginUI, user.U2FTokens...)
if finishErr != nil && keyID == nil { if finishErr != nil && keyID == nil {
return finishErr return finishErr
} }
@ -1409,12 +1410,12 @@ func (es *UserEventstore) VerifyMFAU2F(ctx context.Context, userID string, crede
return finishErr return finishErr
} }
func (es *UserEventstore) AddPasswordless(ctx context.Context, userID string) (*usr_model.WebAuthNToken, error) { func (es *UserEventstore) AddPasswordless(ctx context.Context, userID string, isLoginUI bool) (*usr_model.WebAuthNToken, error) {
user, err := es.HumanByID(ctx, userID) user, err := es.HumanByID(ctx, userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
webAuthN, err := es.webauthn.BeginRegistration(user, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementRequired, user.PasswordlessTokens...) webAuthN, err := es.webauthn.BeginRegistration(user, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementRequired, isLoginUI, user.PasswordlessTokens...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1432,18 +1433,18 @@ func (es *UserEventstore) AddPasswordless(ctx context.Context, userID string) (*
return webAuthN, nil return webAuthN, nil
} }
func (es *UserEventstore) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName string, credentialData []byte) error { func (es *UserEventstore) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error {
user, err := es.HumanByID(ctx, userID) user, err := es.HumanByID(ctx, userID)
if err != nil { if err != nil {
return err return err
} }
_, token := user.Human.GetPasswordlessToVerify() _, token := user.Human.GetPasswordlessToVerify()
webAuthN, err := es.webauthn.FinishRegistration(user, token, tokenName, credentialData) webAuthN, err := es.webauthn.FinishRegistration(user, token, tokenName, credentialData, userAgentID != "")
if err != nil { if err != nil {
return err return err
} }
repoUser := model.UserFromModel(user) repoUser := model.UserFromModel(user)
repoWebAuthN := model.WebAuthNVerifyFromModel(webAuthN) repoWebAuthN := model.WebAuthNVerifyFromModel(webAuthN, userAgentID)
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, MFAPasswordlessVerifyAggregate(es.AggregateCreator(), repoUser, repoWebAuthN)) err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, MFAPasswordlessVerifyAggregate(es.AggregateCreator(), repoUser, repoWebAuthN))
if err != nil { if err != nil {
return err return err
@ -1469,7 +1470,7 @@ func (es *UserEventstore) RemovePasswordlessToken(ctx context.Context, userID, w
return nil return nil
} }
func (es *UserEventstore) BeginPasswordlessLogin(ctx context.Context, userID string, authRequest *req_model.AuthRequest) (*usr_model.WebAuthNLogin, error) { func (es *UserEventstore) BeginPasswordlessLogin(ctx context.Context, userID string, authRequest *req_model.AuthRequest, isLoginUI bool) (*usr_model.WebAuthNLogin, error) {
user, err := es.HumanByID(ctx, userID) user, err := es.HumanByID(ctx, userID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1477,7 +1478,7 @@ func (es *UserEventstore) BeginPasswordlessLogin(ctx context.Context, userID str
if user.PasswordlessTokens == nil { if user.PasswordlessTokens == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-5M9sd", "Errors.User.MFA.Passwordless.NotExisting") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-5M9sd", "Errors.User.MFA.Passwordless.NotExisting")
} }
webAuthNLogin, err := es.webauthn.BeginLogin(user, usr_model.UserVerificationRequirementRequired, user.PasswordlessTokens...) webAuthNLogin, err := es.webauthn.BeginLogin(user, usr_model.UserVerificationRequirementRequired, isLoginUI, user.PasswordlessTokens...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1491,13 +1492,13 @@ func (es *UserEventstore) BeginPasswordlessLogin(ctx context.Context, userID str
return webAuthNLogin, nil return webAuthNLogin, nil
} }
func (es *UserEventstore) VerifyPasswordless(ctx context.Context, userID string, credentialData []byte, authRequest *req_model.AuthRequest) error { func (es *UserEventstore) VerifyPasswordless(ctx context.Context, userID string, credentialData []byte, authRequest *req_model.AuthRequest, isLoginUI bool) error {
user, err := es.HumanByID(ctx, userID) user, err := es.HumanByID(ctx, userID)
if err != nil { if err != nil {
return err return err
} }
_, passwordless := user.GetPasswordlessLogin(authRequest.ID) _, passwordless := user.GetPasswordlessLogin(authRequest.ID)
keyID, signCount, finishErr := es.webauthn.FinishLogin(user, passwordless, credentialData, user.PasswordlessTokens...) keyID, signCount, finishErr := es.webauthn.FinishLogin(user, passwordless, credentialData, isLoginUI, user.PasswordlessTokens...)
if finishErr != nil && keyID == nil { if finishErr != nil && keyID == nil {
return finishErr return finishErr
} }

View File

@ -1678,7 +1678,7 @@ func TestSetPassword(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
err := tt.args.es.SetPassword(tt.args.ctx, tt.args.policy, tt.args.userID, tt.args.code, tt.args.password) err := tt.args.es.SetPassword(tt.args.ctx, tt.args.policy, tt.args.userID, tt.args.code, tt.args.password, "")
if tt.res.errFunc == nil && err != nil { if tt.res.errFunc == nil && err != nil {
t.Errorf("result has error: %v", err) t.Errorf("result has error: %v", err)
@ -1838,7 +1838,7 @@ func TestChangePassword(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.ChangePassword(tt.args.ctx, tt.args.policy, tt.args.userID, tt.args.old, tt.args.new) result, err := tt.args.es.ChangePassword(tt.args.ctx, tt.args.policy, tt.args.userID, tt.args.old, tt.args.new, "")
if tt.res.errFunc == nil && result.AggregateID == "" { if tt.res.errFunc == nil && result.AggregateID == "" {
t.Errorf("result has no id") t.Errorf("result has no id")
@ -3578,7 +3578,7 @@ func TestCheckMFAOTPSetup(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
err := tt.args.es.CheckMFAOTPSetup(tt.args.ctx, tt.args.userID, tt.args.code) err := tt.args.es.CheckMFAOTPSetup(tt.args.ctx, tt.args.userID, tt.args.code, "")
if tt.res.errFunc == nil && err != nil { if tt.res.errFunc == nil && err != nil {
t.Errorf("result should not get err") t.Errorf("result should not get err")

View File

@ -3,6 +3,7 @@ package model
import ( import (
"encoding/json" "encoding/json"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models"
@ -16,6 +17,10 @@ type OTP struct {
State int32 `json:"-"` State int32 `json:"-"`
} }
type OTPVerified struct {
UserAgentID string `json:"userAgentID,omitempty"`
}
func OTPFromModel(otp *model.OTP) *OTP { func OTPFromModel(otp *model.OTP) *OTP {
return &OTP{ return &OTP{
ObjectRoot: otp.ObjectRoot, ObjectRoot: otp.ObjectRoot,
@ -55,3 +60,11 @@ func (o *OTP) setData(event *es_models.Event) error {
} }
return nil return nil
} }
func (o *OTPVerified) SetData(event *es_models.Event) error {
if err := json.Unmarshal(event.Data, o); err != nil {
logging.Log("EVEN-BF421").WithError(err).Error("could not unmarshal event data")
return caos_errs.ThrowInternal(err, "MODEL-GB6hj", "could not unmarshal event")
}
return nil
}

View File

@ -26,6 +26,11 @@ type PasswordCode struct {
NotificationType int32 `json:"notificationType,omitempty"` NotificationType int32 `json:"notificationType,omitempty"`
} }
type PasswordChange struct {
Password
UserAgentID string `json:"userAgentID,omitempty"`
}
func PasswordFromModel(password *model.Password) *Password { func PasswordFromModel(password *model.Password) *Password {
return &Password{ return &Password{
ObjectRoot: password.ObjectRoot, ObjectRoot: password.ObjectRoot,
@ -51,6 +56,17 @@ func PasswordCodeToModel(code *PasswordCode) *model.PasswordCode {
} }
} }
func PasswordChangeFromModel(password *model.Password, userAgentID string) *PasswordChange {
return &PasswordChange{
Password: Password{
ObjectRoot: password.ObjectRoot,
Secret: password.SecretCrypto,
ChangeRequired: password.ChangeRequired,
},
UserAgentID: userAgentID,
}
}
func (u *Human) appendUserPasswordChangedEvent(event *es_models.Event) error { func (u *Human) appendUserPasswordChangedEvent(event *es_models.Event) error {
u.Password = new(Password) u.Password = new(Password)
err := u.Password.setData(event) err := u.Password.setData(event)
@ -84,3 +100,12 @@ func (c *PasswordCode) SetData(event *es_models.Event) error {
} }
return nil return nil
} }
func (pw *PasswordChange) SetData(event *es_models.Event) error {
if err := json.Unmarshal(event.Data, pw); err != nil {
logging.Log("EVEN-ADs31").WithError(err).Error("could not unmarshal event data")
return caos_errs.ThrowInternal(err, "MODEL-BDd32", "could not unmarshal event")
}
pw.ObjectRoot.AppendEvent(event)
return nil
}

View File

@ -32,6 +32,7 @@ type WebAuthNVerify struct {
AAGUID []byte `json:"aaguid"` AAGUID []byte `json:"aaguid"`
SignCount uint32 `json:"signCount"` SignCount uint32 `json:"signCount"`
WebAuthNTokenName string `json:"webAuthNTokenName"` WebAuthNTokenName string `json:"webAuthNTokenName"`
UserAgentID string `json:"userAgentID,omitempty"`
} }
type WebAuthNSignCount struct { type WebAuthNSignCount struct {
@ -104,7 +105,7 @@ func WebAuthNToModel(webAuthN *WebAuthNToken) *model.WebAuthNToken {
} }
} }
func WebAuthNVerifyFromModel(webAuthN *model.WebAuthNToken) *WebAuthNVerify { func WebAuthNVerifyFromModel(webAuthN *model.WebAuthNToken, userAgentID string) *WebAuthNVerify {
return &WebAuthNVerify{ return &WebAuthNVerify{
WebAuthNTokenID: webAuthN.WebAuthNTokenID, WebAuthNTokenID: webAuthN.WebAuthNTokenID,
KeyID: webAuthN.KeyID, KeyID: webAuthN.KeyID,
@ -113,6 +114,7 @@ func WebAuthNVerifyFromModel(webAuthN *model.WebAuthNToken) *WebAuthNVerify {
SignCount: webAuthN.SignCount, SignCount: webAuthN.SignCount,
AttestationType: webAuthN.AttestationType, AttestationType: webAuthN.AttestationType,
WebAuthNTokenName: webAuthN.WebAuthNTokenName, WebAuthNTokenName: webAuthN.WebAuthNTokenName,
UserAgentID: userAgentID,
} }
} }
@ -148,6 +150,14 @@ func WebAuthNLoginToModel(webAuthN *WebAuthNLogin) *model.WebAuthNLogin {
} }
} }
func (w *WebAuthNVerify) SetData(event *es_models.Event) error {
if err := json.Unmarshal(event.Data, w); err != nil {
logging.Log("EVEN-G342rf").WithError(err).Error("could not unmarshal event data")
return caos_errs.ThrowInternal(err, "MODEL-B6641", "could not unmarshal event")
}
return nil
}
func (u *Human) appendU2FAddedEvent(event *es_models.Event) error { func (u *Human) appendU2FAddedEvent(event *es_models.Event) error {
webauthn := new(WebAuthNToken) webauthn := new(WebAuthNToken)
err := webauthn.setData(event) err := webauthn.setData(event)

View File

@ -410,16 +410,16 @@ func SkipMFAAggregate(aggCreator *es_models.AggregateCreator, user *model.User)
} }
} }
func PasswordChangeAggregate(aggCreator *es_models.AggregateCreator, user *model.User, password *model.Password) func(ctx context.Context) (*es_models.Aggregate, error) { func PasswordChangeAggregate(aggCreator *es_models.AggregateCreator, user *model.User, passwordChange *model.PasswordChange) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) { return func(ctx context.Context) (*es_models.Aggregate, error) {
if password == nil { if passwordChange == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-d9832", "Errors.Internal") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-d9832", "Errors.Internal")
} }
agg, err := UserAggregate(ctx, aggCreator, user) agg, err := UserAggregate(ctx, aggCreator, user)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return agg.AppendEvent(model.HumanPasswordChanged, password) return agg.AppendEvent(model.HumanPasswordChanged, passwordChange)
} }
} }
@ -737,13 +737,13 @@ func MFAOTPAddAggregate(aggCreator *es_models.AggregateCreator, user *model.User
} }
} }
func MFAOTPVerifyAggregate(aggCreator *es_models.AggregateCreator, user *model.User) func(ctx context.Context) (*es_models.Aggregate, error) { func MFAOTPVerifyAggregate(aggCreator *es_models.AggregateCreator, user *model.User, userAgentID string) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) { return func(ctx context.Context) (*es_models.Aggregate, error) {
agg, err := UserAggregate(ctx, aggCreator, user) agg, err := UserAggregate(ctx, aggCreator, user)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return agg.AppendEvent(model.HumanMFAOTPVerified, nil) return agg.AppendEvent(model.HumanMFAOTPVerified, &model.OTPVerified{UserAgentID: userAgentID})
} }
} }

View File

@ -1060,10 +1060,10 @@ func TestSkipMFAAggregate(t *testing.T) {
func TestChangePasswordAggregate(t *testing.T) { func TestChangePasswordAggregate(t *testing.T) {
type args struct { type args struct {
ctx context.Context ctx context.Context
user *model.User user *model.User
password *model.Password passwordChange *model.PasswordChange
aggCreator *models.AggregateCreator aggCreator *models.AggregateCreator
} }
type res struct { type res struct {
eventLen int eventLen int
@ -1086,8 +1086,8 @@ func TestChangePasswordAggregate(t *testing.T) {
Profile: &model.Profile{DisplayName: "DisplayName"}, Profile: &model.Profile{DisplayName: "DisplayName"},
}, },
}, },
password: &model.Password{ChangeRequired: true}, passwordChange: &model.PasswordChange{Password: model.Password{ChangeRequired: true}},
aggCreator: models.NewAggregateCreator("Test"), aggCreator: models.NewAggregateCreator("Test"),
}, },
res: res{ res: res{
eventLen: 1, eventLen: 1,
@ -1113,7 +1113,7 @@ func TestChangePasswordAggregate(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
agg, err := PasswordChangeAggregate(tt.args.aggCreator, tt.args.user, tt.args.password)(tt.args.ctx) agg, err := PasswordChangeAggregate(tt.args.aggCreator, tt.args.user, tt.args.passwordChange)(tt.args.ctx)
if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen { if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events)) t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
@ -2329,7 +2329,7 @@ func TestOTPVerifyAggregate(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
agg, err := MFAOTPVerifyAggregate(tt.args.aggCreator, tt.args.user)(tt.args.ctx) agg, err := MFAOTPVerifyAggregate(tt.args.aggCreator, tt.args.user, "")(tt.args.ctx)
if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen { if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events)) t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))

View File

@ -367,12 +367,13 @@ func (u *UserView) addPasswordlessToken(event *models.Event) error {
if err != nil { if err != nil {
return err return err
} }
for _, t := range u.PasswordlessTokens { for i, t := range u.PasswordlessTokens {
if t.State == int32(model.MFAStateNotReady) { if t.State == int32(model.MFAStateNotReady) {
t = token u.PasswordlessTokens[i].ID = token.ID
return nil return nil
} }
} }
token.State = int32(model.MFAStateNotReady)
u.U2FTokens = append(u.U2FTokens, token) u.U2FTokens = append(u.U2FTokens, token)
return nil return nil
} }
@ -413,12 +414,13 @@ func (u *UserView) addU2FToken(event *models.Event) error {
if err != nil { if err != nil {
return err return err
} }
for _, t := range u.U2FTokens { for i, t := range u.U2FTokens {
if t.State == int32(model.MFAStateNotReady) { if t.State == int32(model.MFAStateNotReady) {
t = token u.U2FTokens[i].ID = token.ID
return nil return nil
} }
} }
token.State = int32(model.MFAStateNotReady)
u.U2FTokens = append(u.U2FTokens, token) u.U2FTokens = append(u.U2FTokens, token)
return nil return nil
} }

View File

@ -81,7 +81,7 @@ func UserSessionsToModel(userSessions []*UserSessionView) []*model.UserSessionVi
return result return result
} }
func (v *UserSessionView) AppendEvent(event *models.Event) { func (v *UserSessionView) AppendEvent(event *models.Event) error {
v.Sequence = event.Sequence v.Sequence = event.Sequence
v.ChangeDate = event.CreationDate v.ChangeDate = event.CreationDate
switch event.Type { switch event.Type {
@ -91,7 +91,10 @@ func (v *UserSessionView) AppendEvent(event *models.Event) {
v.State = int32(req_model.UserSessionStateActive) v.State = int32(req_model.UserSessionStateActive)
case es_model.HumanExternalLoginCheckSucceeded: case es_model.HumanExternalLoginCheckSucceeded:
data := new(es_model.AuthRequest) data := new(es_model.AuthRequest)
data.SetData(event) err := data.SetData(event)
if err != nil {
return err
}
v.ExternalLoginVerification = event.CreationDate v.ExternalLoginVerification = event.CreationDate
v.SelectedIDPConfigID = data.SelectedIDPConfigID v.SelectedIDPConfigID = data.SelectedIDPConfigID
v.State = int32(req_model.UserSessionStateActive) v.State = int32(req_model.UserSessionStateActive)
@ -105,15 +108,31 @@ func (v *UserSessionView) AppendEvent(event *models.Event) {
v.PasswordlessVerification = time.Time{} v.PasswordlessVerification = time.Time{}
v.MultiFactorVerification = time.Time{} v.MultiFactorVerification = time.Time{}
case es_model.UserPasswordCheckFailed, case es_model.UserPasswordCheckFailed,
es_model.UserPasswordChanged, es_model.HumanPasswordCheckFailed:
es_model.HumanPasswordCheckFailed,
es_model.HumanPasswordChanged:
v.PasswordVerification = time.Time{} v.PasswordVerification = time.Time{}
case es_model.UserPasswordChanged,
es_model.HumanPasswordChanged:
data := new(es_model.PasswordChange)
err := data.SetData(event)
if err != nil {
return err
}
if v.UserAgentID != data.UserAgentID {
v.PasswordVerification = time.Time{}
}
case es_model.MFAOTPVerified,
es_model.HumanMFAOTPVerified:
data := new(es_model.OTPVerified)
err := data.SetData(event)
if err != nil {
return err
}
if v.UserAgentID == data.UserAgentID {
v.setSecondFactorVerification(event.CreationDate, req_model.MFATypeOTP)
}
case es_model.MFAOTPCheckSucceeded, case es_model.MFAOTPCheckSucceeded,
es_model.HumanMFAOTPCheckSucceeded: es_model.HumanMFAOTPCheckSucceeded:
v.SecondFactorVerification = event.CreationDate v.setSecondFactorVerification(event.CreationDate, req_model.MFATypeOTP)
v.SecondFactorVerificationType = int32(req_model.MFATypeOTP)
v.State = int32(req_model.UserSessionStateActive)
case es_model.MFAOTPCheckFailed, case es_model.MFAOTPCheckFailed,
es_model.MFAOTPRemoved, es_model.MFAOTPRemoved,
es_model.HumanMFAOTPCheckFailed, es_model.HumanMFAOTPCheckFailed,
@ -121,10 +140,17 @@ func (v *UserSessionView) AppendEvent(event *models.Event) {
es_model.HumanMFAU2FTokenCheckFailed, es_model.HumanMFAU2FTokenCheckFailed,
es_model.HumanMFAU2FTokenRemoved: es_model.HumanMFAU2FTokenRemoved:
v.SecondFactorVerification = time.Time{} v.SecondFactorVerification = time.Time{}
case es_model.HumanMFAU2FTokenVerified:
data := new(es_model.WebAuthNVerify)
err := data.SetData(event)
if err != nil {
return err
}
if v.UserAgentID == data.UserAgentID {
v.setSecondFactorVerification(event.CreationDate, req_model.MFATypeU2F)
}
case es_model.HumanMFAU2FTokenCheckSucceeded: case es_model.HumanMFAU2FTokenCheckSucceeded:
v.SecondFactorVerification = event.CreationDate v.setSecondFactorVerification(event.CreationDate, req_model.MFATypeU2F)
v.SecondFactorVerificationType = int32(req_model.MFATypeU2F)
v.State = int32(req_model.UserSessionStateActive)
case es_model.SignedOut, case es_model.SignedOut,
es_model.HumanSignedOut, es_model.HumanSignedOut,
es_model.UserLocked, es_model.UserLocked,
@ -137,4 +163,11 @@ func (v *UserSessionView) AppendEvent(event *models.Event) {
v.ExternalLoginVerification = time.Time{} v.ExternalLoginVerification = time.Time{}
v.SelectedIDPConfigID = "" v.SelectedIDPConfigID = ""
} }
return nil
}
func (v *UserSessionView) setSecondFactorVerification(verificationTime time.Time, mfaType req_model.MFAType) {
v.SecondFactorVerification = verificationTime
v.SecondFactorVerificationType = int32(mfaType)
v.State = int32(req_model.UserSessionStateActive)
} }

View File

@ -1,11 +1,13 @@
package model package model
import ( import (
"encoding/json"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/caos/zitadel/internal/crypto"
es_models "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
) )
@ -59,18 +61,87 @@ func TestAppendEvent(t *testing.T) {
{ {
name: "append user password changed event", name: "append user password changed event",
args: args{ args: args{
event: &es_models.Event{CreationDate: now(), Type: es_model.UserPasswordChanged}, event: &es_models.Event{
userView: &UserSessionView{PasswordVerification: now()}, CreationDate: now(),
Type: es_model.UserPasswordChanged,
Data: func() []byte {
d, _ := json.Marshal(&es_model.Password{
Secret: &crypto.CryptoValue{Crypted: []byte("test")},
})
return d
}(),
},
userView: &UserSessionView{UserAgentID: "id", PasswordVerification: now()},
}, },
result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}}, result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), PasswordVerification: time.Time{}},
}, },
{ {
name: "append human password changed event", name: "append human password changed event",
args: args{ args: args{
event: &es_models.Event{CreationDate: now(), Type: es_model.HumanPasswordChanged}, event: &es_models.Event{
userView: &UserSessionView{PasswordVerification: now()}, CreationDate: now(),
Type: es_model.HumanPasswordChanged,
Data: func() []byte {
d, _ := json.Marshal(&es_model.PasswordChange{
Password: es_model.Password{
Secret: &crypto.CryptoValue{Crypted: []byte("test")},
},
})
return d
}(),
},
userView: &UserSessionView{UserAgentID: "id", PasswordVerification: now()},
}, },
result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}}, result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), PasswordVerification: time.Time{}},
},
{
name: "append human password changed event same user agent",
args: args{
event: &es_models.Event{
CreationDate: now(),
Type: es_model.HumanPasswordChanged,
Data: func() []byte {
d, _ := json.Marshal(&es_model.PasswordChange{
Password: es_model.Password{
Secret: &crypto.CryptoValue{Crypted: []byte("test")},
},
UserAgentID: "id",
})
return d
}(),
},
userView: &UserSessionView{UserAgentID: "id", PasswordVerification: now()},
},
result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), PasswordVerification: now()},
},
{
name: "append user otp verified event",
args: args{
event: &es_models.Event{
CreationDate: now(),
Type: es_model.MFAOTPVerified,
Data: nil,
},
userView: &UserSessionView{UserAgentID: "id"},
},
result: &UserSessionView{UserAgentID: "id", ChangeDate: now()},
},
{
name: "append user otp verified event same user agent",
args: args{
event: &es_models.Event{
CreationDate: now(),
Type: es_model.MFAOTPVerified,
Data: func() []byte {
d, _ := json.Marshal(&es_model.OTPVerified{
UserAgentID: "id",
})
return d
}(),
},
userView: &UserSessionView{UserAgentID: "id"},
},
result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), SecondFactorVerification: now()},
}, },
{ {
name: "append user otp check succeeded event", name: "append user otp check succeeded event",

View File

@ -7,25 +7,36 @@ import (
"github.com/duo-labs/webauthn/protocol" "github.com/duo-labs/webauthn/protocol"
"github.com/duo-labs/webauthn/webauthn" "github.com/duo-labs/webauthn/webauthn"
"github.com/caos/zitadel/internal/config/systemdefaults"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
) )
type WebAuthN struct { type WebAuthN struct {
web *webauthn.WebAuthn webLogin *webauthn.WebAuthn
webConsole *webauthn.WebAuthn
} }
func StartServer(displayName, id, origin string) (*WebAuthN, error) { func StartServer(sd systemdefaults.WebAuthN) (*WebAuthN, error) {
web, err := webauthn.New(&webauthn.Config{ webLogin, err := webauthn.New(&webauthn.Config{
RPDisplayName: displayName, RPDisplayName: sd.DisplayName,
RPID: id, RPID: sd.ID,
RPOrigin: origin, RPOrigin: sd.OriginLogin,
})
if err != nil {
return nil, err
}
webConsole, err := webauthn.New(&webauthn.Config{
RPDisplayName: sd.DisplayName,
RPID: sd.ID,
RPOrigin: sd.OriginConsole,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &WebAuthN{ return &WebAuthN{
web: web, webLogin: webLogin,
webConsole: webConsole,
}, err }, err
} }
@ -39,7 +50,7 @@ func (u *webUser) WebAuthnID() []byte {
} }
func (u *webUser) WebAuthnName() string { func (u *webUser) WebAuthnName() string {
return u.UserName return u.PreferredLoginName
} }
func (u *webUser) WebAuthnDisplayName() string { func (u *webUser) WebAuthnDisplayName() string {
@ -54,7 +65,7 @@ func (u *webUser) WebAuthnCredentials() []webauthn.Credential {
return u.credentials return u.credentials
} }
func (w *WebAuthN) BeginRegistration(user *usr_model.User, authType usr_model.AuthenticatorAttachment, userVerification usr_model.UserVerificationRequirement, webAuthNs ...*usr_model.WebAuthNToken) (*usr_model.WebAuthNToken, error) { func (w *WebAuthN) BeginRegistration(user *usr_model.User, authType usr_model.AuthenticatorAttachment, userVerification usr_model.UserVerificationRequirement, isLoginUI bool, webAuthNs ...*usr_model.WebAuthNToken) (*usr_model.WebAuthNToken, error) {
creds := WebAuthNsToCredentials(webAuthNs) creds := WebAuthNsToCredentials(webAuthNs)
existing := make([]protocol.CredentialDescriptor, len(creds)) existing := make([]protocol.CredentialDescriptor, len(creds))
for i, cred := range creds { for i, cred := range creds {
@ -63,7 +74,7 @@ func (w *WebAuthN) BeginRegistration(user *usr_model.User, authType usr_model.Au
CredentialID: cred.ID, CredentialID: cred.ID,
} }
} }
credentialOptions, sessionData, err := w.web.BeginRegistration( credentialOptions, sessionData, err := w.web(isLoginUI).BeginRegistration(
&webUser{ &webUser{
User: user, User: user,
credentials: creds, credentials: creds,
@ -90,7 +101,7 @@ func (w *WebAuthN) BeginRegistration(user *usr_model.User, authType usr_model.Au
}, nil }, nil
} }
func (w *WebAuthN) FinishRegistration(user *usr_model.User, webAuthN *usr_model.WebAuthNToken, tokenName string, credData []byte) (*usr_model.WebAuthNToken, error) { func (w *WebAuthN) FinishRegistration(user *usr_model.User, webAuthN *usr_model.WebAuthNToken, tokenName string, credData []byte, isLoginUI bool) (*usr_model.WebAuthNToken, error) {
if webAuthN == nil { if webAuthN == nil {
return nil, caos_errs.ThrowInternal(nil, "WEBAU-5M9so", "Errors.User.WebAuthN.NotFound") return nil, caos_errs.ThrowInternal(nil, "WEBAU-5M9so", "Errors.User.WebAuthN.NotFound")
} }
@ -99,7 +110,7 @@ func (w *WebAuthN) FinishRegistration(user *usr_model.User, webAuthN *usr_model.
return nil, caos_errs.ThrowInternal(err, "WEBAU-sEr8c", "Errors.User.WebAuthN.ErrorOnParseCredential") return nil, caos_errs.ThrowInternal(err, "WEBAU-sEr8c", "Errors.User.WebAuthN.ErrorOnParseCredential")
} }
sessionData := WebAuthNToSessionData(webAuthN) sessionData := WebAuthNToSessionData(webAuthN)
credential, err := w.web.CreateCredential( credential, err := w.web(isLoginUI).CreateCredential(
&webUser{ &webUser{
User: user, User: user,
}, },
@ -117,8 +128,8 @@ func (w *WebAuthN) FinishRegistration(user *usr_model.User, webAuthN *usr_model.
return webAuthN, nil return webAuthN, nil
} }
func (w *WebAuthN) BeginLogin(user *usr_model.User, userVerification usr_model.UserVerificationRequirement, webAuthNs ...*usr_model.WebAuthNToken) (*usr_model.WebAuthNLogin, error) { func (w *WebAuthN) BeginLogin(user *usr_model.User, userVerification usr_model.UserVerificationRequirement, isLoginUI bool, webAuthNs ...*usr_model.WebAuthNToken) (*usr_model.WebAuthNLogin, error) {
assertion, sessionData, err := w.web.BeginLogin(&webUser{ assertion, sessionData, err := w.web(isLoginUI).BeginLogin(&webUser{
User: user, User: user,
credentials: WebAuthNsToCredentials(webAuthNs), credentials: WebAuthNsToCredentials(webAuthNs),
}, webauthn.WithUserVerification(UserVerificationFromModel(userVerification))) }, webauthn.WithUserVerification(UserVerificationFromModel(userVerification)))
@ -137,7 +148,7 @@ func (w *WebAuthN) BeginLogin(user *usr_model.User, userVerification usr_model.U
}, nil }, nil
} }
func (w *WebAuthN) FinishLogin(user *usr_model.User, webAuthN *usr_model.WebAuthNLogin, credData []byte, webAuthNs ...*usr_model.WebAuthNToken) ([]byte, uint32, error) { func (w *WebAuthN) FinishLogin(user *usr_model.User, webAuthN *usr_model.WebAuthNLogin, credData []byte, isLoginUI bool, webAuthNs ...*usr_model.WebAuthNToken) ([]byte, uint32, error) {
assertionData, err := protocol.ParseCredentialRequestResponseBody(bytes.NewReader(credData)) assertionData, err := protocol.ParseCredentialRequestResponseBody(bytes.NewReader(credData))
if err != nil { if err != nil {
return nil, 0, caos_errs.ThrowInternal(err, "WEBAU-ADgv4", "Errors.User.WebAuthN.ValidateLoginFailed") return nil, 0, caos_errs.ThrowInternal(err, "WEBAU-ADgv4", "Errors.User.WebAuthN.ValidateLoginFailed")
@ -146,7 +157,7 @@ func (w *WebAuthN) FinishLogin(user *usr_model.User, webAuthN *usr_model.WebAuth
User: user, User: user,
credentials: WebAuthNsToCredentials(webAuthNs), credentials: WebAuthNsToCredentials(webAuthNs),
} }
credential, err := w.web.ValidateLogin(webUser, WebAuthNLoginToSessionData(webAuthN), assertionData) credential, err := w.web(isLoginUI).ValidateLogin(webUser, WebAuthNLoginToSessionData(webAuthN), assertionData)
if err != nil { if err != nil {
return nil, 0, caos_errs.ThrowInternal(err, "WEBAU-3M9si", "Errors.User.WebAuthN.ValidateLoginFailed") return nil, 0, caos_errs.ThrowInternal(err, "WEBAU-3M9si", "Errors.User.WebAuthN.ValidateLoginFailed")
} }
@ -156,3 +167,10 @@ func (w *WebAuthN) FinishLogin(user *usr_model.User, webAuthN *usr_model.WebAuth
} }
return credential.ID, credential.Authenticator.SignCount, nil return credential.ID, credential.Authenticator.SignCount, nil
} }
func (w *WebAuthN) web(isLoginUI bool) *webauthn.WebAuthn {
if isLoginUI {
return w.webLogin
}
return w.webConsole
}

View File

@ -656,6 +656,7 @@ message MultiFactor {
MfaType type = 1; MfaType type = 1;
MFAState state = 2; MFAState state = 2;
string attribute = 3; string attribute = 3;
string id = 4;
} }
message MfaOtpResponse { message MfaOtpResponse {

View File

@ -2029,6 +2029,7 @@ message UserMultiFactors {
message UserMultiFactor { message UserMultiFactor {
MfaType type = 1; MfaType type = 1;
MFAState state = 2; MFAState state = 2;
string attribute = 3;
} }
enum MfaType { enum MfaType {

169
site/package-lock.json generated
View File

@ -25,19 +25,19 @@
"dev": true "dev": true
}, },
"@babel/core": { "@babel/core": {
"version": "7.12.3", "version": "7.12.9",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz",
"integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.10.4", "@babel/code-frame": "^7.10.4",
"@babel/generator": "^7.12.1", "@babel/generator": "^7.12.5",
"@babel/helper-module-transforms": "^7.12.1", "@babel/helper-module-transforms": "^7.12.1",
"@babel/helpers": "^7.12.1", "@babel/helpers": "^7.12.5",
"@babel/parser": "^7.12.3", "@babel/parser": "^7.12.7",
"@babel/template": "^7.10.4", "@babel/template": "^7.12.7",
"@babel/traverse": "^7.12.1", "@babel/traverse": "^7.12.9",
"@babel/types": "^7.12.1", "@babel/types": "^7.12.7",
"convert-source-map": "^1.7.0", "convert-source-map": "^1.7.0",
"debug": "^4.1.0", "debug": "^4.1.0",
"gensync": "^1.0.0-beta.1", "gensync": "^1.0.0-beta.1",
@ -48,10 +48,55 @@
"source-map": "^0.5.0" "source-map": "^0.5.0"
}, },
"dependencies": { "dependencies": {
"@babel/parser": {
"version": "7.12.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz",
"integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==",
"dev": true
},
"@babel/template": {
"version": "7.12.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz",
"integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/parser": "^7.12.7",
"@babel/types": "^7.12.7"
}
},
"@babel/traverse": {
"version": "7.12.9",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz",
"integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/generator": "^7.12.5",
"@babel/helper-function-name": "^7.10.4",
"@babel/helper-split-export-declaration": "^7.11.0",
"@babel/parser": "^7.12.7",
"@babel/types": "^7.12.7",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.19"
}
},
"@babel/types": {
"version": "7.12.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz",
"integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.10.4",
"lodash": "^4.17.19",
"to-fast-properties": "^2.0.0"
}
},
"debug": { "debug": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"ms": "2.1.2" "ms": "2.1.2"
@ -1061,9 +1106,9 @@
} }
}, },
"@formatjs/ecma402-abstract": { "@formatjs/ecma402-abstract": {
"version": "1.4.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz",
"integrity": "sha512-Mv027hcLFjE45K8UJ8PjRpdDGfR0aManEFj1KzoN8zXNveHGEygpZGfFf/FTTMl+QEVSrPAUlyxaCApvmv47AQ==", "integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==",
"requires": { "requires": {
"tslib": "^2.0.1" "tslib": "^2.0.1"
} }
@ -1079,9 +1124,9 @@
"integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==" "integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw=="
}, },
"@rollup/plugin-babel": { "@rollup/plugin-babel": {
"version": "5.2.1", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.2.tgz",
"integrity": "sha512-Jd7oqFR2dzZJ3NWANDyBjwTtX/lYbZpVcmkHrfQcpvawHs9E4c0nYk5U2mfZ6I/DZcIvy506KZJi54XK/jxH7A==", "integrity": "sha512-MjmH7GvFT4TW8xFdIeFS3wqIX646y5tACdxkTO+khbHvS3ZcVJL6vkAHLw2wqPmkhwCfWHoNsp15VYNwW6JEJA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-module-imports": "^7.10.4", "@babel/helper-module-imports": "^7.10.4",
@ -1089,9 +1134,9 @@
} }
}, },
"@rollup/plugin-commonjs": { "@rollup/plugin-commonjs": {
"version": "15.1.0", "version": "17.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-15.1.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.0.0.tgz",
"integrity": "sha512-xCQqz4z/o0h2syQ7d9LskIMvBSH4PX5PjYdpSSvgS+pQik3WahkQVNWg3D8XJeYjZoVWnIUQYDghuEMRGrmQYQ==", "integrity": "sha512-/omBIJG1nHQc+bgkYDuLpb/V08QyutP9amOrJRUSlYJZP+b/68gM//D8sxJe3Yry2QnYIr3QjR3x4AlxJEN3GA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@rollup/pluginutils": "^3.1.0", "@rollup/pluginutils": "^3.1.0",
@ -1101,14 +1146,6 @@
"is-reference": "^1.2.1", "is-reference": "^1.2.1",
"magic-string": "^0.25.7", "magic-string": "^0.25.7",
"resolve": "^1.17.0" "resolve": "^1.17.0"
},
"dependencies": {
"estree-walker": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.1.tgz",
"integrity": "sha512-tF0hv+Yi2Ot1cwj9eYHtxC0jB9bmjacjQs6ZBTj82H8JwUywFuc+7E83NWfNMwHXZc11mjfFcVXPe9gEP4B8dg==",
"dev": true
}
} }
}, },
"@rollup/plugin-json": { "@rollup/plugin-json": {
@ -1145,14 +1182,14 @@
} }
}, },
"@rollup/plugin-url": { "@rollup/plugin-url": {
"version": "5.0.1", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-url/-/plugin-url-5.0.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-url/-/plugin-url-6.0.0.tgz",
"integrity": "sha512-/dO8Ic+vR9VtMkHjmFBWzISjX0iDwrB3vLg8sy4A7hxu2Uk0J09kAXbtku7gJb1fqVcJUIByFG5d/4sgNh1DvA==", "integrity": "sha512-UHGyoo3EbLufCZtXon/baNf75PAYT+Gs/NUgFMx2ZaDQ314PKNP0pJTrpbhDqFkZADzpkqJWyQOfJ2Nd6Qk4TA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@rollup/pluginutils": "^3.0.4", "@rollup/pluginutils": "^3.1.0",
"make-dir": "^3.0.0", "make-dir": "^3.1.0",
"mime": "^2.4.4" "mime": "^2.4.6"
} }
}, },
"@rollup/pluginutils": { "@rollup/pluginutils": {
@ -1599,9 +1636,9 @@
"dev": true "dev": true
}, },
"globalyzer": { "globalyzer": {
"version": "0.1.4", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.4.tgz", "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
"integrity": "sha512-LeguVWaxgHN0MNbWC6YljNMzHkrCny9fzjmEUdnF1kQ7wATFD1RHFRqA1qxaX2tgxGENlcxjOflopBwj3YZiXA==" "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q=="
}, },
"globrex": { "globrex": {
"version": "0.1.2", "version": "0.1.2",
@ -1647,9 +1684,9 @@
"dev": true "dev": true
}, },
"highlight.js": { "highlight.js": {
"version": "10.3.2", "version": "10.4.1",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.3.2.tgz", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.4.1.tgz",
"integrity": "sha512-3jRT7OUYsVsKvukNKZCtnvRcFyCJqSEIuIMsEybAXRiFSwpt65qjPd/Pr+UOdYt7WJlt+lj3+ypUsHiySBp/Jw==" "integrity": "sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg=="
}, },
"hosted-git-info": { "hosted-git-info": {
"version": "2.8.8", "version": "2.8.8",
@ -1703,21 +1740,21 @@
"dev": true "dev": true
}, },
"intl-messageformat": { "intl-messageformat": {
"version": "9.3.18", "version": "9.3.20",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.3.18.tgz", "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.3.20.tgz",
"integrity": "sha512-OKrLWppdxXtRdRCPjmRZ9Ru7UZkZJDlMl+1Vpb3sCLWK0mFpr129K+gIlIb5zrWoAH3NiYDzekBXPTRWCyHSIA==", "integrity": "sha512-jmpjYHE076J/0CIofrPhtUC4LfmsAhuv4JMQxytl2KJd2bim+3+gQJh+Z1vyHUzcj4fIHdt388ZGchb8f0NwOA==",
"requires": { "requires": {
"fast-memoize": "^2.5.2", "fast-memoize": "^2.5.2",
"intl-messageformat-parser": "6.0.16", "intl-messageformat-parser": "6.0.18",
"tslib": "^2.0.1" "tslib": "^2.0.1"
} }
}, },
"intl-messageformat-parser": { "intl-messageformat-parser": {
"version": "6.0.16", "version": "6.0.18",
"resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.0.16.tgz", "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.0.18.tgz",
"integrity": "sha512-Qy3Zz0vF4fhMVuW4BDqUr55LsOl9enM03wuwbP4Yg7v29rYNpf7Z76Whstu6uDLDJokrjbpgDvRcjSDTAhxKJw==", "integrity": "sha512-vLjACEunfi5uSUCWFLOR4PXQ9DGLpED3tM7o9zsYsOvjl0VIheoxyG0WZXnsnhn+S+Zu158M6CkuHXeNZfKRRg==",
"requires": { "requires": {
"@formatjs/ecma402-abstract": "1.4.0", "@formatjs/ecma402-abstract": "1.5.0",
"tslib": "^2.0.1" "tslib": "^2.0.1"
} }
}, },
@ -1904,9 +1941,9 @@
} }
}, },
"marked": { "marked": {
"version": "1.2.4", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/marked/-/marked-1.2.4.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.5.tgz",
"integrity": "sha512-6x5TFGCTKSQBLTZtOburGxCxFEBJEGYVLwCMTBCxzvyuisGcC20UNzDSJhCr/cJ/Kmh6ulfJm10g6WWEAJ3kvg==" "integrity": "sha512-2AlqgYnVPOc9WDyWu7S5DJaEZsfk6dNh/neatQ3IHUW4QLutM/VPSH9lG7bif+XjFWc9K9XR3QvR+fXuECmfdA=="
}, },
"matchit": { "matchit": {
"version": "1.1.0", "version": "1.1.0",
@ -2247,9 +2284,9 @@
} }
}, },
"rollup": { "rollup": {
"version": "2.33.2", "version": "2.34.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.33.2.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.34.0.tgz",
"integrity": "sha512-QPQ6/fWCrzHtSXkI269rhKaC7qXGghYBwXU04b1JsDZ6ibZa3DJ9D1SFAYRMgx1inDg0DaTbb3N4Z1NK/r3fhw==", "integrity": "sha512-dW5iLvttZzdVehjEuNJ1bWvuMEJjOWGmnuFS82WeKHTGXDkRHQeq/ExdifkSyJv9dLcR86ysKRmrIDyR6O0X8g==",
"dev": true, "dev": true,
"requires": { "requires": {
"fsevents": "~2.1.2" "fsevents": "~2.1.2"
@ -2547,15 +2584,15 @@
} }
}, },
"svelte": { "svelte": {
"version": "3.29.7", "version": "3.31.0",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.29.7.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.31.0.tgz",
"integrity": "sha512-rx0g311kBODvEWUU01DFBUl3MJuJven04bvTVFUG/w0On/wuj0PajQY/QlXcJndFxG+W1s8iXKaB418tdHWc3A==", "integrity": "sha512-r+n8UJkDqoQm1b+3tA3Lh6mHXKpcfOSOuEuIo5gE2W9wQYi64RYX/qE6CZBDDsP/H4M+N426JwY7XGH4xASvGQ==",
"dev": true "dev": true
}, },
"svelte-i18n": { "svelte-i18n": {
"version": "3.2.5", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.2.5.tgz", "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.3.0.tgz",
"integrity": "sha512-EBPEPcf3Ro7av0b8g/owlfb8fQgnYFan/9gfpRtJ5EmJ7SUUnoCD8JngN9FCKY/cBu1kg2aCUCM6XyauMuAvIA==", "integrity": "sha512-2pIfd4G8QfB87udPugt6g2Wl/qZ0kIB60+nQKHdon7d7aFzY5qsYT2NqQgK7dnVO2Gu0R8i2kSsNzsaVSk5UsA==",
"requires": { "requires": {
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"dlv": "^1.1.3", "dlv": "^1.1.3",
@ -2591,12 +2628,12 @@
} }
}, },
"tiny-glob": { "tiny-glob": {
"version": "0.2.6", "version": "0.2.8",
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.6.tgz", "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.8.tgz",
"integrity": "sha512-A7ewMqPu1B5PWwC3m7KVgAu96Ch5LA0w4SnEN/LbDREj/gAD0nPWboRbn8YoP9ISZXqeNAlMvKSKoEuhcfK3Pw==", "integrity": "sha512-vkQP7qOslq63XRX9kMswlby99kyO5OvKptw7AMwBVMjXEI7Tb61eoI5DydyEMOseyGS5anDN1VPoVxEvH01q8w==",
"requires": { "requires": {
"globalyzer": "^0.1.0", "globalyzer": "0.1.0",
"globrex": "^0.1.1" "globrex": "^0.1.2"
} }
}, },
"to-fast-properties": { "to-fast-properties": {

View File

@ -29,17 +29,17 @@
"@babel/plugin-transform-runtime": "^7.9.0", "@babel/plugin-transform-runtime": "^7.9.0",
"@babel/preset-env": "^7.12.1", "@babel/preset-env": "^7.12.1",
"@babel/runtime": "^7.12.1", "@babel/runtime": "^7.12.1",
"@rollup/plugin-commonjs": "^15.1.0", "@rollup/plugin-babel": "^5.0.0",
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-json": "^4.0.3", "@rollup/plugin-json": "^4.0.3",
"@rollup/plugin-node-resolve": "^10.0.0", "@rollup/plugin-node-resolve": "^10.0.0",
"@rollup/plugin-replace": "^2.2.0",
"@rollup/plugin-url": "^6.0.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"rollup": "^2.32.1", "rollup": "^2.32.1",
"rollup-plugin-svelte": "^6.1.0", "rollup-plugin-svelte": "^6.1.0",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"@rollup/plugin-babel": "^5.0.0",
"@rollup/plugin-replace": "^2.2.0",
"@rollup/plugin-url": "^5.0.0",
"sapper": "^0.28.10", "sapper": "^0.28.10",
"svelte": "^3.29.4" "svelte": "^3.31.0"
} }
} }