mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 10:57:35 +00:00
feat: Login, OP Support and Auth Queries (#177)
* 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>
This commit is contained in:
20
internal/authz/repository/eventsourcing/eventstore/iam.go
Normal file
20
internal/authz/repository/eventsourcing/eventstore/iam.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package eventstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/iam/model"
|
||||
iam_event "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
|
||||
)
|
||||
|
||||
type IamRepo struct {
|
||||
IamID string
|
||||
IamEvents *iam_event.IamEventstore
|
||||
}
|
||||
|
||||
func (repo *IamRepo) Health(ctx context.Context) error {
|
||||
return repo.IamEvents.Health(ctx)
|
||||
}
|
||||
|
||||
func (repo *IamRepo) IamByID(ctx context.Context) (*model.Iam, error) {
|
||||
return repo.IamEvents.IamByID(ctx, repo.IamID)
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
package eventstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/view"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
iam_event "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
|
||||
proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TokenVerifierRepo struct {
|
||||
TokenVerificationKey [32]byte
|
||||
IamID string
|
||||
IamEvents *iam_event.IamEventstore
|
||||
ProjectEvents *proj_event.ProjectEventstore
|
||||
View *view.View
|
||||
}
|
||||
|
||||
func (repo *TokenVerifierRepo) VerifyAccessToken(ctx context.Context, tokenString, appName, appID string) (userID string, clientID string, agentID string, err error) {
|
||||
clientID, err = repo.verifierClientID(ctx, appName, appID)
|
||||
if err != nil {
|
||||
return "", "", "", caos_errs.ThrowPermissionDenied(nil, "APP-ptTIF2", "invalid token")
|
||||
}
|
||||
//TODO: use real key
|
||||
tokenID, err := crypto.DecryptAESString(tokenString, string(repo.TokenVerificationKey[:32]))
|
||||
if err != nil {
|
||||
return "", "", "", caos_errs.ThrowPermissionDenied(nil, "APP-8EF0zZ", "invalid token")
|
||||
}
|
||||
token, err := repo.View.TokenByID(tokenID)
|
||||
if err != nil {
|
||||
return "", "", "", caos_errs.ThrowPermissionDenied(err, "APP-BxUSiL", "invalid token")
|
||||
}
|
||||
if !token.Expiration.After(time.Now().UTC()) {
|
||||
return "", "", "", caos_errs.ThrowPermissionDenied(err, "APP-k9KS0", "invalid token")
|
||||
}
|
||||
|
||||
for _, aud := range token.Audience {
|
||||
if clientID == aud {
|
||||
return token.UserID, clientID, token.UserAgentID, nil
|
||||
}
|
||||
}
|
||||
return "", "", "", caos_errs.ThrowPermissionDenied(nil, "APP-Zxfako", "invalid audience")
|
||||
}
|
||||
|
||||
func (repo *TokenVerifierRepo) ProjectIDByClientID(ctx context.Context, clientID string) (projectID string, err error) {
|
||||
app, err := repo.View.ApplicationByOIDCClientID(clientID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return app.ID, nil
|
||||
}
|
||||
|
||||
func (repo *TokenVerifierRepo) verifierClientID(ctx context.Context, appName, appClientID string) (string, error) {
|
||||
if appClientID != "" {
|
||||
return appClientID, nil
|
||||
}
|
||||
iam, err := repo.IamEvents.IamByID(ctx, repo.IamID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
app, err := repo.View.ApplicationByProjecIDAndAppName(iam.IamProjectID, appName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return app.OIDCClientID, nil
|
||||
}
|
102
internal/authz/repository/eventsourcing/eventstore/user_grant.go
Normal file
102
internal/authz/repository/eventsourcing/eventstore/user_grant.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package eventstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/view"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
iam_event "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
|
||||
grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||
"github.com/caos/zitadel/internal/usergrant/repository/view/model"
|
||||
)
|
||||
|
||||
type UserGrantRepo struct {
|
||||
View *view.View
|
||||
IamID string
|
||||
IamProjectID string
|
||||
Auth auth.Config
|
||||
IamEvents *iam_event.IamEventstore
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) Health() error {
|
||||
return repo.View.Health()
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) ResolveGrants(ctx context.Context) (*auth.Grant, error) {
|
||||
err := repo.fillIamProjectID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctxData := auth.GetCtxData(ctx)
|
||||
|
||||
orgGrant, err := repo.View.UserGrantByIDs(ctxData.OrgID, repo.IamProjectID, ctxData.UserID)
|
||||
if err != nil && !caos_errs.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
iamAdminGrant, err := repo.View.UserGrantByIDs(repo.IamID, repo.IamProjectID, ctxData.UserID)
|
||||
if err != nil && !caos_errs.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mergeOrgAndAdminGrant(ctxData, orgGrant, iamAdminGrant), nil
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) SearchMyZitadelPermissions(ctx context.Context) ([]string, error) {
|
||||
grant, err := repo.ResolveGrants(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
permissions := &grant_model.Permissions{Permissions: []string{}}
|
||||
for _, role := range grant.Roles {
|
||||
roleName, ctxID := auth.SplitPermission(role)
|
||||
for _, mapping := range repo.Auth.RolePermissionMappings {
|
||||
if mapping.Role == roleName {
|
||||
permissions.AppendPermissions(ctxID, mapping.Permissions...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return permissions.Permissions, nil
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) fillIamProjectID(ctx context.Context) error {
|
||||
if repo.IamProjectID != "" {
|
||||
return nil
|
||||
}
|
||||
iam, err := repo.IamEvents.IamByID(ctx, repo.IamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !iam.SetUpDone {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-skiwS", "Setup not done")
|
||||
}
|
||||
repo.IamProjectID = iam.IamProjectID
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeOrgAndAdminGrant(ctxData auth.CtxData, orgGrant, iamAdminGrant *model.UserGrantView) (grant *auth.Grant) {
|
||||
if orgGrant != nil {
|
||||
roles := orgGrant.RoleKeys
|
||||
if iamAdminGrant != nil {
|
||||
roles = addIamAdminRoles(roles, iamAdminGrant.RoleKeys)
|
||||
}
|
||||
grant = &auth.Grant{OrgID: orgGrant.ResourceOwner, Roles: roles}
|
||||
} else if iamAdminGrant != nil {
|
||||
grant = &auth.Grant{
|
||||
OrgID: ctxData.OrgID,
|
||||
Roles: iamAdminGrant.RoleKeys,
|
||||
}
|
||||
}
|
||||
return grant
|
||||
}
|
||||
|
||||
func addIamAdminRoles(orgRoles, iamAdminRoles []string) []string {
|
||||
result := make([]string, 0)
|
||||
result = append(result, iamAdminRoles...)
|
||||
for _, role := range orgRoles {
|
||||
if !auth.ExistsPerm(result, role) {
|
||||
result = append(result, role)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||
"github.com/caos/zitadel/internal/project/repository/eventsourcing"
|
||||
es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
|
||||
view_model "github.com/caos/zitadel/internal/project/repository/view/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
handler
|
||||
}
|
||||
|
||||
const (
|
||||
applicationTable = "authz.applications"
|
||||
)
|
||||
|
||||
func (p *Application) MinimumCycleDuration() time.Duration { return p.cycleDuration }
|
||||
|
||||
func (p *Application) ViewModel() string {
|
||||
return applicationTable
|
||||
}
|
||||
|
||||
func (p *Application) EventQuery() (*models.SearchQuery, error) {
|
||||
sequence, err := p.view.GetLatestApplicationSequence()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return eventsourcing.ProjectQuery(sequence), nil
|
||||
}
|
||||
|
||||
func (p *Application) Process(event *models.Event) (err error) {
|
||||
app := new(view_model.ApplicationView)
|
||||
switch event.Type {
|
||||
case es_model.ApplicationAdded:
|
||||
app.AppendEvent(event)
|
||||
case es_model.ApplicationChanged,
|
||||
es_model.OIDCConfigAdded,
|
||||
es_model.OIDCConfigChanged,
|
||||
es_model.ApplicationDeactivated,
|
||||
es_model.ApplicationReactivated:
|
||||
err := app.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app, err = p.view.ApplicationByID(app.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app.AppendEvent(event)
|
||||
case es_model.ApplicationRemoved:
|
||||
err := app.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.view.DeleteApplication(app.ID, event.Sequence)
|
||||
default:
|
||||
return p.view.ProcessedApplicationSequence(event.Sequence)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.view.PutApplication(app)
|
||||
}
|
||||
|
||||
func (p *Application) OnError(event *models.Event, spoolerError error) error {
|
||||
logging.LogWithFields("SPOOL-sjZw", "id", event.AggregateID).WithError(spoolerError).Warn("something went wrong in project app handler")
|
||||
return spooler.HandleError(event, spoolerError, p.view.GetLatestApplicationFailedEvent, p.view.ProcessedApplicationFailedEvent, p.view.ProcessedApplicationSequence, p.errorCountUntilSkip)
|
||||
}
|
49
internal/authz/repository/eventsourcing/handler/handler.go
Normal file
49
internal/authz/repository/eventsourcing/handler/handler.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
iam_events "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/view"
|
||||
"github.com/caos/zitadel/internal/config/types"
|
||||
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||
)
|
||||
|
||||
type Configs map[string]*Config
|
||||
|
||||
type Config struct {
|
||||
MinimumCycleDuration types.Duration
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
view *view.View
|
||||
bulkLimit uint64
|
||||
cycleDuration time.Duration
|
||||
errorCountUntilSkip uint64
|
||||
}
|
||||
|
||||
type EventstoreRepos struct {
|
||||
IamEvents *iam_events.IamEventstore
|
||||
}
|
||||
|
||||
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []spooler.Handler {
|
||||
return []spooler.Handler{
|
||||
&UserGrant{
|
||||
handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount},
|
||||
eventstore: eventstore,
|
||||
iamID: systemDefaults.IamID,
|
||||
iamEvents: repos.IamEvents,
|
||||
},
|
||||
&Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}},
|
||||
}
|
||||
}
|
||||
|
||||
func (configs Configs) cycleDuration(viewModel string) time.Duration {
|
||||
c, ok := configs[viewModel]
|
||||
if !ok {
|
||||
return 1 * time.Second
|
||||
}
|
||||
return c.MinimumCycleDuration.Duration
|
||||
}
|
226
internal/authz/repository/eventsourcing/handler/user_grant.go
Normal file
226
internal/authz/repository/eventsourcing/handler/user_grant.go
Normal file
@@ -0,0 +1,226 @@
|
||||
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_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||
proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
|
||||
view_model "github.com/caos/zitadel/internal/usergrant/repository/view/model"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UserGrant struct {
|
||||
handler
|
||||
eventstore eventstore.Eventstore
|
||||
iamEvents *iam_events.IamEventstore
|
||||
iamID string
|
||||
iamProjectID string
|
||||
}
|
||||
|
||||
const (
|
||||
userGrantTable = "authz.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(iam_es_model.IamAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate).
|
||||
LatestSequenceFilter(sequence), nil
|
||||
}
|
||||
|
||||
func (u *UserGrant) Process(event *models.Event) (err error) {
|
||||
switch event.AggregateType {
|
||||
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) processProject(event *models.Event) (err error) {
|
||||
switch event.Type {
|
||||
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,
|
||||
}
|
||||
} 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) 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)
|
||||
}
|
98
internal/authz/repository/eventsourcing/repository.go
Normal file
98
internal/authz/repository/eventsourcing/repository.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/handler"
|
||||
es_iam "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing"
|
||||
|
||||
"github.com/caos/zitadel/internal/auth_request/repository/cache"
|
||||
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/eventstore"
|
||||
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/spooler"
|
||||
authz_view "github.com/caos/zitadel/internal/authz/repository/eventsourcing/view"
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/config/types"
|
||||
es_int "github.com/caos/zitadel/internal/eventstore"
|
||||
es_spol "github.com/caos/zitadel/internal/eventstore/spooler"
|
||||
es_key "github.com/caos/zitadel/internal/key/repository/eventsourcing"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Eventstore es_int.Config
|
||||
AuthRequest cache.Config
|
||||
View types.SQL
|
||||
Spooler spooler.SpoolerConfig
|
||||
KeyConfig es_key.KeyConfig
|
||||
}
|
||||
|
||||
type EsRepository struct {
|
||||
spooler *es_spol.Spooler
|
||||
eventstore.UserGrantRepo
|
||||
eventstore.IamRepo
|
||||
eventstore.TokenVerifierRepo
|
||||
}
|
||||
|
||||
func Start(conf Config, authZ auth.Config, systemDefaults sd.SystemDefaults) (*EsRepository, error) {
|
||||
es, err := es_int.Start(conf.Eventstore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sqlClient, err := conf.View.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
idGenerator := id.SonyFlakeGenerator
|
||||
view, err := authz_view.StartView(sqlClient, idGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iam, err := es_iam.StartIam(es_iam.IamConfig{
|
||||
Eventstore: es,
|
||||
Cache: conf.Eventstore.Cache,
|
||||
}, systemDefaults)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
project, err := es_proj.StartProject(es_proj.ProjectConfig{
|
||||
Eventstore: es,
|
||||
Cache: conf.Eventstore.Cache,
|
||||
}, systemDefaults)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repos := handler.EventstoreRepos{IamEvents: iam}
|
||||
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, repos, systemDefaults)
|
||||
|
||||
return &EsRepository{
|
||||
spool,
|
||||
eventstore.UserGrantRepo{
|
||||
View: view,
|
||||
IamID: systemDefaults.IamID,
|
||||
Auth: authZ,
|
||||
IamEvents: iam,
|
||||
},
|
||||
eventstore.IamRepo{
|
||||
IamID: systemDefaults.IamID,
|
||||
IamEvents: iam,
|
||||
},
|
||||
eventstore.TokenVerifierRepo{
|
||||
//TODO: Add Token Verification Key
|
||||
IamID: systemDefaults.IamID,
|
||||
IamEvents: iam,
|
||||
ProjectEvents: project,
|
||||
View: view,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (repo *EsRepository) Health(ctx context.Context) error {
|
||||
if err := repo.UserGrantRepo.Health(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
19
internal/authz/repository/eventsourcing/spooler/lock.go
Normal file
19
internal/authz/repository/eventsourcing/spooler/lock.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package spooler
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
es_locker "github.com/caos/zitadel/internal/eventstore/locker"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
lockTable = "authz.locks"
|
||||
)
|
||||
|
||||
type locker struct {
|
||||
dbClient *sql.DB
|
||||
}
|
||||
|
||||
func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error {
|
||||
return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime)
|
||||
}
|
127
internal/authz/repository/eventsourcing/spooler/lock_test.go
Normal file
127
internal/authz/repository/eventsourcing/spooler/lock_test.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package spooler
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
)
|
||||
|
||||
type dbMock struct {
|
||||
db *sql.DB
|
||||
mock sqlmock.Sqlmock
|
||||
}
|
||||
|
||||
func mockDB(t *testing.T) *dbMock {
|
||||
mockDB := dbMock{}
|
||||
var err error
|
||||
mockDB.db, mockDB.mock, err = sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("error occured while creating stub db %v", err)
|
||||
}
|
||||
|
||||
mockDB.mock.MatchExpectationsInOrder(true)
|
||||
|
||||
return &mockDB
|
||||
}
|
||||
|
||||
func (db *dbMock) expectCommit() *dbMock {
|
||||
db.mock.ExpectCommit()
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *dbMock) expectRollback() *dbMock {
|
||||
db.mock.ExpectRollback()
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *dbMock) expectBegin() *dbMock {
|
||||
db.mock.ExpectBegin()
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *dbMock) expectSavepoint() *dbMock {
|
||||
db.mock.ExpectExec("SAVEPOINT").WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *dbMock) expectReleaseSavepoint() *dbMock {
|
||||
db.mock.ExpectExec("RELEASE SAVEPOINT").WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *dbMock) expectRenew(lockerID, view string, affectedRows int64) *dbMock {
|
||||
query := db.mock.
|
||||
ExpectExec(`INSERT INTO authz\.locks \(object_type, locker_id, locked_until\) VALUES \(\$1, \$2, now\(\)\+\$3\) ON CONFLICT \(object_type\) DO UPDATE SET locked_until = now\(\)\+\$4, locker_id = \$5 WHERE \(locks\.locked_until < now\(\) OR locks\.locker_id = \$6\) AND locks\.object_type = \$7`).
|
||||
WithArgs(view, lockerID, sqlmock.AnyArg(), sqlmock.AnyArg(), lockerID, lockerID, view).
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
|
||||
if affectedRows == 0 {
|
||||
query.WillReturnResult(sqlmock.NewResult(0, 0))
|
||||
} else {
|
||||
query.WillReturnResult(sqlmock.NewResult(1, affectedRows))
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func Test_locker_Renew(t *testing.T) {
|
||||
type fields struct {
|
||||
db *dbMock
|
||||
}
|
||||
type args struct {
|
||||
lockerID string
|
||||
viewModel string
|
||||
waitTime time.Duration
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "renew succeeded",
|
||||
fields: fields{
|
||||
db: mockDB(t).
|
||||
expectBegin().
|
||||
expectSavepoint().
|
||||
expectRenew("locker", "view", 1).
|
||||
expectReleaseSavepoint().
|
||||
expectCommit(),
|
||||
},
|
||||
args: args{lockerID: "locker", viewModel: "view", waitTime: 1 * time.Second},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "renew now rows updated",
|
||||
fields: fields{
|
||||
db: mockDB(t).
|
||||
expectBegin().
|
||||
expectSavepoint().
|
||||
expectRenew("locker", "view", 0).
|
||||
expectRollback(),
|
||||
},
|
||||
args: args{lockerID: "locker", viewModel: "view", waitTime: 1 * time.Second},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
l := &locker{
|
||||
dbClient: tt.fields.db.db,
|
||||
}
|
||||
if err := l.Renew(tt.args.lockerID, tt.args.viewModel, tt.args.waitTime); (err != nil) != tt.wantErr {
|
||||
t.Errorf("locker.Renew() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if err := tt.fields.db.mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("not all database expectations met: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
31
internal/authz/repository/eventsourcing/spooler/spooler.go
Normal file
31
internal/authz/repository/eventsourcing/spooler/spooler.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package spooler
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
|
||||
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/handler"
|
||||
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/view"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||
)
|
||||
|
||||
type SpoolerConfig struct {
|
||||
BulkLimit uint64
|
||||
FailureCountUntilSkip uint64
|
||||
ConcurrentTasks int
|
||||
Handlers handler.Configs
|
||||
}
|
||||
|
||||
func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sql *sql.DB, repos handler.EventstoreRepos, systemDefaults sd.SystemDefaults) *spooler.Spooler {
|
||||
spoolerConfig := spooler.Config{
|
||||
Eventstore: es,
|
||||
Locker: &locker{dbClient: sql},
|
||||
ConcurrentTasks: c.ConcurrentTasks,
|
||||
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, repos, systemDefaults),
|
||||
}
|
||||
spool := spoolerConfig.New()
|
||||
spool.Start()
|
||||
return spool
|
||||
}
|
60
internal/authz/repository/eventsourcing/view/application.go
Normal file
60
internal/authz/repository/eventsourcing/view/application.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
proj_model "github.com/caos/zitadel/internal/project/model"
|
||||
"github.com/caos/zitadel/internal/project/repository/view"
|
||||
"github.com/caos/zitadel/internal/project/repository/view/model"
|
||||
global_view "github.com/caos/zitadel/internal/view"
|
||||
)
|
||||
|
||||
const (
|
||||
applicationTable = "authz.applications"
|
||||
)
|
||||
|
||||
func (v *View) ApplicationByID(appID string) (*model.ApplicationView, error) {
|
||||
return view.ApplicationByID(v.Db, applicationTable, appID)
|
||||
}
|
||||
|
||||
func (v *View) ApplicationByOIDCClientID(clientID string) (*model.ApplicationView, error) {
|
||||
return view.ApplicationByOIDCClientID(v.Db, applicationTable, clientID)
|
||||
}
|
||||
|
||||
func (v *View) ApplicationByProjecIDAndAppName(projectID, appName string) (*model.ApplicationView, error) {
|
||||
return view.ApplicationByProjectIDAndAppName(v.Db, applicationTable, projectID, appName)
|
||||
}
|
||||
|
||||
func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) ([]*model.ApplicationView, int, error) {
|
||||
return view.SearchApplications(v.Db, applicationTable, request)
|
||||
}
|
||||
|
||||
func (v *View) PutApplication(project *model.ApplicationView) error {
|
||||
err := view.PutApplication(v.Db, applicationTable, project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedApplicationSequence(project.Sequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteApplication(appID string, eventSequence uint64) error {
|
||||
err := view.DeleteApplication(v.Db, applicationTable, appID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return v.ProcessedApplicationSequence(eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestApplicationSequence() (uint64, error) {
|
||||
return v.latestSequence(applicationTable)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedApplicationSequence(eventSequence uint64) error {
|
||||
return v.saveCurrentSequence(applicationTable, eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestApplicationFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
|
||||
return v.latestFailedEvent(applicationTable, sequence)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedApplicationFailedEvent(failedEvent *global_view.FailedEvent) error {
|
||||
return v.saveFailedEvent(failedEvent)
|
||||
}
|
17
internal/authz/repository/eventsourcing/view/error_event.go
Normal file
17
internal/authz/repository/eventsourcing/view/error_event.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/view"
|
||||
)
|
||||
|
||||
const (
|
||||
errTable = "authz.failed_event"
|
||||
)
|
||||
|
||||
func (v *View) saveFailedEvent(failedEvent *view.FailedEvent) error {
|
||||
return view.SaveFailedEvent(v.Db, errTable, failedEvent)
|
||||
}
|
||||
|
||||
func (v *View) latestFailedEvent(viewName string, sequence uint64) (*view.FailedEvent, error) {
|
||||
return view.LatestFailedEvent(v.Db, errTable, viewName, sequence)
|
||||
}
|
17
internal/authz/repository/eventsourcing/view/sequence.go
Normal file
17
internal/authz/repository/eventsourcing/view/sequence.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/view"
|
||||
)
|
||||
|
||||
const (
|
||||
sequencesTable = "authz.current_sequences"
|
||||
)
|
||||
|
||||
func (v *View) saveCurrentSequence(viewName string, sequence uint64) error {
|
||||
return view.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence)
|
||||
}
|
||||
|
||||
func (v *View) latestSequence(viewName string) (uint64, error) {
|
||||
return view.LatestSequence(v.Db, sequencesTable, viewName)
|
||||
}
|
59
internal/authz/repository/eventsourcing/view/token.go
Normal file
59
internal/authz/repository/eventsourcing/view/token.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/token/repository/view"
|
||||
"github.com/caos/zitadel/internal/token/repository/view/model"
|
||||
global_view "github.com/caos/zitadel/internal/view"
|
||||
)
|
||||
|
||||
const (
|
||||
tokenTable = "auth.tokens"
|
||||
)
|
||||
|
||||
func (v *View) TokenByID(tokenID string) (*model.Token, error) {
|
||||
return view.TokenByID(v.Db, tokenTable, tokenID)
|
||||
}
|
||||
|
||||
func (v *View) IsTokenValid(tokenID string) (bool, error) {
|
||||
return view.IsTokenValid(v.Db, tokenTable, tokenID)
|
||||
}
|
||||
|
||||
func (v *View) PutToken(token *model.Token) error {
|
||||
err := view.PutToken(v.Db, tokenTable, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedTokenSequence(token.Sequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteToken(tokenID string, eventSequence uint64) error {
|
||||
err := view.DeleteToken(v.Db, tokenTable, tokenID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return v.ProcessedTokenSequence(eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteSessionTokens(agentID, userID string, eventSequence uint64) error {
|
||||
err := view.DeleteTokens(v.Db, tokenTable, agentID, userID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return v.ProcessedTokenSequence(eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestTokenSequence() (uint64, error) {
|
||||
return v.latestSequence(tokenTable)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedTokenSequence(eventSequence uint64) error {
|
||||
return v.saveCurrentSequence(tokenTable, eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestTokenFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
|
||||
return v.latestFailedEvent(tokenTable, sequence)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedTokenFailedEvent(failedEvent *global_view.FailedEvent) error {
|
||||
return v.saveFailedEvent(failedEvent)
|
||||
}
|
64
internal/authz/repository/eventsourcing/view/user_grant.go
Normal file
64
internal/authz/repository/eventsourcing/view/user_grant.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||
"github.com/caos/zitadel/internal/usergrant/repository/view"
|
||||
"github.com/caos/zitadel/internal/usergrant/repository/view/model"
|
||||
global_view "github.com/caos/zitadel/internal/view"
|
||||
)
|
||||
|
||||
const (
|
||||
userGrantTable = "authz.user_grants"
|
||||
)
|
||||
|
||||
func (v *View) UserGrantByID(grantID string) (*model.UserGrantView, error) {
|
||||
return view.UserGrantByID(v.Db, userGrantTable, grantID)
|
||||
}
|
||||
|
||||
func (v *View) UserGrantByIDs(resourceOwnerID, projectID, userID string) (*model.UserGrantView, error) {
|
||||
return view.UserGrantByIDs(v.Db, userGrantTable, resourceOwnerID, projectID, userID)
|
||||
}
|
||||
|
||||
func (v *View) UserGrantsByUserID(userID string) ([]*model.UserGrantView, error) {
|
||||
return view.UserGrantsByUserID(v.Db, userGrantTable, userID)
|
||||
}
|
||||
|
||||
func (v *View) UserGrantsByProjectID(projectID string) ([]*model.UserGrantView, error) {
|
||||
return view.UserGrantsByProjectID(v.Db, userGrantTable, projectID)
|
||||
}
|
||||
|
||||
func (v *View) SearchUserGrants(request *grant_model.UserGrantSearchRequest) ([]*model.UserGrantView, int, error) {
|
||||
return view.SearchUserGrants(v.Db, userGrantTable, request)
|
||||
}
|
||||
|
||||
func (v *View) PutUserGrant(grant *model.UserGrantView, sequence uint64) error {
|
||||
err := view.PutUserGrant(v.Db, userGrantTable, grant)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedUserGrantSequence(sequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteUserGrant(grantID string, eventSequence uint64) error {
|
||||
err := view.DeleteUserGrant(v.Db, userGrantTable, grantID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return v.ProcessedUserGrantSequence(eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestUserGrantSequence() (uint64, error) {
|
||||
return v.latestSequence(userGrantTable)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedUserGrantSequence(eventSequence uint64) error {
|
||||
return v.saveCurrentSequence(userGrantTable, eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestUserGrantFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
|
||||
return v.latestFailedEvent(userGrantTable, sequence)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedUserGrantFailedEvent(failedEvent *global_view.FailedEvent) error {
|
||||
return v.saveFailedEvent(failedEvent)
|
||||
}
|
28
internal/authz/repository/eventsourcing/view/view.go
Normal file
28
internal/authz/repository/eventsourcing/view/view.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
type View struct {
|
||||
Db *gorm.DB
|
||||
idGenerator id.Generator
|
||||
}
|
||||
|
||||
func StartView(sqlClient *sql.DB, idGenerator id.Generator) (*View, error) {
|
||||
gorm, err := gorm.Open("postgres", sqlClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &View{
|
||||
Db: gorm,
|
||||
idGenerator: idGenerator,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *View) Health() (err error) {
|
||||
return v.Db.DB().Ping()
|
||||
}
|
Reference in New Issue
Block a user