mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-19 14:27:32 +00:00
8a5badddf6
* fix: change oidc config * fix: change oidc config secret * begin models * begin repo * fix: implement grpc app funcs * fix: add application requests * fix: converter * fix: converter * fix: converter and generate clientid * fix: tests * feat: project grant aggregate * feat: project grant * fix: project grant check if role existing * fix: project grant requests * fix: project grant fixes * fix: project grant member model * fix: project grant member aggregate * fix: project grant member eventstore * fix: project grant member requests * feat: user model * begin repo * repo models and more * feat: user command side * lots of functions * user command side * profile requests * commit before rebase on user * save * local config with gopass and more * begin new auth command (user centric) * Update internal/user/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/address.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/address.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/email.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/email.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/email.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/mfa.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/mfa.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/password.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/password.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/password.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/phone.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/phone.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/phone.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/usergrant/repository/eventsourcing/model/user_grant.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/usergrant/repository/eventsourcing/model/user_grant.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/usergrant/repository/eventsourcing/user_grant.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/user_test.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/eventstore_mock_test.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * changes from mr review * save files into basedir * changes from mr review * changes from mr review * move to auth request * Update internal/usergrant/repository/eventsourcing/cache.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update internal/usergrant/repository/eventsourcing/cache.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * changes requested on mr * fix generate codes * fix return if no events * password code * email verification step * more steps * lot of mfa * begin tests * more next steps * auth api * auth api (user) * auth api (user) * auth api (user) * differ requests * merge * tests * fix compilation error * mock for id generator * Update internal/user/repository/eventsourcing/model/password.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * requests of mr * check email * begin separation of command and query * otp * change packages * some cleanup and fixes * tests for auth request / next steps * add VerificationLifetimes to config and make it run * tests * fix code challenge validation * cleanup * fix merge * begin view * repackaging tests and configs * fix startup config for auth * add migration * add PromptSelectAccount * fix copy / paste * remove user_agent files * fixes * fix sequences in user_session * token commands * token queries and signout * fix * fix set password test * add token handler and table * handle session init * add session state * add user view test cases * change VerifyMyMfaOTP * some fixes * fix user repo in auth api * cleanup * add user session view test * fix merge * begin oidc * user agent and more * config * keys * key command and query * add login statics * key handler * start login * login handlers * lot of fixes * merge oidc * add missing exports * add missing exports * fix some bugs * authrequestid in htmls * getrequest * update auth request * fix userid check * add username to authrequest * fix user session and auth request handling * fix UserSessionsByAgentID * fix auth request tests * fix user session on UserPasswordChanged and MfaOtpRemoved * fix MfaTypesSetupPossible * handle mfa * fill username * auth request query checks new events * fix userSessionByIDs * fix tokens * fix userSessionByIDs test * add user selection * init code * user code creation date * add init user step * add verification failed types * add verification failures * verify init code * user init code handle * user init code handle * fix userSessionByIDs * update logging * user agent cookie * browserinfo from request * add DeleteAuthRequest * add static login files to binary * add login statik to build * move generate to separate file and remove statik.go files * remove static dirs from startup.yaml * generate into separate namespaces * merge master * auth request code * auth request type mapping * fix keys * improve tokens * improve register and basic styling * fix ailerons font * improve password reset * add audience to token * all oidc apps as audience * fix test nextStep * fix email texts * remove "not set" * lot of style changes * improve copy to clipboard * fix footer * add cookie handler * remove placeholders * fix compilation after merge * fix auth config * remove comments * typo * use new secrets store * change default pws to match default policy * fixes * add todo * enable login * fix db name * Auth queries (#179) * my usersession * org structure/ auth handlers * working user grant spooler * auth internal user grants * search my project orgs * remove permissions file * my zitadel permissions * my zitadel permissions * remove unused code * authz * app searches in view * token verification * fix user grant load * fix tests * fix tests * read configs * remove unused const * remove todos * env variables * app_name * working authz * search projects * global resourceowner * Update internal/api/auth/permissions.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * Update internal/api/auth/permissions.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * model2 rename * at least it works * check token expiry * search my user grants * remove token table from authz Co-authored-by: Livio Amstutz <livio.a@gmail.com> * fix test * fix ports and enable console Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com> Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Silvan <silvan.reusser@gmail.com>
345 lines
11 KiB
Go
345 lines
11 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"github.com/caos/logging"
|
|
"github.com/caos/zitadel/internal/errors"
|
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
|
"github.com/caos/zitadel/internal/eventstore"
|
|
"github.com/caos/zitadel/internal/eventstore/models"
|
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
|
"github.com/caos/zitadel/internal/eventstore/spooler"
|
|
iam_events "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
|
|
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
|
|
org_model "github.com/caos/zitadel/internal/org/model"
|
|
org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
|
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
|
proj_model "github.com/caos/zitadel/internal/project/model"
|
|
proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing"
|
|
proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
|
|
usr_model "github.com/caos/zitadel/internal/user/model"
|
|
usr_events "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
|
usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
|
grant_es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model"
|
|
view_model "github.com/caos/zitadel/internal/usergrant/repository/view/model"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type UserGrant struct {
|
|
handler
|
|
eventstore eventstore.Eventstore
|
|
projectEvents *proj_event.ProjectEventstore
|
|
userEvents *usr_events.UserEventstore
|
|
orgEvents *org_events.OrgEventstore
|
|
iamEvents *iam_events.IamEventstore
|
|
iamID string
|
|
iamProjectID string
|
|
}
|
|
|
|
const (
|
|
userGrantTable = "auth.user_grants"
|
|
)
|
|
|
|
func (u *UserGrant) MinimumCycleDuration() time.Duration { return u.cycleDuration }
|
|
|
|
func (u *UserGrant) ViewModel() string {
|
|
return userGrantTable
|
|
}
|
|
|
|
func (u *UserGrant) EventQuery() (*models.SearchQuery, error) {
|
|
if u.iamProjectID == "" {
|
|
err := u.setIamProjectID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
sequence, err := u.view.GetLatestUserGrantSequence()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return es_models.NewSearchQuery().
|
|
AggregateTypeFilter(grant_es_model.UserGrantAggregate, iam_es_model.IamAggregate, org_es_model.OrgAggregate, usr_es_model.UserAggregate, proj_es_model.ProjectAggregate).
|
|
LatestSequenceFilter(sequence), nil
|
|
}
|
|
|
|
func (u *UserGrant) Process(event *models.Event) (err error) {
|
|
switch event.AggregateType {
|
|
case grant_es_model.UserGrantAggregate:
|
|
err = u.processUserGrant(event)
|
|
case usr_es_model.UserAggregate:
|
|
err = u.processUser(event)
|
|
case proj_es_model.ProjectAggregate:
|
|
err = u.processProject(event)
|
|
case iam_es_model.IamAggregate:
|
|
err = u.processIamMember(event, "IAM", false)
|
|
case org_es_model.OrgAggregate:
|
|
return u.processOrg(event)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (u *UserGrant) processUserGrant(event *models.Event) (err error) {
|
|
grant := new(view_model.UserGrantView)
|
|
switch event.Type {
|
|
case grant_es_model.UserGrantAdded:
|
|
err = grant.AppendEvent(event)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = u.fillData(grant, event.ResourceOwner)
|
|
case grant_es_model.UserGrantChanged,
|
|
grant_es_model.UserGrantDeactivated,
|
|
grant_es_model.UserGrantReactivated:
|
|
grant, err = u.view.UserGrantByID(event.AggregateID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = grant.AppendEvent(event)
|
|
case grant_es_model.UserGrantRemoved:
|
|
err = u.view.DeleteUserGrant(event.AggregateID, event.Sequence)
|
|
default:
|
|
return u.view.ProcessedUserGrantSequence(event.Sequence)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return u.view.PutUserGrant(grant, grant.Sequence)
|
|
}
|
|
|
|
func (u *UserGrant) processUser(event *models.Event) (err error) {
|
|
switch event.Type {
|
|
case usr_es_model.UserProfileChanged,
|
|
usr_es_model.UserEmailChanged:
|
|
grants, err := u.view.UserGrantsByUserID(event.AggregateID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
user, err := u.userEvents.UserByID(context.Background(), event.AggregateID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, grant := range grants {
|
|
u.fillUserData(grant, user)
|
|
err = u.view.PutUserGrant(grant, event.Sequence)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
default:
|
|
return u.view.ProcessedUserGrantSequence(event.Sequence)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (u *UserGrant) processProject(event *models.Event) (err error) {
|
|
switch event.Type {
|
|
case proj_es_model.ProjectChanged:
|
|
grants, err := u.view.UserGrantsByProjectID(event.AggregateID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
project, err := u.projectEvents.ProjectByID(context.Background(), event.AggregateID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, grant := range grants {
|
|
u.fillProjectData(grant, project)
|
|
return u.view.PutUserGrant(grant, event.Sequence)
|
|
}
|
|
case proj_es_model.ProjectMemberAdded, proj_es_model.ProjectMemberChanged, proj_es_model.ProjectMemberRemoved:
|
|
member := new(proj_es_model.ProjectMember)
|
|
member.SetData(event)
|
|
return u.processMember(event, "PROJECT", true, member.UserID, member.Roles)
|
|
case proj_es_model.ProjectGrantMemberAdded, proj_es_model.ProjectGrantMemberChanged, proj_es_model.ProjectGrantMemberRemoved:
|
|
member := new(proj_es_model.ProjectGrantMember)
|
|
member.SetData(event)
|
|
return u.processMember(event, "PROJECT_GRANT", true, member.UserID, member.Roles)
|
|
default:
|
|
return u.view.ProcessedUserGrantSequence(event.Sequence)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (u *UserGrant) processOrg(event *models.Event) (err error) {
|
|
switch event.Type {
|
|
case org_es_model.OrgMemberAdded, org_es_model.OrgMemberChanged, org_es_model.OrgMemberRemoved:
|
|
member := new(org_es_model.OrgMember)
|
|
member.SetData(event)
|
|
return u.processMember(event, "ORG", false, member.UserID, member.Roles)
|
|
default:
|
|
return u.view.ProcessedUserGrantSequence(event.Sequence)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (u *UserGrant) processIamMember(event *models.Event, rolePrefix string, suffix bool) error {
|
|
member := new(iam_es_model.IamMember)
|
|
|
|
switch event.Type {
|
|
case iam_es_model.IamMemberAdded, iam_es_model.IamMemberChanged:
|
|
member.SetData(event)
|
|
|
|
grant, err := u.view.UserGrantByIDs(u.iamID, u.iamProjectID, member.UserID)
|
|
if err != nil && !errors.IsNotFound(err) {
|
|
return err
|
|
}
|
|
if errors.IsNotFound(err) {
|
|
grant = &view_model.UserGrantView{
|
|
ID: u.iamProjectID + member.UserID,
|
|
ResourceOwner: u.iamID,
|
|
OrgName: u.iamID,
|
|
OrgDomain: u.iamID,
|
|
ProjectID: u.iamProjectID,
|
|
UserID: member.UserID,
|
|
RoleKeys: member.Roles,
|
|
CreationDate: event.CreationDate,
|
|
}
|
|
if suffix {
|
|
grant.RoleKeys = suffixRoles(event.AggregateID, grant.RoleKeys)
|
|
}
|
|
} else {
|
|
newRoles := member.Roles
|
|
if grant.RoleKeys != nil {
|
|
grant.RoleKeys = mergeExistingRoles(rolePrefix, grant.RoleKeys, newRoles)
|
|
} else {
|
|
grant.RoleKeys = newRoles
|
|
}
|
|
|
|
}
|
|
grant.Sequence = event.Sequence
|
|
grant.ChangeDate = event.CreationDate
|
|
return u.view.PutUserGrant(grant, grant.Sequence)
|
|
case iam_es_model.IamMemberRemoved:
|
|
member.SetData(event)
|
|
grant, err := u.view.UserGrantByIDs(u.iamID, u.iamProjectID, member.UserID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return u.view.DeleteUserGrant(grant.ID, event.Sequence)
|
|
default:
|
|
return u.view.ProcessedUserGrantSequence(event.Sequence)
|
|
}
|
|
}
|
|
|
|
func (u *UserGrant) processMember(event *models.Event, rolePrefix string, suffix bool, userID string, roleKeys []string) error {
|
|
switch event.Type {
|
|
case org_es_model.OrgMemberAdded, proj_es_model.ProjectMemberAdded, proj_es_model.ProjectGrantMemberAdded,
|
|
org_es_model.OrgMemberChanged, proj_es_model.ProjectMemberChanged, proj_es_model.ProjectGrantMemberChanged:
|
|
|
|
grant, err := u.view.UserGrantByIDs(event.ResourceOwner, u.iamProjectID, userID)
|
|
if err != nil && !errors.IsNotFound(err) {
|
|
return err
|
|
}
|
|
if suffix {
|
|
roleKeys = suffixRoles(event.AggregateID, roleKeys)
|
|
}
|
|
if errors.IsNotFound(err) {
|
|
grant = &view_model.UserGrantView{
|
|
ID: u.iamProjectID + event.ResourceOwner + userID,
|
|
ResourceOwner: event.ResourceOwner,
|
|
ProjectID: u.iamProjectID,
|
|
UserID: userID,
|
|
RoleKeys: roleKeys,
|
|
CreationDate: event.CreationDate,
|
|
}
|
|
u.fillData(grant, event.ResourceOwner)
|
|
} else {
|
|
newRoles := roleKeys
|
|
if grant.RoleKeys != nil {
|
|
grant.RoleKeys = mergeExistingRoles(rolePrefix, grant.RoleKeys, newRoles)
|
|
} else {
|
|
grant.RoleKeys = newRoles
|
|
}
|
|
}
|
|
grant.Sequence = event.Sequence
|
|
grant.ChangeDate = event.CreationDate
|
|
return u.view.PutUserGrant(grant, event.Sequence)
|
|
case org_es_model.OrgMemberRemoved,
|
|
proj_es_model.ProjectMemberRemoved,
|
|
proj_es_model.ProjectGrantMemberRemoved:
|
|
|
|
grant, err := u.view.UserGrantByIDs(event.ResourceOwner, u.iamProjectID, userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return u.view.DeleteUserGrant(grant.ID, event.Sequence)
|
|
default:
|
|
return u.view.ProcessedUserGrantSequence(event.Sequence)
|
|
}
|
|
}
|
|
|
|
func suffixRoles(suffix string, roles []string) []string {
|
|
suffixedRoles := make([]string, len(roles))
|
|
for i := 0; i < len(roles); i++ {
|
|
suffixedRoles[i] = roles[i] + ":" + suffix
|
|
}
|
|
return suffixedRoles
|
|
}
|
|
|
|
func mergeExistingRoles(rolePrefix string, existingRoles, newRoles []string) []string {
|
|
mergedRoles := make([]string, 0)
|
|
for _, existing := range existingRoles {
|
|
if !strings.HasPrefix(existing, rolePrefix) {
|
|
mergedRoles = append(mergedRoles, existing)
|
|
}
|
|
}
|
|
return append(mergedRoles, newRoles...)
|
|
}
|
|
|
|
func (u *UserGrant) setIamProjectID() error {
|
|
if u.iamProjectID != "" {
|
|
return nil
|
|
}
|
|
iam, err := u.iamEvents.IamByID(context.Background(), u.iamID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !iam.SetUpDone {
|
|
return caos_errs.ThrowPreconditionFailed(nil, "HANDL-s5DTs", "Setup not done")
|
|
}
|
|
u.iamProjectID = iam.IamProjectID
|
|
return nil
|
|
}
|
|
|
|
func (u *UserGrant) fillData(grant *view_model.UserGrantView, resourceOwner string) (err error) {
|
|
user, err := u.userEvents.UserByID(context.Background(), grant.UserID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
u.fillUserData(grant, user)
|
|
project, err := u.projectEvents.ProjectByID(context.Background(), grant.ProjectID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
u.fillProjectData(grant, project)
|
|
|
|
org, err := u.orgEvents.OrgByID(context.TODO(), org_model.NewOrg(resourceOwner))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
u.fillOrgData(grant, org)
|
|
return nil
|
|
}
|
|
|
|
func (u *UserGrant) fillUserData(grant *view_model.UserGrantView, user *usr_model.User) {
|
|
grant.UserName = user.UserName
|
|
grant.FirstName = user.FirstName
|
|
grant.LastName = user.LastName
|
|
grant.Email = user.EmailAddress
|
|
}
|
|
|
|
func (u *UserGrant) fillProjectData(grant *view_model.UserGrantView, project *proj_model.Project) {
|
|
grant.ProjectName = project.Name
|
|
}
|
|
|
|
func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView, org *org_model.Org) {
|
|
grant.OrgDomain = org.Domain
|
|
grant.OrgName = org.Name
|
|
}
|
|
|
|
func (u *UserGrant) OnError(event *models.Event, err error) error {
|
|
logging.LogWithFields("SPOOL-8is4s", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler")
|
|
return spooler.HandleError(event, err, u.view.GetLatestUserGrantFailedEvent, u.view.ProcessedUserGrantFailedEvent, u.view.ProcessedUserGrantSequence, u.errorCountUntilSkip)
|
|
}
|