feat: project roles (#843)

* fix logging

* token verification

* feat: assert roles

* feat: add project role assertion on project and token type on app

* id and access token role assertion

* add project role check

* user grant required step in login

* update library

* fix merge

* fix merge

* fix merge

* update oidc library

* fix tests

* add tests for GrantRequiredStep

* add missing field ProjectRoleCheck on project view model

* fix project create

* fix project create
This commit is contained in:
Livio Amstutz
2020-10-16 07:49:38 +02:00
committed by GitHub
parent f5a7a0a09f
commit a321d850ae
57 changed files with 10894 additions and 18297 deletions

View File

@@ -222,32 +222,32 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
}
func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) *model.ExternalUser {
displayName := tokens.IDTokenClaims.PreferredUsername
if displayName == "" && tokens.IDTokenClaims.Email != "" {
displayName = tokens.IDTokenClaims.Email
displayName := tokens.IDTokenClaims.GetPreferredUsername()
if displayName == "" && tokens.IDTokenClaims.GetEmail() != "" {
displayName = tokens.IDTokenClaims.GetEmail()
}
switch idpConfig.OIDCIDPDisplayNameMapping {
case iam_model.OIDCMappingFieldEmail:
if tokens.IDTokenClaims.EmailVerified && tokens.IDTokenClaims.Email != "" {
displayName = tokens.IDTokenClaims.Email
if tokens.IDTokenClaims.IsEmailVerified() && tokens.IDTokenClaims.GetEmail() != "" {
displayName = tokens.IDTokenClaims.GetEmail()
}
}
externalUser := &model.ExternalUser{
IDPConfigID: idpConfig.IDPConfigID,
ExternalUserID: tokens.IDTokenClaims.Subject,
PreferredUsername: tokens.IDTokenClaims.PreferredUsername,
ExternalUserID: tokens.IDTokenClaims.GetSubject(),
PreferredUsername: tokens.IDTokenClaims.GetPreferredUsername(),
DisplayName: displayName,
FirstName: tokens.IDTokenClaims.GivenName,
LastName: tokens.IDTokenClaims.FamilyName,
NickName: tokens.IDTokenClaims.Nickname,
Email: tokens.IDTokenClaims.Email,
IsEmailVerified: tokens.IDTokenClaims.EmailVerified,
FirstName: tokens.IDTokenClaims.GetGivenName(),
LastName: tokens.IDTokenClaims.GetFamilyName(),
NickName: tokens.IDTokenClaims.GetNickname(),
Email: tokens.IDTokenClaims.GetEmail(),
IsEmailVerified: tokens.IDTokenClaims.IsEmailVerified(),
}
if tokens.IDTokenClaims.PhoneNumber != "" {
externalUser.Phone = tokens.IDTokenClaims.PhoneNumber
externalUser.IsPhoneVerified = tokens.IDTokenClaims.PhoneNumberVerified
if tokens.IDTokenClaims.GetPhoneNumber() != "" {
externalUser.Phone = tokens.IDTokenClaims.GetPhoneNumber()
externalUser.IsPhoneVerified = tokens.IDTokenClaims.IsPhoneNumberVerified()
}
return externalUser
}

View File

@@ -1,8 +1,13 @@
package handler
import (
"net/http"
"strings"
"github.com/caos/oidc/pkg/oidc"
"github.com/caos/oidc/pkg/rp"
"golang.org/x/text/language"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/auth_request/model"
caos_errors "github.com/caos/zitadel/internal/errors"
@@ -10,8 +15,6 @@ import (
iam_model "github.com/caos/zitadel/internal/iam/model"
org_model "github.com/caos/zitadel/internal/org/model"
usr_model "github.com/caos/zitadel/internal/user/model"
"net/http"
"strings"
)
func (l *Login) handleExternalRegister(w http.ResponseWriter, r *http.Request) {
@@ -109,11 +112,11 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques
}
func (l *Login) mapTokenToLoginUserAndExternalIDP(orgIamPolicy *iam_model.OrgIAMPolicyView, tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) (*usr_model.User, *usr_model.ExternalIDP) {
username := tokens.IDTokenClaims.PreferredUsername
username := tokens.IDTokenClaims.GetPreferredUsername()
switch idpConfig.OIDCUsernameMapping {
case iam_model.OIDCMappingFieldEmail:
if tokens.IDTokenClaims.EmailVerified && tokens.IDTokenClaims.Email != "" {
username = tokens.IDTokenClaims.Email
if tokens.IDTokenClaims.IsEmailVerified() && tokens.IDTokenClaims.GetEmail() != "" {
username = tokens.IDTokenClaims.GetEmail()
}
}
@@ -128,35 +131,35 @@ func (l *Login) mapTokenToLoginUserAndExternalIDP(orgIamPolicy *iam_model.OrgIAM
UserName: username,
Human: &usr_model.Human{
Profile: &usr_model.Profile{
FirstName: tokens.IDTokenClaims.GivenName,
LastName: tokens.IDTokenClaims.FamilyName,
PreferredLanguage: tokens.IDTokenClaims.Locale,
NickName: tokens.IDTokenClaims.Nickname,
FirstName: tokens.IDTokenClaims.GetGivenName(),
LastName: tokens.IDTokenClaims.GetFamilyName(),
PreferredLanguage: language.Tag(tokens.IDTokenClaims.GetLocale()),
NickName: tokens.IDTokenClaims.GetNickname(),
},
Email: &usr_model.Email{
EmailAddress: tokens.IDTokenClaims.Email,
IsEmailVerified: tokens.IDTokenClaims.EmailVerified,
EmailAddress: tokens.IDTokenClaims.GetEmail(),
IsEmailVerified: tokens.IDTokenClaims.IsEmailVerified(),
},
},
}
if tokens.IDTokenClaims.PhoneNumber != "" {
if tokens.IDTokenClaims.GetPhoneNumber() != "" {
user.Phone = &usr_model.Phone{
PhoneNumber: tokens.IDTokenClaims.PhoneNumber,
IsPhoneVerified: tokens.IDTokenClaims.PhoneNumberVerified,
PhoneNumber: tokens.IDTokenClaims.GetPhoneNumber(),
IsPhoneVerified: tokens.IDTokenClaims.IsPhoneNumberVerified(),
}
}
displayName := tokens.IDTokenClaims.PreferredUsername
displayName := tokens.IDTokenClaims.GetPreferredUsername()
switch idpConfig.OIDCIDPDisplayNameMapping {
case iam_model.OIDCMappingFieldEmail:
if tokens.IDTokenClaims.EmailVerified && tokens.IDTokenClaims.Email != "" {
displayName = tokens.IDTokenClaims.Email
if tokens.IDTokenClaims.IsEmailVerified() && tokens.IDTokenClaims.GetEmail() != "" {
displayName = tokens.IDTokenClaims.GetEmail()
}
}
externalIDP := &usr_model.ExternalIDP{
IDPConfigID: idpConfig.IDPConfigID,
UserID: tokens.IDTokenClaims.Subject,
UserID: tokens.IDTokenClaims.GetSubject(),
DisplayName: displayName,
}
return user, externalIDP

View File

@@ -211,6 +211,8 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *
l.renderExternalNotFoundOption(w, r, authReq, err)
case *model.ExternalLoginStep:
l.handleExternalLoginStep(w, r, authReq, step.SelectedIDPConfigID)
case *model.GrantRequiredStep:
l.renderInternalError(w, r, authReq, caos_errs.ThrowPreconditionFailed(nil, "APP-asb43", "Errors.User.GrantRequired"))
default:
l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(nil, "APP-ds3QF", "step no possible"))
}

View File

@@ -187,6 +187,7 @@ Errors:
AuthRequest:
NotFound: AuthRequest konnte nicht gefunden werden
UserAgentNotCorresponding: User Agent stimmt nicht überein
RequestTypeNotSupported: Requesttyp wird nicht unterstürzt
User:
NotFound: Benutzer konnte nicht gefunden werden
NotMatchingUserID: User stimm nicht mit User in Auth Request überein
@@ -226,5 +227,6 @@ Errors:
ExternalIDP:
IDPTypeNotImplemented: IDP Typ ist nicht implementiert
NotAllowed: Externer Login Provider ist nicht erlaubt
GrantRequired: Der Login an diese Applikation ist nicht möglich. Der Benutzer benötigt mindestens eine Berechtigung an der Applikation. Bitte melde dich bei deinem Administrator.
optional: (optional)

View File

@@ -187,6 +187,7 @@ Errors:
AuthRequest:
NotFound: Could not find authrequest
UserAgentNotCorresponding: User Agent does not correspond
RequestTypeNotSupported: Request type is not supported
User:
NotFound: User could not be found
NotMatchingUserID: User and user in authrequest don't match
@@ -226,6 +227,7 @@ Errors:
ExternalIDP:
IDPTypeNotImplemented: IDP Type is not implemented
NotAllowed: External Login Provider not allowed
GrantRequired: Login not possible. The user is required to have at least one grant on the application. Please contact your administrator.
optional: (optional)

View File

@@ -1,6 +1,6 @@
{{template "main-top" .}}
<div>
<div class="head">
{{ .ErrType }}
{{ .ErrMessage }}
</div>