From 973ba6b4016f1e5f6686fec05303f66154208f21 Mon Sep 17 00:00:00 2001 From: yokowu <18836617@qq.com> Date: Wed, 10 Jun 2026 19:12:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=9B=A2=E9=98=9F=20?= =?UTF-8?q?OIDC=20=E4=BC=81=E4=B8=9A=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/biz/team/handler/http/v1/oidc.go | 90 ++ .../team/handler/http/v1/oidc_route_test.go | 53 + backend/biz/team/register.go | 5 + backend/biz/team/repo/oidc.go | 208 +++ backend/biz/team/repo/oidc_test.go | 176 +++ backend/biz/team/usecase/oidc.go | 441 ++++++ backend/biz/team/usecase/oidc_test.go | 140 ++ backend/biz/user/handler/v1/auth.go | 94 ++ .../biz/user/handler/v1/members_route_test.go | 34 + backend/biz/user/repo/user.go | 26 + backend/biz/user/repo/user_test.go | 51 + backend/consts/user.go | 1 + backend/db/client.go | 170 ++- backend/db/ent.go | 2 + backend/db/hook/hook.go | 12 + backend/db/intercept/intercept.go | 30 + backend/db/migrate/schema.go | 42 + backend/db/mutation.go | 1023 ++++++++++++++ backend/db/page.go | 14 + backend/db/predicate/predicate.go | 3 + backend/db/runtime/runtime.go | 41 + backend/db/teamoidcconfig.go | 259 ++++ backend/db/teamoidcconfig/teamoidcconfig.go | 184 +++ backend/db/teamoidcconfig/where.go | 695 +++++++++ backend/db/teamoidcconfig_create.go | 1246 +++++++++++++++++ backend/db/teamoidcconfig_delete.go | 88 ++ backend/db/teamoidcconfig_query.go | 657 +++++++++ backend/db/teamoidcconfig_update.go | 756 ++++++++++ backend/db/tx.go | 3 + backend/docs/swagger.json | 402 +++++- backend/domain/team_oidc.go | 101 ++ backend/ent/schema/teamoidcconfig.go | 54 + backend/errcode/errcode.go | 9 + backend/errcode/locale.en.toml | 29 +- backend/errcode/locale.zh.toml | 27 + backend/go.mod | 2 + backend/go.sum | 4 + .../000014_team_oidc_config.down.sql | 1 + .../migration/000014_team_oidc_config.up.sql | 17 + ..._user_identities_identity_id_text.down.sql | 2 + ...er_user_identities_identity_id_text.up.sql | 2 + backend/migration/migration_test.go | 37 + backend/pkg/oidc/client.go | 187 +++ backend/pkg/oidc/client_test.go | 85 ++ frontend/src/App.tsx | 4 + frontend/src/api/Api.ts | 187 +++ frontend/src/components/manager/nav-teams.tsx | 13 +- frontend/src/pages/console/manager/oidc.tsx | 183 +++ frontend/src/pages/login.tsx | 38 + frontend/src/pages/team-oidc-login.tsx | 72 + 50 files changed, 7990 insertions(+), 10 deletions(-) create mode 100644 backend/biz/team/handler/http/v1/oidc.go create mode 100644 backend/biz/team/handler/http/v1/oidc_route_test.go create mode 100644 backend/biz/team/repo/oidc.go create mode 100644 backend/biz/team/repo/oidc_test.go create mode 100644 backend/biz/team/usecase/oidc.go create mode 100644 backend/biz/team/usecase/oidc_test.go create mode 100644 backend/biz/user/repo/user_test.go create mode 100644 backend/db/teamoidcconfig.go create mode 100644 backend/db/teamoidcconfig/teamoidcconfig.go create mode 100644 backend/db/teamoidcconfig/where.go create mode 100644 backend/db/teamoidcconfig_create.go create mode 100644 backend/db/teamoidcconfig_delete.go create mode 100644 backend/db/teamoidcconfig_query.go create mode 100644 backend/db/teamoidcconfig_update.go create mode 100644 backend/domain/team_oidc.go create mode 100644 backend/ent/schema/teamoidcconfig.go create mode 100644 backend/migration/000014_team_oidc_config.down.sql create mode 100644 backend/migration/000014_team_oidc_config.up.sql create mode 100644 backend/migration/000015_alter_user_identities_identity_id_text.down.sql create mode 100644 backend/migration/000015_alter_user_identities_identity_id_text.up.sql create mode 100644 backend/migration/migration_test.go create mode 100644 backend/pkg/oidc/client.go create mode 100644 backend/pkg/oidc/client_test.go create mode 100644 frontend/src/pages/console/manager/oidc.tsx create mode 100644 frontend/src/pages/team-oidc-login.tsx diff --git a/backend/biz/team/handler/http/v1/oidc.go b/backend/biz/team/handler/http/v1/oidc.go new file mode 100644 index 00000000..8be96164 --- /dev/null +++ b/backend/biz/team/handler/http/v1/oidc.go @@ -0,0 +1,90 @@ +package v1 + +import ( + "log/slog" + + "github.com/GoYoko/web" + "github.com/samber/do" + + "github.com/chaitin/MonkeyCode/backend/domain" + "github.com/chaitin/MonkeyCode/backend/middleware" +) + +type TeamOIDCHandler struct { + logger *slog.Logger + usecase domain.TeamOIDCUsecase +} + +func NewTeamOIDCHandler(i *do.Injector) (*TeamOIDCHandler, error) { + w := do.MustInvoke[*web.Web](i) + auth := do.MustInvoke[*middleware.AuthMiddleware](i) + logger := do.MustInvoke[*slog.Logger](i) + + h := &TeamOIDCHandler{ + logger: logger.With("module", "handler.team_oidc"), + usecase: do.MustInvoke[domain.TeamOIDCUsecase](i), + } + + g := w.Group("/api/v1/teams/oidc") + g.GET("", web.BaseHandler(h.Get), auth.TeamAuth()) + g.PUT("", web.BindHandler(h.Save), auth.TeamAuth()) + g.POST("/test", web.BindHandler(h.Test), auth.TeamAuth()) + + return h, nil +} + +// Get 获取团队 OIDC 配置 +// +// @Summary 获取团队 OIDC 配置 +// @Description 获取当前团队企业登录 OIDC 配置 +// @Tags 【Team 管理员】企业登录 +// @Accept json +// @Produce json +// @Security MonkeyCodeAITeamAuth +// @Success 200 {object} web.Resp{data=domain.TeamOIDCConfigResp} +// @Router /api/v1/teams/oidc [get] +func (h *TeamOIDCHandler) Get(c *web.Context) error { + resp, err := h.usecase.GetConfig(c.Request().Context(), middleware.GetTeamUser(c)) + if err != nil { + return err + } + return c.Success(resp) +} + +// Save 保存团队 OIDC 配置 +// +// @Summary 保存团队 OIDC 配置 +// @Description 新增或更新当前团队企业登录 OIDC 配置 +// @Tags 【Team 管理员】企业登录 +// @Accept json +// @Produce json +// @Security MonkeyCodeAITeamAuth +// @Param req body domain.SaveTeamOIDCConfigReq true "请求参数" +// @Success 200 {object} web.Resp{data=domain.TeamOIDCConfigResp} +// @Router /api/v1/teams/oidc [put] +func (h *TeamOIDCHandler) Save(c *web.Context, req domain.SaveTeamOIDCConfigReq) error { + resp, err := h.usecase.SaveConfig(c.Request().Context(), middleware.GetTeamUser(c), &req) + if err != nil { + return err + } + return c.Success(resp) +} + +// Test 测试团队 OIDC 配置 +// +// @Summary 测试团队 OIDC 配置 +// @Description 拉取 OIDC discovery 文档验证配置可用性 +// @Tags 【Team 管理员】企业登录 +// @Accept json +// @Produce json +// @Security MonkeyCodeAITeamAuth +// @Param req body domain.SaveTeamOIDCConfigReq true "请求参数" +// @Success 200 {object} web.Resp{data=domain.TeamOIDCTestResp} +// @Router /api/v1/teams/oidc/test [post] +func (h *TeamOIDCHandler) Test(c *web.Context, req domain.SaveTeamOIDCConfigReq) error { + resp, err := h.usecase.TestConfig(c.Request().Context(), middleware.GetTeamUser(c), &req) + if err != nil { + return err + } + return c.Success(resp) +} diff --git a/backend/biz/team/handler/http/v1/oidc_route_test.go b/backend/biz/team/handler/http/v1/oidc_route_test.go new file mode 100644 index 00000000..24dbdaf9 --- /dev/null +++ b/backend/biz/team/handler/http/v1/oidc_route_test.go @@ -0,0 +1,53 @@ +package v1 + +import ( + "context" + "io" + "log/slog" + "testing" + + "github.com/GoYoko/web" + "github.com/samber/do" + + "github.com/chaitin/MonkeyCode/backend/domain" + "github.com/chaitin/MonkeyCode/backend/middleware" +) + +func TestNewTeamOIDCHandlerRegistersRoutes(t *testing.T) { + injector := do.New() + w := web.New() + do.ProvideValue(injector, w) + do.ProvideValue(injector, slog.New(slog.NewTextHandler(io.Discard, nil))) + do.ProvideValue[domain.TeamOIDCUsecase](injector, &teamOIDCUsecaseStub{}) + do.ProvideValue(injector, &middleware.AuthMiddleware{}) + do.ProvideValue(injector, middleware.NewTargetActiveMiddleware(slog.New(slog.NewTextHandler(io.Discard, nil)), nil)) + + if _, err := NewTeamOIDCHandler(injector); err != nil { + t.Fatal(err) + } + + want := map[string]bool{ + "GET /api/v1/teams/oidc": false, + "PUT /api/v1/teams/oidc": false, + "POST /api/v1/teams/oidc/test": false, + } + for _, route := range w.Routes() { + key := route.Method + " " + route.Path + if _, ok := want[key]; ok { + want[key] = true + } + } + for key, ok := range want { + if !ok { + t.Fatalf("route %s is not registered", key) + } + } +} + +type teamOIDCUsecaseStub struct { + domain.TeamOIDCUsecase +} + +func (s *teamOIDCUsecaseStub) GetConfig(ctx context.Context, teamUser *domain.TeamUser) (*domain.TeamOIDCConfigResp, error) { + return &domain.TeamOIDCConfigResp{}, nil +} diff --git a/backend/biz/team/register.go b/backend/biz/team/register.go index 875133da..08042b19 100644 --- a/backend/biz/team/register.go +++ b/backend/biz/team/register.go @@ -30,6 +30,10 @@ func ProvideTeam(i *do.Injector) { do.Provide(i, repo.NewTeamPolicyRepo) do.Provide(i, usecase.NewTeamPolicyUsecase) do.Provide(i, v1.NewTeamPolicyHandler) + do.Provide(i, repo.NewTeamOIDCRepo) + do.Provide(i, usecase.NewTeamOIDCUsecase) + do.Provide(i, usecase.NewTeamOIDCLoginUsecase) + do.Provide(i, v1.NewTeamOIDCHandler) do.Provide(i, v1.NewTeamGroupUserHandler) } @@ -45,4 +49,5 @@ func InvokeTeam(i *do.Injector) { do.MustInvoke[*v1.TeamImageHandler](i) do.MustInvoke[*v1.TeamHostHandler](i) do.MustInvoke[*v1.TeamPolicyHandler](i) + do.MustInvoke[*v1.TeamOIDCHandler](i) } diff --git a/backend/biz/team/repo/oidc.go b/backend/biz/team/repo/oidc.go new file mode 100644 index 00000000..f7909b52 --- /dev/null +++ b/backend/biz/team/repo/oidc.go @@ -0,0 +1,208 @@ +package repo + +import ( + "context" + "strings" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" + "github.com/samber/do" + + "github.com/chaitin/MonkeyCode/backend/consts" + "github.com/chaitin/MonkeyCode/backend/db" + "github.com/chaitin/MonkeyCode/backend/db/team" + "github.com/chaitin/MonkeyCode/backend/db/teamgroupmember" + "github.com/chaitin/MonkeyCode/backend/db/teammember" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" + "github.com/chaitin/MonkeyCode/backend/db/user" + "github.com/chaitin/MonkeyCode/backend/db/useridentity" + "github.com/chaitin/MonkeyCode/backend/domain" + "github.com/chaitin/MonkeyCode/backend/errcode" + "github.com/chaitin/MonkeyCode/backend/pkg/entx" + "github.com/chaitin/MonkeyCode/backend/pkg/oidc" +) + +type TeamOIDCRepo struct { + db *db.Client +} + +func NewTeamOIDCRepo(i *do.Injector) (domain.TeamOIDCRepo, error) { + return &TeamOIDCRepo{db: do.MustInvoke[*db.Client](i)}, nil +} + +func (r *TeamOIDCRepo) GetConfig(ctx context.Context, teamID uuid.UUID) (*db.TeamOIDCConfig, error) { + return r.db.TeamOIDCConfig.Query().Where(teamoidcconfig.TeamIDEQ(teamID)).First(ctx) +} + +func (r *TeamOIDCRepo) GetDefaultEnabledConfig(ctx context.Context) (*db.TeamOIDCConfig, error) { + return r.db.TeamOIDCConfig.Query(). + Where(teamoidcconfig.EnabledEQ(true)). + WithTeam(). + Modify(func(s *sql.Selector) { + t := sql.Table(team.Table) + s.Join(t).On(s.C(teamoidcconfig.FieldTeamID), t.C(team.FieldID)) + s.OrderBy(t.C(team.FieldCreatedAt), s.C(teamoidcconfig.FieldCreatedAt)) + }). + First(ctx) +} + +func (r *TeamOIDCRepo) UpsertConfig(ctx context.Context, teamID uuid.UUID, req *domain.SaveTeamOIDCConfigReq) (*db.TeamOIDCConfig, error) { + issuer := oidc.CleanIssuer(req.Issuer) + scopes := strings.TrimSpace(req.Scopes) + if scopes == "" { + scopes = "openid email profile" + } + displayName := strings.TrimSpace(req.DisplayName) + if displayName == "" { + displayName = "企业登录" + } + create := r.db.TeamOIDCConfig.Create(). + SetID(uuid.New()). + SetTeamID(teamID). + SetEnabled(req.Enabled). + SetDisplayName(displayName). + SetIssuer(issuer). + SetClientID(strings.TrimSpace(req.ClientID)). + SetScopes(scopes). + SetEmailDomain(strings.TrimSpace(strings.ToLower(req.EmailDomain))). + SetAutoCreateMember(req.AutoCreateMember). + SetAllowPasswordLogin(req.AllowPasswordLogin) + if req.ClientSecret != "" { + create.SetClientSecretCiphertext(req.ClientSecret) + } + id, err := create. + OnConflictColumns(teamoidcconfig.FieldTeamID). + Update(func(upsert *db.TeamOIDCConfigUpsert) { + upsert.SetEnabled(req.Enabled) + upsert.SetDisplayName(displayName) + upsert.SetIssuer(issuer) + upsert.SetClientID(strings.TrimSpace(req.ClientID)) + upsert.SetScopes(scopes) + upsert.SetEmailDomain(strings.TrimSpace(strings.ToLower(req.EmailDomain))) + upsert.SetAutoCreateMember(req.AutoCreateMember) + upsert.SetAllowPasswordLogin(req.AllowPasswordLogin) + if req.ClientSecret != "" { + upsert.SetClientSecretCiphertext(req.ClientSecret) + } + }). + ID(ctx) + if err != nil { + return nil, err + } + return r.db.TeamOIDCConfig.Get(ctx, id) +} + +func (r *TeamOIDCRepo) FindUserByOIDCIdentity(ctx context.Context, identityID string) (*db.User, error) { + identity, err := r.db.UserIdentity.Query(). + Where(useridentity.PlatformEQ(consts.UserPlatformOIDC), useridentity.IdentityIDEQ(identityID)). + WithUser(). + First(ctx) + if err != nil { + return nil, err + } + return identity.Edges.User, nil +} + +func (r *TeamOIDCRepo) FindTeamMemberByEmail(ctx context.Context, teamID uuid.UUID, email string) (*db.TeamMember, error) { + return r.db.TeamMember.Query(). + Where( + teammember.TeamIDEQ(teamID), + teammember.HasUserWith(user.EmailEQ(normalizeEmail(email))), + ). + WithUser(). + First(ctx) +} + +func (r *TeamOIDCRepo) BindOIDCIdentity(ctx context.Context, userID uuid.UUID, external *domain.OIDCExternalUser) error { + name := external.Name + if name == "" { + name = external.Username + } + if name == "" { + name = external.Email + } + return r.db.UserIdentity.Create(). + SetID(uuid.New()). + SetUserID(userID). + SetPlatform(consts.UserPlatformOIDC). + SetIdentityID(oidc.IdentityID(external.Issuer, external.Subject)). + SetUsername(name). + SetEmail(external.Email). + SetAvatarURL(external.AvatarURL). + OnConflict( + sql.ConflictColumns(useridentity.FieldPlatform, useridentity.FieldIdentityID), + sql.ResolveWithIgnore(), + ). + Exec(ctx) +} + +func (r *TeamOIDCRepo) AutoCreateMember(ctx context.Context, teamID uuid.UUID, external *domain.OIDCExternalUser) (*db.User, error) { + var created *db.User + err := entx.WithTx2(ctx, r.db, func(tx *db.Tx) error { + tm, err := tx.Team.Get(ctx, teamID) + if err != nil { + return err + } + count, err := tx.TeamMember.Query().Where(teammember.TeamIDEQ(teamID)).Count(ctx) + if err != nil { + return err + } + if count >= tm.MemberLimit { + return errcode.ErrTeamMemberLimitExceeded + } + email := normalizeEmail(external.Email) + u, err := tx.User.Query().Where(user.EmailEQ(email), user.RoleEQ(consts.UserRoleSubAccount)).First(ctx) + if err != nil { + if !db.IsNotFound(err) { + return err + } + name := external.Name + if name == "" { + name = external.Username + } + if name == "" { + name = email + } + u, err = tx.User.Create(). + SetID(uuid.New()). + SetName(name). + SetEmail(email). + SetAvatarURL(external.AvatarURL). + SetRole(consts.UserRoleSubAccount). + SetStatus(consts.UserStatusActive). + Save(ctx) + if err != nil { + return err + } + } + exists, err := tx.TeamMember.Query().Where(teammember.TeamIDEQ(teamID), teammember.UserIDEQ(u.ID)).Exist(ctx) + if err != nil { + return err + } + if !exists { + if _, err := tx.TeamMember.Create().SetID(uuid.New()).SetTeamID(teamID).SetUserID(u.ID).SetRole(consts.TeamMemberRoleUser).Save(ctx); err != nil { + return err + } + } + group, err := ensureDefaultTeamGroupTx(ctx, tx, teamID) + if err != nil { + return err + } + exists, err = tx.TeamGroupMember.Query().Where(teamgroupmember.GroupIDEQ(group.ID), teamgroupmember.UserIDEQ(u.ID)).Exist(ctx) + if err != nil { + return err + } + if !exists { + if err := tx.TeamGroupMember.Create().SetID(uuid.New()).SetGroupID(group.ID).SetUserID(u.ID).Exec(ctx); err != nil { + return err + } + } + created = u + return nil + }) + return created, err +} + +func normalizeEmail(email string) string { + return strings.ToLower(strings.TrimSpace(email)) +} diff --git a/backend/biz/team/repo/oidc_test.go b/backend/biz/team/repo/oidc_test.go new file mode 100644 index 00000000..ee983844 --- /dev/null +++ b/backend/biz/team/repo/oidc_test.go @@ -0,0 +1,176 @@ +package repo + +import ( + "context" + "testing" + "time" + + "github.com/google/uuid" + + "github.com/chaitin/MonkeyCode/backend/db/enttest" + "github.com/chaitin/MonkeyCode/backend/domain" +) + +func TestTeamOIDCConfigSchemaPersistsDefaults(t *testing.T) { + ctx := context.Background() + client := enttest.Open(t, "sqlite3", "file:team_oidc_config?mode=memory&cache=shared&_fk=1") + defer client.Close() + + teamID := uuid.New() + _, err := client.Team.Create(). + SetID(teamID). + SetName("研发团队"). + SetMemberLimit(10). + Save(ctx) + if err != nil { + t.Fatal(err) + } + + cfg, err := client.TeamOIDCConfig.Create(). + SetID(uuid.New()). + SetTeamID(teamID). + SetEnabled(true). + SetDisplayName("公司账号登录"). + SetIssuer("https://id.example.com"). + SetClientID("monkeycode"). + SetClientSecretCiphertext("secret"). + Save(ctx) + if err != nil { + t.Fatal(err) + } + + if cfg.Scopes != "openid email profile" { + t.Fatalf("scopes = %q, want default openid email profile", cfg.Scopes) + } + if cfg.AutoCreateMember { + t.Fatal("auto_create_member default should be false") + } + if !cfg.AllowPasswordLogin { + t.Fatal("allow_password_login default should be true") + } +} + +func TestTeamOIDCRepoAutoCreateMemberCreatesDefaultGroup(t *testing.T) { + ctx := context.Background() + client := enttest.Open(t, "sqlite3", "file:team_oidc_repo?mode=memory&cache=shared&_fk=1") + defer client.Close() + + teamID := uuid.New() + _, err := client.Team.Create().SetID(teamID).SetName("研发团队").SetMemberLimit(2).Save(ctx) + if err != nil { + t.Fatal(err) + } + + r := &TeamOIDCRepo{db: client} + user, err := r.AutoCreateMember(ctx, teamID, &domain.OIDCExternalUser{ + Issuer: "https://id.example.com", + Subject: "sub-1", + Email: "new@example.com", + EmailVerified: true, + Name: "新成员", + }) + if err != nil { + t.Fatal(err) + } + if user.Email != "new@example.com" { + t.Fatalf("email = %q", user.Email) + } + + memberCount, err := client.TeamMember.Query().Count(ctx) + if err != nil { + t.Fatal(err) + } + if memberCount != 1 { + t.Fatalf("member count = %d, want 1", memberCount) + } + groupMemberCount, err := client.TeamGroupMember.Query().Count(ctx) + if err != nil { + t.Fatal(err) + } + if groupMemberCount != 1 { + t.Fatalf("group member count = %d, want 1", groupMemberCount) + } +} + +func TestTeamOIDCRepoAutoCreateMemberHonorsLimit(t *testing.T) { + ctx := context.Background() + client := enttest.Open(t, "sqlite3", "file:team_oidc_repo_limit?mode=memory&cache=shared&_fk=1") + defer client.Close() + + teamID := uuid.New() + _, err := client.Team.Create().SetID(teamID).SetName("研发团队").SetMemberLimit(0).Save(ctx) + if err != nil { + t.Fatal(err) + } + r := &TeamOIDCRepo{db: client} + _, err = r.AutoCreateMember(ctx, teamID, &domain.OIDCExternalUser{Email: "new@example.com", Name: "新成员"}) + if err == nil { + t.Fatal("expected member limit error") + } +} + +func TestTeamOIDCRepoGetDefaultEnabledConfigReturnsEarliestTeam(t *testing.T) { + ctx := context.Background() + client := enttest.Open(t, "sqlite3", "file:team_oidc_default?mode=memory&cache=shared&_fk=1") + defer client.Close() + + r := &TeamOIDCRepo{db: client} + early := time.Date(2026, 6, 9, 10, 0, 0, 0, time.UTC) + late := time.Date(2026, 6, 10, 10, 0, 0, 0, time.UTC) + + disabledTeamID := uuid.New() + enabledLateTeamID := uuid.New() + enabledEarlyTeamID := uuid.New() + for _, tc := range []struct { + id uuid.UUID + name string + createdAt time.Time + }{ + {disabledTeamID, "未启用团队", early.Add(-time.Hour)}, + {enabledLateTeamID, "后创建团队", late}, + {enabledEarlyTeamID, "先创建团队", early}, + } { + _, err := client.Team.Create(). + SetID(tc.id). + SetName(tc.name). + SetMemberLimit(10). + SetCreatedAt(tc.createdAt). + Save(ctx) + if err != nil { + t.Fatal(err) + } + } + + for _, tc := range []struct { + teamID uuid.UUID + enabled bool + displayName string + }{ + {disabledTeamID, false, "未启用企业登录"}, + {enabledLateTeamID, true, "后创建企业登录"}, + {enabledEarlyTeamID, true, "默认企业登录"}, + } { + _, err := client.TeamOIDCConfig.Create(). + SetID(uuid.New()). + SetTeamID(tc.teamID). + SetEnabled(tc.enabled). + SetDisplayName(tc.displayName). + SetIssuer("https://id.example.com/"). + SetClientID("monkeycode"). + Save(ctx) + if err != nil { + t.Fatal(err) + } + } + + cfg, err := r.GetDefaultEnabledConfig(ctx) + if err != nil { + t.Fatal(err) + } + if cfg.TeamID != enabledEarlyTeamID { + t.Fatalf("team id = %s, want %s", cfg.TeamID, enabledEarlyTeamID) + } + if cfg.DisplayName != "默认企业登录" { + t.Fatalf("display name = %q", cfg.DisplayName) + } +} diff --git a/backend/biz/team/usecase/oidc.go b/backend/biz/team/usecase/oidc.go new file mode 100644 index 00000000..2ac2f272 --- /dev/null +++ b/backend/biz/team/usecase/oidc.go @@ -0,0 +1,441 @@ +package usecase + +import ( + "context" + "crypto/rand" + "encoding/base64" + "fmt" + "log/slog" + "strings" + "time" + + "github.com/google/uuid" + "github.com/redis/go-redis/v9" + "github.com/samber/do" + "golang.org/x/oauth2" + + "github.com/chaitin/MonkeyCode/backend/config" + "github.com/chaitin/MonkeyCode/backend/db" + "github.com/chaitin/MonkeyCode/backend/domain" + "github.com/chaitin/MonkeyCode/backend/errcode" + "github.com/chaitin/MonkeyCode/backend/pkg/cvt" + oidcpkg "github.com/chaitin/MonkeyCode/backend/pkg/oidc" +) + +const oidcStatePrefix = "team_oidc_state:" +const oidcDebugValueMaxLen = 256 + +type TeamOIDCUsecase struct { + repo domain.TeamOIDCRepo + cfg *config.Config + redis *redis.Client + oidc *oidcpkg.Client + logger *slog.Logger +} + +func NewTeamOIDCUsecase(i *do.Injector) (domain.TeamOIDCUsecase, error) { + return newTeamOIDCUsecase(i) +} + +func NewTeamOIDCLoginUsecase(i *do.Injector) (domain.TeamOIDCLoginUsecase, error) { + return newTeamOIDCUsecase(i) +} + +func newTeamOIDCUsecase(i *do.Injector) (*TeamOIDCUsecase, error) { + return &TeamOIDCUsecase{ + repo: do.MustInvoke[domain.TeamOIDCRepo](i), + cfg: do.MustInvoke[*config.Config](i), + redis: do.MustInvoke[*redis.Client](i), + oidc: oidcpkg.NewClient(nil), + logger: do.MustInvoke[*slog.Logger](i).With("module", "usecase.team_oidc"), + }, nil +} + +func (u *TeamOIDCUsecase) GetConfig(ctx context.Context, teamUser *domain.TeamUser) (*domain.TeamOIDCConfigResp, error) { + cfg, err := u.repo.GetConfig(ctx, teamUser.GetTeamID()) + if err != nil { + if db.IsNotFound(err) { + return &domain.TeamOIDCConfigResp{Config: u.emptyConfig(teamUser.GetTeamID())}, nil + } + return nil, err + } + return &domain.TeamOIDCConfigResp{Config: u.configResp(cfg)}, nil +} + +func (u *TeamOIDCUsecase) SaveConfig(ctx context.Context, teamUser *domain.TeamUser, req *domain.SaveTeamOIDCConfigReq) (*domain.TeamOIDCConfigResp, error) { + req.Issuer = oidcpkg.CleanIssuer(req.Issuer) + if req.Scopes == "" { + req.Scopes = "openid email profile" + } + if req.DisplayName == "" { + req.DisplayName = "企业登录" + } + cfg, err := u.repo.UpsertConfig(ctx, teamUser.GetTeamID(), req) + if err != nil { + return nil, err + } + return &domain.TeamOIDCConfigResp{Config: u.configResp(cfg)}, nil +} + +func (u *TeamOIDCUsecase) TestConfig(ctx context.Context, _ *domain.TeamUser, req *domain.SaveTeamOIDCConfigReq) (*domain.TeamOIDCTestResp, error) { + doc, err := u.oidc.Discover(ctx, req.Issuer) + if err != nil { + return nil, errcode.ErrOIDCConfigInvalid.Wrap(err) + } + return &domain.TeamOIDCTestResp{Success: true, Issuer: doc.Issuer, Message: "ok"}, nil +} + +func (u *TeamOIDCUsecase) StartLogin(ctx context.Context, teamID uuid.UUID) (string, error) { + cfg, err := u.repo.GetConfig(ctx, teamID) + if err != nil { + return "", errcode.ErrOIDCDisabled.Wrap(err) + } + if !cfg.Enabled { + return "", errcode.ErrOIDCDisabled + } + doc, err := u.oidc.Discover(ctx, cfg.Issuer) + if err != nil { + return "", errcode.ErrOIDCConfigInvalid.Wrap(err) + } + state := randomURLToken() + nonce := randomURLToken() + value := fmt.Sprintf("%s|%s", teamID.String(), nonce) + if err := u.redis.Set(ctx, oidcStatePrefix+state, value, 5*time.Minute).Err(); err != nil { + return "", err + } + oauthCfg := oidcpkg.OAuthConfig(doc, oidcpkg.Config{ + Issuer: cfg.Issuer, + ClientID: cfg.ClientID, + ClientSecret: cfg.ClientSecretCiphertext, + RedirectURL: u.redirectURI(), + Scopes: oidcpkg.SplitScopes(cfg.Scopes), + }) + return oauthCfg.AuthCodeURL(state, oauth2.SetAuthURLParam("nonce", nonce)), nil +} + +func (u *TeamOIDCUsecase) HandleCallback(ctx context.Context, req *domain.TeamOIDCCallbackReq) (*domain.User, error) { + u.logger.DebugContext(ctx, "oidc callback: received", + "state_present", req.State != "", + "state_len", len(req.State), + "code_present", req.Code != "", + "code_len", len(req.Code), + ) + value, err := u.redis.GetDel(ctx, oidcStatePrefix+req.State).Result() + if err != nil { + return nil, errcode.ErrOIDCStateInvalid.Wrap(err) + } + parts := strings.SplitN(value, "|", 2) + if len(parts) != 2 { + return nil, errcode.ErrOIDCStateInvalid + } + teamID, err := uuid.Parse(parts[0]) + if err != nil { + return nil, errcode.ErrOIDCStateInvalid.Wrap(err) + } + nonce := parts[1] + u.logger.DebugContext(ctx, "oidc callback: state validated", + "team_id", teamID, + "nonce_present", nonce != "", + "nonce_len", len(nonce), + ) + + cfg, err := u.repo.GetConfig(ctx, teamID) + if err != nil || !cfg.Enabled { + u.logger.DebugContext(ctx, "oidc callback: config unavailable", + "team_id", teamID, + "error", err, + ) + return nil, errcode.ErrOIDCDisabled.Wrap(err) + } + u.logger.DebugContext(ctx, "oidc callback: config loaded", + "team_id", teamID, + "issuer", cfg.Issuer, + "client_id", cfg.ClientID, + "scopes", cfg.Scopes, + "email_domain", cfg.EmailDomain, + "auto_create_member", cfg.AutoCreateMember, + "allow_password_login", cfg.AllowPasswordLogin, + ) + doc, err := u.oidc.Discover(ctx, cfg.Issuer) + if err != nil { + return nil, errcode.ErrOIDCConfigInvalid.Wrap(err) + } + u.logger.DebugContext(ctx, "oidc callback: discovery loaded", + "team_id", teamID, + "issuer", doc.Issuer, + "authorization_endpoint", doc.AuthorizationEndpoint, + "token_endpoint", doc.TokenEndpoint, + "userinfo_endpoint", doc.UserinfoEndpoint, + ) + oauthCfg := oidcpkg.OAuthConfig(doc, oidcpkg.Config{ + Issuer: cfg.Issuer, + ClientID: cfg.ClientID, + ClientSecret: cfg.ClientSecretCiphertext, + RedirectURL: u.redirectURI(), + Scopes: oidcpkg.SplitScopes(cfg.Scopes), + }) + token, err := oauthCfg.Exchange(ctx, req.Code) + if err != nil { + return nil, errcode.ErrOIDCTokenInvalid.Wrap(err) + } + rawIDToken, ok := token.Extra("id_token").(string) + u.logger.DebugContext(ctx, "oidc callback: token exchanged", + "team_id", teamID, + "token_type", token.TokenType, + "expiry", token.Expiry, + "has_access_token", token.AccessToken != "", + "has_refresh_token", token.RefreshToken != "", + "has_id_token", ok && rawIDToken != "", + ) + if !ok || rawIDToken == "" { + return nil, errcode.ErrOIDCTokenInvalid + } + external, err := u.oidc.VerifyIDToken(ctx, doc, oidcpkg.Config{ClientID: cfg.ClientID}, rawIDToken, nonce) + if err != nil { + return nil, errcode.ErrOIDCTokenInvalid.Wrap(err) + } + u.logger.DebugContext(ctx, "oidc callback: id token verified", + append([]any{"team_id", teamID, "source", "id_token"}, oidcExternalUserLogAttrs(external)...)..., + ) + if external.Email == "" || external.Name == "" || external.AvatarURL == "" { + u.logger.DebugContext(ctx, "oidc callback: userinfo required", + "team_id", teamID, + "missing_email", external.Email == "", + "missing_name", external.Name == "", + "missing_avatar_url", external.AvatarURL == "", + ) + enriched, infoErr := u.oidc.UserInfo(ctx, doc, oauthCfg.TokenSource(ctx, token), external) + if enriched != nil { + external = enriched + } + if infoErr != nil { + u.logger.DebugContext(ctx, "oidc callback: userinfo fetch failed", "team_id", teamID, "error", infoErr) + } else { + u.logger.DebugContext(ctx, "oidc callback: userinfo loaded", + append([]any{"team_id", teamID, "source", "userinfo"}, oidcExternalUserLogAttrs(external)...)..., + ) + } + } + if external.Email == "" { + u.logger.DebugContext(ctx, "oidc callback: email missing", + append([]any{"team_id", teamID}, oidcExternalUserLogAttrs(external)...)..., + ) + return nil, errcode.ErrOIDCEmailRequired + } + if !external.EmailVerified { + u.logger.DebugContext(ctx, "oidc callback: email not verified", + append([]any{"team_id", teamID}, oidcExternalUserLogAttrs(external)...)..., + ) + return nil, errcode.ErrOIDCEmailNotVerified + } + if !oidcEmailDomainAllowed(external.Email, cfg.EmailDomain) { + u.logger.DebugContext(ctx, "oidc callback: email domain denied", + append([]any{"team_id", teamID, "email_domain", cfg.EmailDomain}, oidcExternalUserLogAttrs(external)...)..., + ) + return nil, errcode.ErrOIDCEmailDomainDenied + } + + identityID := oidcpkg.IdentityID(external.Issuer, external.Subject) + u.logger.DebugContext(ctx, "oidc callback: identity resolved", + "team_id", teamID, + "identity_id", identityID, + "identity_id_len", len(identityID), + "issuer_len", len(external.Issuer), + "subject_len", len(external.Subject), + ) + user, err := u.repo.FindUserByOIDCIdentity(ctx, identityID) + if err != nil && !db.IsNotFound(err) { + return nil, err + } + if user == nil { + u.logger.DebugContext(ctx, "oidc callback: identity not bound, checking team member", + "team_id", teamID, + "email", external.Email, + "auto_create_member", cfg.AutoCreateMember, + ) + member, err := u.repo.FindTeamMemberByEmail(ctx, teamID, external.Email) + if err == nil && member.Edges.User != nil { + user = member.Edges.User + u.logger.DebugContext(ctx, "oidc callback: matched existing team member", + "team_id", teamID, + "user_id", user.ID, + "email", external.Email, + ) + } else if db.IsNotFound(err) && cfg.AutoCreateMember { + u.logger.DebugContext(ctx, "oidc callback: auto creating team member", + "team_id", teamID, + "email", external.Email, + "name", oidcDisplayName(external), + ) + user, err = u.repo.AutoCreateMember(ctx, teamID, external) + if err != nil { + return nil, err + } + u.logger.DebugContext(ctx, "oidc callback: auto created team member", + "team_id", teamID, + "user_id", user.ID, + "email", external.Email, + ) + } else if db.IsNotFound(err) { + u.logger.DebugContext(ctx, "oidc callback: team member required", + "team_id", teamID, + "email", external.Email, + ) + return nil, errcode.ErrOIDCTeamMemberRequired + } else if err != nil { + return nil, err + } + } else { + u.logger.DebugContext(ctx, "oidc callback: matched bound identity", + "team_id", teamID, + "user_id", user.ID, + "identity_id_len", len(identityID), + ) + } + if user.IsBlocked { + u.logger.DebugContext(ctx, "oidc callback: user blocked", "team_id", teamID, "user_id", user.ID) + return nil, errcode.ErrUserBlocked + } + if err := u.repo.BindOIDCIdentity(ctx, user.ID, external); err != nil { + return nil, err + } + u.logger.DebugContext(ctx, "oidc callback: login success", + "team_id", teamID, + "user_id", user.ID, + "identity_id_len", len(identityID), + "email", external.Email, + ) + return cvt.From(user, &domain.User{}), nil +} + +func (u *TeamOIDCUsecase) PublicConfig(ctx context.Context, teamID uuid.UUID) (*domain.TeamOIDCPublicConfigResp, error) { + cfg, err := u.repo.GetConfig(ctx, teamID) + if err != nil || !cfg.Enabled { + return &domain.TeamOIDCPublicConfigResp{TeamID: teamID, Enabled: false}, nil + } + return u.publicConfigResp(cfg), nil +} + +func (u *TeamOIDCUsecase) DefaultPublicConfig(ctx context.Context) (*domain.TeamOIDCPublicConfigResp, error) { + cfg, err := u.repo.GetDefaultEnabledConfig(ctx) + if err != nil { + if db.IsNotFound(err) { + return &domain.TeamOIDCPublicConfigResp{Enabled: false}, nil + } + return nil, err + } + return u.publicConfigResp(cfg), nil +} + +func (u *TeamOIDCUsecase) publicConfigResp(cfg *db.TeamOIDCConfig) *domain.TeamOIDCPublicConfigResp { + return &domain.TeamOIDCPublicConfigResp{ + TeamID: cfg.TeamID, + Enabled: true, + DisplayName: cfg.DisplayName, + LoginURL: strings.TrimRight(u.cfg.Server.BaseURL, "/") + "/api/v1/users/oidc/login?team_id=" + cfg.TeamID.String(), + } +} + +func (u *TeamOIDCUsecase) PasswordLoginAllowed(ctx context.Context, teamID uuid.UUID) (bool, error) { + cfg, err := u.repo.GetConfig(ctx, teamID) + if err != nil { + if db.IsNotFound(err) { + return true, nil + } + return false, err + } + return !cfg.Enabled || cfg.AllowPasswordLogin, nil +} + +func (u *TeamOIDCUsecase) emptyConfig(teamID uuid.UUID) *domain.TeamOIDCConfig { + return &domain.TeamOIDCConfig{ + TeamID: teamID, + DisplayName: "企业登录", + Scopes: "openid email profile", + AllowPasswordLogin: true, + RedirectURI: u.redirectURI(), + LoginURL: u.loginURL(teamID), + } +} + +func (u *TeamOIDCUsecase) configResp(cfg *db.TeamOIDCConfig) *domain.TeamOIDCConfig { + return &domain.TeamOIDCConfig{ + ID: cfg.ID, + TeamID: cfg.TeamID, + Enabled: cfg.Enabled, + DisplayName: cfg.DisplayName, + Issuer: cfg.Issuer, + ClientID: cfg.ClientID, + HasClientSecret: cfg.ClientSecretCiphertext != "", + Scopes: cfg.Scopes, + EmailDomain: cfg.EmailDomain, + AutoCreateMember: cfg.AutoCreateMember, + AllowPasswordLogin: cfg.AllowPasswordLogin, + RedirectURI: u.redirectURI(), + LoginURL: u.loginURL(cfg.TeamID), + } +} + +func (u *TeamOIDCUsecase) redirectURI() string { + return strings.TrimRight(u.cfg.Server.BaseURL, "/") + "/api/v1/users/oidc/callback" +} + +func (u *TeamOIDCUsecase) loginURL(teamID uuid.UUID) string { + return strings.TrimRight(u.cfg.Server.BaseURL, "/") + "/team-login/" + teamID.String() +} + +func oidcEmailDomainAllowed(email, domain string) bool { + domain = strings.TrimPrefix(strings.ToLower(strings.TrimSpace(domain)), "@") + if domain == "" { + return true + } + return strings.HasSuffix(strings.ToLower(strings.TrimSpace(email)), "@"+domain) +} + +func oidcDisplayName(external *domain.OIDCExternalUser) string { + if external.Name != "" { + return external.Name + } + if external.Username != "" { + return external.Username + } + if idx := strings.IndexByte(external.Email, '@'); idx > 0 { + return external.Email[:idx] + } + return external.Email +} + +func oidcExternalUserLogAttrs(external *domain.OIDCExternalUser) []any { + if external == nil { + return []any{"external_user_nil", true} + } + return []any{ + "issuer", external.Issuer, + "issuer_len", len(external.Issuer), + "subject", external.Subject, + "subject_len", len(external.Subject), + "email", external.Email, + "email_present", external.Email != "", + "email_verified", external.EmailVerified, + "name", external.Name, + "name_present", external.Name != "", + "username", external.Username, + "username_present", external.Username != "", + "avatar_url", oidcShortDebugValue(external.AvatarURL), + "avatar_url_present", external.AvatarURL != "", + "avatar_url_len", len(external.AvatarURL), + } +} + +func oidcShortDebugValue(value string) string { + if len(value) <= oidcDebugValueMaxLen { + return value + } + return value[:oidcDebugValueMaxLen] + "...(truncated)" +} + +func randomURLToken() string { + var b [32]byte + _, _ = rand.Read(b[:]) + return base64.RawURLEncoding.EncodeToString(b[:]) +} diff --git a/backend/biz/team/usecase/oidc_test.go b/backend/biz/team/usecase/oidc_test.go new file mode 100644 index 00000000..dc5aeb62 --- /dev/null +++ b/backend/biz/team/usecase/oidc_test.go @@ -0,0 +1,140 @@ +package usecase + +import ( + "context" + "errors" + "strings" + "testing" + + "github.com/google/uuid" + + "github.com/chaitin/MonkeyCode/backend/config" + "github.com/chaitin/MonkeyCode/backend/db" + "github.com/chaitin/MonkeyCode/backend/domain" +) + +func TestOIDCEmailDomainAllowed(t *testing.T) { + if !oidcEmailDomainAllowed("alice@example.com", "example.com") { + t.Fatal("expected domain to be allowed") + } + if oidcEmailDomainAllowed("alice@evil.com", "example.com") { + t.Fatal("expected domain to be denied") + } +} + +func TestOIDCDisplayNameFallsBack(t *testing.T) { + got := oidcDisplayName(&domain.OIDCExternalUser{Email: "alice@example.com"}) + if got != "alice" { + t.Fatalf("display name = %q, want alice", got) + } +} + +func TestOIDCExternalUserLogAttrsSummarizesCallbackData(t *testing.T) { + attrs := oidcExternalUserLogAttrs(&domain.OIDCExternalUser{ + Issuer: "https://id.example.com/realms/mcai", + Subject: "fcf4de16-2676-414d-8779-aafddb3e8362", + Email: "alice@example.com", + EmailVerified: true, + Name: "Alice", + Username: "alice", + AvatarURL: strings.Repeat("a", 300), + }) + + got := logAttrsMap(attrs) + if got["issuer"] != "https://id.example.com/realms/mcai" { + t.Fatalf("issuer attr = %v", got["issuer"]) + } + if got["subject"] != "fcf4de16-2676-414d-8779-aafddb3e8362" { + t.Fatalf("subject attr = %v", got["subject"]) + } + if got["subject_len"] != 36 { + t.Fatalf("subject_len attr = %v", got["subject_len"]) + } + if got["email"] != "alice@example.com" { + t.Fatalf("email attr = %v", got["email"]) + } + if got["email_verified"] != true { + t.Fatalf("email_verified attr = %v", got["email_verified"]) + } + if got["username"] != "alice" { + t.Fatalf("username attr = %v", got["username"]) + } + if got["avatar_url_len"] != 300 { + t.Fatalf("avatar_url_len attr = %v", got["avatar_url_len"]) + } + if got["avatar_url"] == strings.Repeat("a", 300) { + t.Fatal("avatar_url should be shortened for debug logs") + } +} + +func TestTeamOIDCUsecaseDefaultPublicConfigReturnsDisabledWhenNotConfigured(t *testing.T) { + u := &TeamOIDCUsecase{ + repo: &defaultOIDCRepoStub{err: &db.NotFoundError{}}, + cfg: &config.Config{}, + } + + resp, err := u.DefaultPublicConfig(context.Background()) + if err != nil { + t.Fatal(err) + } + if resp.Enabled { + t.Fatal("expected default OIDC config disabled") + } +} + +func logAttrsMap(attrs []any) map[string]any { + m := make(map[string]any, len(attrs)/2) + for i := 0; i+1 < len(attrs); i += 2 { + key, _ := attrs[i].(string) + m[key] = attrs[i+1] + } + return m +} + +func TestTeamOIDCUsecaseDefaultPublicConfigReturnsLoginURL(t *testing.T) { + teamID := uuid.New() + cfg := &config.Config{} + cfg.Server.BaseURL = "http://monkeycode.example.com/" + u := &TeamOIDCUsecase{ + repo: &defaultOIDCRepoStub{cfg: &db.TeamOIDCConfig{ + TeamID: teamID, + Enabled: true, + DisplayName: "公司账号登录", + }}, + cfg: cfg, + } + + resp, err := u.DefaultPublicConfig(context.Background()) + if err != nil { + t.Fatal(err) + } + if !resp.Enabled { + t.Fatal("expected default OIDC config enabled") + } + if resp.TeamID != teamID { + t.Fatalf("team id = %s, want %s", resp.TeamID, teamID) + } + if resp.DisplayName != "公司账号登录" { + t.Fatalf("display name = %q", resp.DisplayName) + } + wantURL := "http://monkeycode.example.com/api/v1/users/oidc/login?team_id=" + teamID.String() + if resp.LoginURL != wantURL { + t.Fatalf("login url = %q, want %q", resp.LoginURL, wantURL) + } +} + +type defaultOIDCRepoStub struct { + domain.TeamOIDCRepo + cfg *db.TeamOIDCConfig + err error +} + +func (s *defaultOIDCRepoStub) GetDefaultEnabledConfig(ctx context.Context) (*db.TeamOIDCConfig, error) { + if s.err != nil { + return nil, s.err + } + if s.cfg == nil { + return nil, errors.New("unexpected empty config") + } + return s.cfg, nil +} diff --git a/backend/biz/user/handler/v1/auth.go b/backend/biz/user/handler/v1/auth.go index a24db20a..eadccf9a 100644 --- a/backend/biz/user/handler/v1/auth.go +++ b/backend/biz/user/handler/v1/auth.go @@ -24,6 +24,7 @@ type AuthHandler struct { logger *slog.Logger usecase domain.UserUsecase teamUsecase domain.TeamGroupUserUsecase + oidcUsecase domain.TeamOIDCLoginUsecase redis *redis.Client authMiddleware *middleware.AuthMiddleware captcha *captcha.Captcha @@ -40,12 +41,14 @@ func NewAuthHandler(i *do.Injector) (*AuthHandler, error) { auth := do.MustInvoke[*middleware.AuthMiddleware](i) targetActive := do.MustInvoke[*middleware.TargetActiveMiddleware](i) captchaSvc := do.MustInvoke[*captcha.Captcha](i) + oidcUsecase, _ := do.Invoke[domain.TeamOIDCLoginUsecase](i) h := &AuthHandler{ config: cfg, logger: logger.With("module", "auth.handler"), usecase: usecase, teamUsecase: teamUsecase, + oidcUsecase: oidcUsecase, redis: redisClient, authMiddleware: auth, captcha: captchaSvc, @@ -60,6 +63,10 @@ func NewAuthHandler(i *do.Injector) (*AuthHandler, error) { // 密码登录 v1.POST("/password-login", web.BindHandler(h.PasswordLogin), targetActive.TargetActive()) + v1.GET("/oidc/login", web.BindHandler(h.OIDCLogin), targetActive.TargetActive()) + v1.GET("/oidc/callback", web.BindHandler(h.OIDCCallback), targetActive.TargetActive()) + v1.GET("/oidc/default-team", web.BaseHandler(h.OIDCDefaultPublicConfig), targetActive.TargetActive()) + v1.GET("/oidc/teams/:team_id", web.BindHandler(h.OIDCPublicConfig), targetActive.TargetActive()) v1.PUT("", web.BindHandler(h.Update), auth.Auth(), targetActive.TargetActive()) v1.PUT("/passwords/change", web.BindHandler(h.ChangePassword), auth.Check(), targetActive.TargetActive()) v1.GET("/status", web.BaseHandler(h.Status), auth.Check(), targetActive.TargetActive()) @@ -73,6 +80,93 @@ func NewAuthHandler(i *do.Injector) (*AuthHandler, error) { return h, nil } +// OIDCLogin 发起团队 OIDC 登录 +// +// @Summary 发起团队 OIDC 登录 +// @Description 根据 team_id 跳转到团队 OIDC 身份源 +// @Tags 【用户】企业团队成员认证 +// @Accept json +// @Produce json +// @Param team_id query string true "团队 ID" +// @Router /api/v1/users/oidc/login [get] +func (h *AuthHandler) OIDCLogin(c *web.Context, req domain.TeamOIDCLoginReq) error { + if h.oidcUsecase == nil { + return errcode.ErrOIDCDisabled + } + authURL, err := h.oidcUsecase.StartLogin(c.Request().Context(), req.TeamID) + if err != nil { + return err + } + return c.Redirect(http.StatusFound, authURL) +} + +// OIDCCallback 处理团队 OIDC 回调 +// +// @Summary 处理团队 OIDC 回调 +// @Description 处理身份源回调并创建 MonkeyCode 登录会话 +// @Tags 【用户】企业团队成员认证 +// @Accept json +// @Produce json +// @Param code query string true "授权码" +// @Param state query string true "状态" +// @Router /api/v1/users/oidc/callback [get] +func (h *AuthHandler) OIDCCallback(c *web.Context, req domain.TeamOIDCCallbackReq) error { + if h.oidcUsecase == nil { + return errcode.ErrOIDCDisabled + } + user, err := h.oidcUsecase.HandleCallback(c.Request().Context(), &req) + if err != nil { + return err + } + _, err = h.authMiddleware.Session.Save(c, consts.MonkeyCodeAISession, user.ID, user) + if err != nil { + h.logger.ErrorContext(c.Request().Context(), "save oidc session failed", "error", err) + return errcode.ErrInternalServer + } + return c.Redirect(http.StatusFound, "/console/") +} + +// OIDCPublicConfig 获取团队公开 OIDC 登录配置 +// +// @Summary 获取团队公开 OIDC 登录配置 +// @Description 用于团队专属登录页展示企业登录入口 +// @Tags 【用户】企业团队成员认证 +// @Accept json +// @Produce json +// @Param team_id path string true "团队 ID" +// @Success 200 {object} web.Resp{data=domain.TeamOIDCPublicConfigResp} +// @Router /api/v1/users/oidc/teams/{team_id} [get] +func (h *AuthHandler) OIDCPublicConfig(c *web.Context, req domain.TeamOIDCPublicConfigReq) error { + if h.oidcUsecase == nil { + return c.Success(&domain.TeamOIDCPublicConfigResp{TeamID: req.TeamID}) + } + resp, err := h.oidcUsecase.PublicConfig(c.Request().Context(), req.TeamID) + if err != nil { + return err + } + return c.Success(resp) +} + +// OIDCDefaultPublicConfig 获取默认团队公开 OIDC 登录配置 +// +// @Summary 获取默认团队公开 OIDC 登录配置 +// @Description 用于私有化登录页展示第一个已启用团队的企业登录入口 +// @Tags 【用户】企业团队成员认证 +// @Accept json +// @Produce json +// @Success 200 {object} web.Resp{data=domain.TeamOIDCPublicConfigResp} +// @Router /api/v1/users/oidc/default-team [get] +func (h *AuthHandler) OIDCDefaultPublicConfig(c *web.Context) error { + if h.oidcUsecase == nil { + return c.Success(&domain.TeamOIDCPublicConfigResp{}) + } + resp, err := h.oidcUsecase.DefaultPublicConfig(c.Request().Context()) + if err != nil { + return err + } + return c.Success(resp) +} + // PasswordLogin 密码登录接口 // // @Summary 密码登录 diff --git a/backend/biz/user/handler/v1/members_route_test.go b/backend/biz/user/handler/v1/members_route_test.go index 52e0e11e..6ee3b3e7 100644 --- a/backend/biz/user/handler/v1/members_route_test.go +++ b/backend/biz/user/handler/v1/members_route_test.go @@ -42,6 +42,32 @@ func TestNewAuthHandlerRegistersMembersRoute(t *testing.T) { t.Fatal("GET /api/v1/users/members route is not registered") } +func TestNewAuthHandlerRegistersDefaultOIDCRoute(t *testing.T) { + injector := do.New() + w := web.New() + do.ProvideValue(injector, w) + do.ProvideValue(injector, &config.Config{}) + do.ProvideValue(injector, slog.New(slog.NewTextHandler(io.Discard, nil))) + do.ProvideValue(injector, (*redis.Client)(nil)) + do.ProvideValue[domain.UserUsecase](injector, &membersUserUsecaseStub{}) + do.ProvideValue[domain.TeamGroupUserUsecase](injector, &membersTeamUsecaseStub{}) + do.ProvideValue[domain.TeamOIDCLoginUsecase](injector, &membersOIDCUsecaseStub{}) + do.ProvideValue(injector, &middleware.AuthMiddleware{}) + do.ProvideValue(injector, middleware.NewTargetActiveMiddleware(slog.New(slog.NewTextHandler(io.Discard, nil)), nil)) + do.ProvideValue(injector, captcha.NewCaptcha()) + + if _, err := NewAuthHandler(injector); err != nil { + t.Fatal(err) + } + + for _, route := range w.Routes() { + if route.Method == "GET" && route.Path == "/api/v1/users/oidc/default-team" { + return + } + } + t.Fatal("GET /api/v1/users/oidc/default-team route is not registered") +} + type membersUserUsecaseStub struct { domain.UserUsecase } @@ -57,3 +83,11 @@ type membersTeamUsecaseStub struct { func (s *membersTeamUsecaseStub) MemberList(ctx context.Context, teamUser *domain.TeamUser, req *domain.MemberListReq) (*domain.MemberListResp, error) { return &domain.MemberListResp{}, nil } + +type membersOIDCUsecaseStub struct { + domain.TeamOIDCLoginUsecase +} + +func (s *membersOIDCUsecaseStub) DefaultPublicConfig(ctx context.Context) (*domain.TeamOIDCPublicConfigResp, error) { + return &domain.TeamOIDCPublicConfigResp{}, nil +} diff --git a/backend/biz/user/repo/user.go b/backend/biz/user/repo/user.go index ca5fec5d..d6f49aa6 100644 --- a/backend/biz/user/repo/user.go +++ b/backend/biz/user/repo/user.go @@ -12,6 +12,7 @@ import ( "github.com/chaitin/MonkeyCode/backend/consts" "github.com/chaitin/MonkeyCode/backend/db" "github.com/chaitin/MonkeyCode/backend/db/notifychannel" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" "github.com/chaitin/MonkeyCode/backend/db/user" "github.com/chaitin/MonkeyCode/backend/domain" "github.com/chaitin/MonkeyCode/backend/errcode" @@ -87,6 +88,10 @@ func (u *userRepo) PasswordLogin(ctx context.Context, req *domain.TeamLoginReq) return nil, errcode.ErrLoginFailed.Wrap(err) } + if err := u.checkPasswordLoginAllowed(ctx, usr); err != nil { + return nil, err + } + err = crypto.VerifyPassword(usr.Password, req.Password) if err != nil { u.logger.Error("invalid password", "email", req.Email, "error", err) @@ -95,6 +100,27 @@ func (u *userRepo) PasswordLogin(ctx context.Context, req *domain.TeamLoginReq) return usr, nil } +func (u *userRepo) checkPasswordLoginAllowed(ctx context.Context, usr *db.User) error { + if usr == nil || usr.Role == consts.UserRoleEnterprise { + return nil + } + for _, member := range usr.Edges.TeamMembers { + cfg, err := u.db.TeamOIDCConfig.Query(). + Where(teamoidcconfig.TeamIDEQ(member.TeamID)). + First(ctx) + if err != nil { + if db.IsNotFound(err) { + continue + } + return err + } + if cfg.Enabled && !cfg.AllowPasswordLogin { + return errcode.ErrPasswordLoginDisabled + } + } + return nil +} + // ChangePassword implements domain.UserRepo. func (u *userRepo) ChangePassword(ctx context.Context, userID uuid.UUID, currentPassword, newPassword string, isReset bool) error { uu, err := u.db.User.Query().Where(user.IDEQ(userID)).First(ctx) diff --git a/backend/biz/user/repo/user_test.go b/backend/biz/user/repo/user_test.go new file mode 100644 index 00000000..42d6a495 --- /dev/null +++ b/backend/biz/user/repo/user_test.go @@ -0,0 +1,51 @@ +package repo + +import ( + "context" + "io" + "log/slog" + "testing" + + "github.com/google/uuid" + _ "github.com/mattn/go-sqlite3" + + "github.com/chaitin/MonkeyCode/backend/consts" + "github.com/chaitin/MonkeyCode/backend/db/enttest" + "github.com/chaitin/MonkeyCode/backend/domain" + "github.com/chaitin/MonkeyCode/backend/errcode" + "github.com/chaitin/MonkeyCode/backend/pkg/crypto" +) + +func TestPasswordLoginRejectsSubAccountWhenOIDCDisablesPassword(t *testing.T) { + ctx := context.Background() + client := enttest.Open(t, "sqlite3", "file:user_repo_password_oidc?mode=memory&cache=shared&_fk=1") + defer client.Close() + + teamID := uuid.New() + userID := uuid.New() + hashed, err := crypto.HashPassword("secret") + if err != nil { + t.Fatal(err) + } + if _, err := client.Team.Create().SetID(teamID).SetName("研发团队").SetMemberLimit(10).Save(ctx); err != nil { + t.Fatal(err) + } + if _, err := client.User.Create().SetID(userID).SetName("成员").SetEmail("member@example.com").SetPassword(hashed).SetRole(consts.UserRoleSubAccount).SetStatus(consts.UserStatusActive).Save(ctx); err != nil { + t.Fatal(err) + } + if _, err := client.TeamMember.Create().SetID(uuid.New()).SetTeamID(teamID).SetUserID(userID).SetRole(consts.TeamMemberRoleUser).Save(ctx); err != nil { + t.Fatal(err) + } + if _, err := client.TeamOIDCConfig.Create().SetID(uuid.New()).SetTeamID(teamID).SetEnabled(true).SetDisplayName("企业登录").SetIssuer("https://id.example.com").SetClientID("client").SetAllowPasswordLogin(false).Save(ctx); err != nil { + t.Fatal(err) + } + + repo := &userRepo{db: client, logger: slog.New(slog.NewTextHandler(io.Discard, nil))} + _, err = repo.PasswordLogin(ctx, &domain.TeamLoginReq{Email: "member@example.com", Password: "secret"}) + if err == nil { + t.Fatal("expected password login disabled error") + } + if err != errcode.ErrPasswordLoginDisabled { + t.Fatalf("error = %v, want ErrPasswordLoginDisabled", err) + } +} diff --git a/backend/consts/user.go b/backend/consts/user.go index 59fdf480..99235604 100644 --- a/backend/consts/user.go +++ b/backend/consts/user.go @@ -8,6 +8,7 @@ const ( UserPlatformGitLab UserPlatform = "gitlab" UserPlatformGitea UserPlatform = "gitea" UserPlatformGitee UserPlatform = "gitee" + UserPlatformOIDC UserPlatform = "oidc" ) type UserStatus string diff --git a/backend/db/client.go b/backend/db/client.go index eaa61680..5e3da272 100644 --- a/backend/db/client.go +++ b/backend/db/client.go @@ -53,6 +53,7 @@ import ( "github.com/chaitin/MonkeyCode/backend/db/teamimage" "github.com/chaitin/MonkeyCode/backend/db/teammember" "github.com/chaitin/MonkeyCode/backend/db/teammodel" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" "github.com/chaitin/MonkeyCode/backend/db/user" "github.com/chaitin/MonkeyCode/backend/db/useridentity" "github.com/chaitin/MonkeyCode/backend/db/virtualmachine" @@ -139,6 +140,8 @@ type Client struct { TeamMember *TeamMemberClient // TeamModel is the client for interacting with the TeamModel builders. TeamModel *TeamModelClient + // TeamOIDCConfig is the client for interacting with the TeamOIDCConfig builders. + TeamOIDCConfig *TeamOIDCConfigClient // User is the client for interacting with the User builders. User *UserClient // UserIdentity is the client for interacting with the UserIdentity builders. @@ -193,6 +196,7 @@ func (c *Client) init() { c.TeamImage = NewTeamImageClient(c.config) c.TeamMember = NewTeamMemberClient(c.config) c.TeamModel = NewTeamModelClient(c.config) + c.TeamOIDCConfig = NewTeamOIDCConfigClient(c.config) c.User = NewUserClient(c.config) c.UserIdentity = NewUserIdentityClient(c.config) c.VirtualMachine = NewVirtualMachineClient(c.config) @@ -325,6 +329,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { TeamImage: NewTeamImageClient(cfg), TeamMember: NewTeamMemberClient(cfg), TeamModel: NewTeamModelClient(cfg), + TeamOIDCConfig: NewTeamOIDCConfigClient(cfg), User: NewUserClient(cfg), UserIdentity: NewUserIdentityClient(cfg), VirtualMachine: NewVirtualMachineClient(cfg), @@ -384,6 +389,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) TeamImage: NewTeamImageClient(cfg), TeamMember: NewTeamMemberClient(cfg), TeamModel: NewTeamModelClient(cfg), + TeamOIDCConfig: NewTeamOIDCConfigClient(cfg), User: NewUserClient(cfg), UserIdentity: NewUserIdentityClient(cfg), VirtualMachine: NewVirtualMachineClient(cfg), @@ -423,8 +429,8 @@ func (c *Client) Use(hooks ...Hook) { c.ProjectIssue, c.ProjectIssueComment, c.ProjectTask, c.Task, c.TaskModelSwitch, c.TaskUsageStat, c.TaskVirtualMachine, c.Team, c.TeamGroup, c.TeamGroupHost, c.TeamGroupImage, c.TeamGroupMember, c.TeamGroupModel, - c.TeamHost, c.TeamImage, c.TeamMember, c.TeamModel, c.User, c.UserIdentity, - c.VirtualMachine, + c.TeamHost, c.TeamImage, c.TeamMember, c.TeamModel, c.TeamOIDCConfig, c.User, + c.UserIdentity, c.VirtualMachine, } { n.Use(hooks...) } @@ -441,8 +447,8 @@ func (c *Client) Intercept(interceptors ...Interceptor) { c.ProjectIssue, c.ProjectIssueComment, c.ProjectTask, c.Task, c.TaskModelSwitch, c.TaskUsageStat, c.TaskVirtualMachine, c.Team, c.TeamGroup, c.TeamGroupHost, c.TeamGroupImage, c.TeamGroupMember, c.TeamGroupModel, - c.TeamHost, c.TeamImage, c.TeamMember, c.TeamModel, c.User, c.UserIdentity, - c.VirtualMachine, + c.TeamHost, c.TeamImage, c.TeamMember, c.TeamModel, c.TeamOIDCConfig, c.User, + c.UserIdentity, c.VirtualMachine, } { n.Intercept(interceptors...) } @@ -525,6 +531,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.TeamMember.mutate(ctx, m) case *TeamModelMutation: return c.TeamModel.mutate(ctx, m) + case *TeamOIDCConfigMutation: + return c.TeamOIDCConfig.mutate(ctx, m) case *UserMutation: return c.User.mutate(ctx, m) case *UserIdentityMutation: @@ -7379,6 +7387,155 @@ func (c *TeamModelClient) mutate(ctx context.Context, m *TeamModelMutation) (Val } } +// TeamOIDCConfigClient is a client for the TeamOIDCConfig schema. +type TeamOIDCConfigClient struct { + config +} + +// NewTeamOIDCConfigClient returns a client for the TeamOIDCConfig from the given config. +func NewTeamOIDCConfigClient(c config) *TeamOIDCConfigClient { + return &TeamOIDCConfigClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `teamoidcconfig.Hooks(f(g(h())))`. +func (c *TeamOIDCConfigClient) Use(hooks ...Hook) { + c.hooks.TeamOIDCConfig = append(c.hooks.TeamOIDCConfig, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `teamoidcconfig.Intercept(f(g(h())))`. +func (c *TeamOIDCConfigClient) Intercept(interceptors ...Interceptor) { + c.inters.TeamOIDCConfig = append(c.inters.TeamOIDCConfig, interceptors...) +} + +// Create returns a builder for creating a TeamOIDCConfig entity. +func (c *TeamOIDCConfigClient) Create() *TeamOIDCConfigCreate { + mutation := newTeamOIDCConfigMutation(c.config, OpCreate) + return &TeamOIDCConfigCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of TeamOIDCConfig entities. +func (c *TeamOIDCConfigClient) CreateBulk(builders ...*TeamOIDCConfigCreate) *TeamOIDCConfigCreateBulk { + return &TeamOIDCConfigCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *TeamOIDCConfigClient) MapCreateBulk(slice any, setFunc func(*TeamOIDCConfigCreate, int)) *TeamOIDCConfigCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &TeamOIDCConfigCreateBulk{err: fmt.Errorf("calling to TeamOIDCConfigClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*TeamOIDCConfigCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &TeamOIDCConfigCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for TeamOIDCConfig. +func (c *TeamOIDCConfigClient) Update() *TeamOIDCConfigUpdate { + mutation := newTeamOIDCConfigMutation(c.config, OpUpdate) + return &TeamOIDCConfigUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *TeamOIDCConfigClient) UpdateOne(_m *TeamOIDCConfig) *TeamOIDCConfigUpdateOne { + mutation := newTeamOIDCConfigMutation(c.config, OpUpdateOne, withTeamOIDCConfig(_m)) + return &TeamOIDCConfigUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *TeamOIDCConfigClient) UpdateOneID(id uuid.UUID) *TeamOIDCConfigUpdateOne { + mutation := newTeamOIDCConfigMutation(c.config, OpUpdateOne, withTeamOIDCConfigID(id)) + return &TeamOIDCConfigUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for TeamOIDCConfig. +func (c *TeamOIDCConfigClient) Delete() *TeamOIDCConfigDelete { + mutation := newTeamOIDCConfigMutation(c.config, OpDelete) + return &TeamOIDCConfigDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *TeamOIDCConfigClient) DeleteOne(_m *TeamOIDCConfig) *TeamOIDCConfigDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *TeamOIDCConfigClient) DeleteOneID(id uuid.UUID) *TeamOIDCConfigDeleteOne { + builder := c.Delete().Where(teamoidcconfig.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &TeamOIDCConfigDeleteOne{builder} +} + +// Query returns a query builder for TeamOIDCConfig. +func (c *TeamOIDCConfigClient) Query() *TeamOIDCConfigQuery { + return &TeamOIDCConfigQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeTeamOIDCConfig}, + inters: c.Interceptors(), + } +} + +// Get returns a TeamOIDCConfig entity by its id. +func (c *TeamOIDCConfigClient) Get(ctx context.Context, id uuid.UUID) (*TeamOIDCConfig, error) { + return c.Query().Where(teamoidcconfig.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *TeamOIDCConfigClient) GetX(ctx context.Context, id uuid.UUID) *TeamOIDCConfig { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryTeam queries the team edge of a TeamOIDCConfig. +func (c *TeamOIDCConfigClient) QueryTeam(_m *TeamOIDCConfig) *TeamQuery { + query := (&TeamClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(teamoidcconfig.Table, teamoidcconfig.FieldID, id), + sqlgraph.To(team.Table, team.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, teamoidcconfig.TeamTable, teamoidcconfig.TeamColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *TeamOIDCConfigClient) Hooks() []Hook { + return c.hooks.TeamOIDCConfig +} + +// Interceptors returns the client interceptors. +func (c *TeamOIDCConfigClient) Interceptors() []Interceptor { + return c.inters.TeamOIDCConfig +} + +func (c *TeamOIDCConfigClient) mutate(ctx context.Context, m *TeamOIDCConfigMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&TeamOIDCConfigCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&TeamOIDCConfigUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&TeamOIDCConfigUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&TeamOIDCConfigDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("db: unknown TeamOIDCConfig mutation op: %q", m.Op()) + } +} + // UserClient is a client for the User schema. type UserClient struct { config @@ -8241,7 +8398,8 @@ type ( ProjectGitBot, ProjectIssue, ProjectIssueComment, ProjectTask, Task, TaskModelSwitch, TaskUsageStat, TaskVirtualMachine, Team, TeamGroup, TeamGroupHost, TeamGroupImage, TeamGroupMember, TeamGroupModel, TeamHost, - TeamImage, TeamMember, TeamModel, User, UserIdentity, VirtualMachine []ent.Hook + TeamImage, TeamMember, TeamModel, TeamOIDCConfig, User, UserIdentity, + VirtualMachine []ent.Hook } inters struct { Audit, GitBot, GitBotTask, GitBotUser, GitIdentity, GitTask, Host, Image, @@ -8250,7 +8408,7 @@ type ( ProjectGitBot, ProjectIssue, ProjectIssueComment, ProjectTask, Task, TaskModelSwitch, TaskUsageStat, TaskVirtualMachine, Team, TeamGroup, TeamGroupHost, TeamGroupImage, TeamGroupMember, TeamGroupModel, TeamHost, - TeamImage, TeamMember, TeamModel, User, UserIdentity, + TeamImage, TeamMember, TeamModel, TeamOIDCConfig, User, UserIdentity, VirtualMachine []ent.Interceptor } ) diff --git a/backend/db/ent.go b/backend/db/ent.go index 0d83033d..d4e57b21 100644 --- a/backend/db/ent.go +++ b/backend/db/ent.go @@ -49,6 +49,7 @@ import ( "github.com/chaitin/MonkeyCode/backend/db/teamimage" "github.com/chaitin/MonkeyCode/backend/db/teammember" "github.com/chaitin/MonkeyCode/backend/db/teammodel" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" "github.com/chaitin/MonkeyCode/backend/db/user" "github.com/chaitin/MonkeyCode/backend/db/useridentity" "github.com/chaitin/MonkeyCode/backend/db/virtualmachine" @@ -149,6 +150,7 @@ func checkColumn(t, c string) error { teamimage.Table: teamimage.ValidColumn, teammember.Table: teammember.ValidColumn, teammodel.Table: teammodel.ValidColumn, + teamoidcconfig.Table: teamoidcconfig.ValidColumn, user.Table: user.ValidColumn, useridentity.Table: useridentity.ValidColumn, virtualmachine.Table: virtualmachine.ValidColumn, diff --git a/backend/db/hook/hook.go b/backend/db/hook/hook.go index cf0675e1..88c72cbb 100644 --- a/backend/db/hook/hook.go +++ b/backend/db/hook/hook.go @@ -453,6 +453,18 @@ func (f TeamModelFunc) Mutate(ctx context.Context, m db.Mutation) (db.Value, err return nil, fmt.Errorf("unexpected mutation type %T. expect *db.TeamModelMutation", m) } +// The TeamOIDCConfigFunc type is an adapter to allow the use of ordinary +// function as TeamOIDCConfig mutator. +type TeamOIDCConfigFunc func(context.Context, *db.TeamOIDCConfigMutation) (db.Value, error) + +// Mutate calls f(ctx, m). +func (f TeamOIDCConfigFunc) Mutate(ctx context.Context, m db.Mutation) (db.Value, error) { + if mv, ok := m.(*db.TeamOIDCConfigMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *db.TeamOIDCConfigMutation", m) +} + // The UserFunc type is an adapter to allow the use of ordinary // function as User mutator. type UserFunc func(context.Context, *db.UserMutation) (db.Value, error) diff --git a/backend/db/intercept/intercept.go b/backend/db/intercept/intercept.go index 6aba2cdb..cd073016 100644 --- a/backend/db/intercept/intercept.go +++ b/backend/db/intercept/intercept.go @@ -46,6 +46,7 @@ import ( "github.com/chaitin/MonkeyCode/backend/db/teamimage" "github.com/chaitin/MonkeyCode/backend/db/teammember" "github.com/chaitin/MonkeyCode/backend/db/teammodel" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" "github.com/chaitin/MonkeyCode/backend/db/user" "github.com/chaitin/MonkeyCode/backend/db/useridentity" "github.com/chaitin/MonkeyCode/backend/db/virtualmachine" @@ -1106,6 +1107,33 @@ func (f TraverseTeamModel) Traverse(ctx context.Context, q db.Query) error { return fmt.Errorf("unexpected query type %T. expect *db.TeamModelQuery", q) } +// The TeamOIDCConfigFunc type is an adapter to allow the use of ordinary function as a Querier. +type TeamOIDCConfigFunc func(context.Context, *db.TeamOIDCConfigQuery) (db.Value, error) + +// Query calls f(ctx, q). +func (f TeamOIDCConfigFunc) Query(ctx context.Context, q db.Query) (db.Value, error) { + if q, ok := q.(*db.TeamOIDCConfigQuery); ok { + return f(ctx, q) + } + return nil, fmt.Errorf("unexpected query type %T. expect *db.TeamOIDCConfigQuery", q) +} + +// The TraverseTeamOIDCConfig type is an adapter to allow the use of ordinary function as Traverser. +type TraverseTeamOIDCConfig func(context.Context, *db.TeamOIDCConfigQuery) error + +// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline. +func (f TraverseTeamOIDCConfig) Intercept(next db.Querier) db.Querier { + return next +} + +// Traverse calls f(ctx, q). +func (f TraverseTeamOIDCConfig) Traverse(ctx context.Context, q db.Query) error { + if q, ok := q.(*db.TeamOIDCConfigQuery); ok { + return f(ctx, q) + } + return fmt.Errorf("unexpected query type %T. expect *db.TeamOIDCConfigQuery", q) +} + // The UserFunc type is an adapter to allow the use of ordinary function as a Querier. type UserFunc func(context.Context, *db.UserQuery) (db.Value, error) @@ -1264,6 +1292,8 @@ func NewQuery(q db.Query) (Query, error) { return &query[*db.TeamMemberQuery, predicate.TeamMember, teammember.OrderOption]{typ: db.TypeTeamMember, tq: q}, nil case *db.TeamModelQuery: return &query[*db.TeamModelQuery, predicate.TeamModel, teammodel.OrderOption]{typ: db.TypeTeamModel, tq: q}, nil + case *db.TeamOIDCConfigQuery: + return &query[*db.TeamOIDCConfigQuery, predicate.TeamOIDCConfig, teamoidcconfig.OrderOption]{typ: db.TypeTeamOIDCConfig, tq: q}, nil case *db.UserQuery: return &query[*db.UserQuery, predicate.User, user.OrderOption]{typ: db.TypeUser, tq: q}, nil case *db.UserIdentityQuery: diff --git a/backend/db/migrate/schema.go b/backend/db/migrate/schema.go index 3c179a40..a72fe816 100644 --- a/backend/db/migrate/schema.go +++ b/backend/db/migrate/schema.go @@ -1244,6 +1244,43 @@ var ( }, }, } + // TeamOidcConfigsColumns holds the columns for the "team_oidc_configs" table. + TeamOidcConfigsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID, Unique: true}, + {Name: "enabled", Type: field.TypeBool, Default: false}, + {Name: "display_name", Type: field.TypeString, Default: "企业登录"}, + {Name: "issuer", Type: field.TypeString}, + {Name: "client_id", Type: field.TypeString}, + {Name: "client_secret_ciphertext", Type: field.TypeString, Nullable: true}, + {Name: "scopes", Type: field.TypeString, Default: "openid email profile"}, + {Name: "email_domain", Type: field.TypeString, Nullable: true}, + {Name: "auto_create_member", Type: field.TypeBool, Default: false}, + {Name: "allow_password_login", Type: field.TypeBool, Default: true}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + {Name: "team_id", Type: field.TypeUUID}, + } + // TeamOidcConfigsTable holds the schema information for the "team_oidc_configs" table. + TeamOidcConfigsTable = &schema.Table{ + Name: "team_oidc_configs", + Columns: TeamOidcConfigsColumns, + PrimaryKey: []*schema.Column{TeamOidcConfigsColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "team_oidc_configs_teams_team", + Columns: []*schema.Column{TeamOidcConfigsColumns[12]}, + RefColumns: []*schema.Column{TeamsColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + Indexes: []*schema.Index{ + { + Name: "teamoidcconfig_team_id", + Unique: true, + Columns: []*schema.Column{TeamOidcConfigsColumns[12]}, + }, + }, + } // UsersColumns holds the columns for the "users" table. UsersColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID, Unique: true}, @@ -1392,6 +1429,7 @@ var ( TeamImagesTable, TeamMembersTable, TeamModelsTable, + TeamOidcConfigsTable, UsersTable, UserIdentitiesTable, VirtualmachinesTable, @@ -1569,6 +1607,10 @@ func init() { TeamModelsTable.Annotation = &entsql.Annotation{ Table: "team_models", } + TeamOidcConfigsTable.ForeignKeys[0].RefTable = TeamsTable + TeamOidcConfigsTable.Annotation = &entsql.Annotation{ + Table: "team_oidc_configs", + } UsersTable.Annotation = &entsql.Annotation{ Table: "users", } diff --git a/backend/db/mutation.go b/backend/db/mutation.go index f359602c..bd8e42ce 100644 --- a/backend/db/mutation.go +++ b/backend/db/mutation.go @@ -50,6 +50,7 @@ import ( "github.com/chaitin/MonkeyCode/backend/db/teamimage" "github.com/chaitin/MonkeyCode/backend/db/teammember" "github.com/chaitin/MonkeyCode/backend/db/teammodel" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" "github.com/chaitin/MonkeyCode/backend/db/user" "github.com/chaitin/MonkeyCode/backend/db/useridentity" "github.com/chaitin/MonkeyCode/backend/db/virtualmachine" @@ -103,6 +104,7 @@ const ( TypeTeamImage = "TeamImage" TypeTeamMember = "TeamMember" TypeTeamModel = "TeamModel" + TypeTeamOIDCConfig = "TeamOIDCConfig" TypeUser = "User" TypeUserIdentity = "UserIdentity" TypeVirtualMachine = "VirtualMachine" @@ -36386,6 +36388,1027 @@ func (m *TeamModelMutation) ResetEdge(name string) error { return fmt.Errorf("unknown TeamModel edge %s", name) } +// TeamOIDCConfigMutation represents an operation that mutates the TeamOIDCConfig nodes in the graph. +type TeamOIDCConfigMutation struct { + config + op Op + typ string + id *uuid.UUID + enabled *bool + display_name *string + issuer *string + client_id *string + client_secret_ciphertext *string + scopes *string + email_domain *string + auto_create_member *bool + allow_password_login *bool + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + team *uuid.UUID + clearedteam bool + done bool + oldValue func(context.Context) (*TeamOIDCConfig, error) + predicates []predicate.TeamOIDCConfig +} + +var _ ent.Mutation = (*TeamOIDCConfigMutation)(nil) + +// teamoidcconfigOption allows management of the mutation configuration using functional options. +type teamoidcconfigOption func(*TeamOIDCConfigMutation) + +// newTeamOIDCConfigMutation creates new mutation for the TeamOIDCConfig entity. +func newTeamOIDCConfigMutation(c config, op Op, opts ...teamoidcconfigOption) *TeamOIDCConfigMutation { + m := &TeamOIDCConfigMutation{ + config: c, + op: op, + typ: TypeTeamOIDCConfig, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withTeamOIDCConfigID sets the ID field of the mutation. +func withTeamOIDCConfigID(id uuid.UUID) teamoidcconfigOption { + return func(m *TeamOIDCConfigMutation) { + var ( + err error + once sync.Once + value *TeamOIDCConfig + ) + m.oldValue = func(ctx context.Context) (*TeamOIDCConfig, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().TeamOIDCConfig.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withTeamOIDCConfig sets the old TeamOIDCConfig of the mutation. +func withTeamOIDCConfig(node *TeamOIDCConfig) teamoidcconfigOption { + return func(m *TeamOIDCConfigMutation) { + m.oldValue = func(context.Context) (*TeamOIDCConfig, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m TeamOIDCConfigMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m TeamOIDCConfigMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("db: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of TeamOIDCConfig entities. +func (m *TeamOIDCConfigMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *TeamOIDCConfigMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *TeamOIDCConfigMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().TeamOIDCConfig.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetTeamID sets the "team_id" field. +func (m *TeamOIDCConfigMutation) SetTeamID(u uuid.UUID) { + m.team = &u +} + +// TeamID returns the value of the "team_id" field in the mutation. +func (m *TeamOIDCConfigMutation) TeamID() (r uuid.UUID, exists bool) { + v := m.team + if v == nil { + return + } + return *v, true +} + +// OldTeamID returns the old "team_id" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldTeamID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTeamID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTeamID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTeamID: %w", err) + } + return oldValue.TeamID, nil +} + +// ResetTeamID resets all changes to the "team_id" field. +func (m *TeamOIDCConfigMutation) ResetTeamID() { + m.team = nil +} + +// SetEnabled sets the "enabled" field. +func (m *TeamOIDCConfigMutation) SetEnabled(b bool) { + m.enabled = &b +} + +// Enabled returns the value of the "enabled" field in the mutation. +func (m *TeamOIDCConfigMutation) Enabled() (r bool, exists bool) { + v := m.enabled + if v == nil { + return + } + return *v, true +} + +// OldEnabled returns the old "enabled" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldEnabled(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEnabled is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEnabled requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEnabled: %w", err) + } + return oldValue.Enabled, nil +} + +// ResetEnabled resets all changes to the "enabled" field. +func (m *TeamOIDCConfigMutation) ResetEnabled() { + m.enabled = nil +} + +// SetDisplayName sets the "display_name" field. +func (m *TeamOIDCConfigMutation) SetDisplayName(s string) { + m.display_name = &s +} + +// DisplayName returns the value of the "display_name" field in the mutation. +func (m *TeamOIDCConfigMutation) DisplayName() (r string, exists bool) { + v := m.display_name + if v == nil { + return + } + return *v, true +} + +// OldDisplayName returns the old "display_name" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldDisplayName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDisplayName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDisplayName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDisplayName: %w", err) + } + return oldValue.DisplayName, nil +} + +// ResetDisplayName resets all changes to the "display_name" field. +func (m *TeamOIDCConfigMutation) ResetDisplayName() { + m.display_name = nil +} + +// SetIssuer sets the "issuer" field. +func (m *TeamOIDCConfigMutation) SetIssuer(s string) { + m.issuer = &s +} + +// Issuer returns the value of the "issuer" field in the mutation. +func (m *TeamOIDCConfigMutation) Issuer() (r string, exists bool) { + v := m.issuer + if v == nil { + return + } + return *v, true +} + +// OldIssuer returns the old "issuer" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldIssuer(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldIssuer is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldIssuer requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldIssuer: %w", err) + } + return oldValue.Issuer, nil +} + +// ResetIssuer resets all changes to the "issuer" field. +func (m *TeamOIDCConfigMutation) ResetIssuer() { + m.issuer = nil +} + +// SetClientID sets the "client_id" field. +func (m *TeamOIDCConfigMutation) SetClientID(s string) { + m.client_id = &s +} + +// ClientID returns the value of the "client_id" field in the mutation. +func (m *TeamOIDCConfigMutation) ClientID() (r string, exists bool) { + v := m.client_id + if v == nil { + return + } + return *v, true +} + +// OldClientID returns the old "client_id" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldClientID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldClientID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldClientID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldClientID: %w", err) + } + return oldValue.ClientID, nil +} + +// ResetClientID resets all changes to the "client_id" field. +func (m *TeamOIDCConfigMutation) ResetClientID() { + m.client_id = nil +} + +// SetClientSecretCiphertext sets the "client_secret_ciphertext" field. +func (m *TeamOIDCConfigMutation) SetClientSecretCiphertext(s string) { + m.client_secret_ciphertext = &s +} + +// ClientSecretCiphertext returns the value of the "client_secret_ciphertext" field in the mutation. +func (m *TeamOIDCConfigMutation) ClientSecretCiphertext() (r string, exists bool) { + v := m.client_secret_ciphertext + if v == nil { + return + } + return *v, true +} + +// OldClientSecretCiphertext returns the old "client_secret_ciphertext" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldClientSecretCiphertext(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldClientSecretCiphertext is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldClientSecretCiphertext requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldClientSecretCiphertext: %w", err) + } + return oldValue.ClientSecretCiphertext, nil +} + +// ClearClientSecretCiphertext clears the value of the "client_secret_ciphertext" field. +func (m *TeamOIDCConfigMutation) ClearClientSecretCiphertext() { + m.client_secret_ciphertext = nil + m.clearedFields[teamoidcconfig.FieldClientSecretCiphertext] = struct{}{} +} + +// ClientSecretCiphertextCleared returns if the "client_secret_ciphertext" field was cleared in this mutation. +func (m *TeamOIDCConfigMutation) ClientSecretCiphertextCleared() bool { + _, ok := m.clearedFields[teamoidcconfig.FieldClientSecretCiphertext] + return ok +} + +// ResetClientSecretCiphertext resets all changes to the "client_secret_ciphertext" field. +func (m *TeamOIDCConfigMutation) ResetClientSecretCiphertext() { + m.client_secret_ciphertext = nil + delete(m.clearedFields, teamoidcconfig.FieldClientSecretCiphertext) +} + +// SetScopes sets the "scopes" field. +func (m *TeamOIDCConfigMutation) SetScopes(s string) { + m.scopes = &s +} + +// Scopes returns the value of the "scopes" field in the mutation. +func (m *TeamOIDCConfigMutation) Scopes() (r string, exists bool) { + v := m.scopes + if v == nil { + return + } + return *v, true +} + +// OldScopes returns the old "scopes" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldScopes(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScopes is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScopes requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScopes: %w", err) + } + return oldValue.Scopes, nil +} + +// ResetScopes resets all changes to the "scopes" field. +func (m *TeamOIDCConfigMutation) ResetScopes() { + m.scopes = nil +} + +// SetEmailDomain sets the "email_domain" field. +func (m *TeamOIDCConfigMutation) SetEmailDomain(s string) { + m.email_domain = &s +} + +// EmailDomain returns the value of the "email_domain" field in the mutation. +func (m *TeamOIDCConfigMutation) EmailDomain() (r string, exists bool) { + v := m.email_domain + if v == nil { + return + } + return *v, true +} + +// OldEmailDomain returns the old "email_domain" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldEmailDomain(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEmailDomain is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEmailDomain requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEmailDomain: %w", err) + } + return oldValue.EmailDomain, nil +} + +// ClearEmailDomain clears the value of the "email_domain" field. +func (m *TeamOIDCConfigMutation) ClearEmailDomain() { + m.email_domain = nil + m.clearedFields[teamoidcconfig.FieldEmailDomain] = struct{}{} +} + +// EmailDomainCleared returns if the "email_domain" field was cleared in this mutation. +func (m *TeamOIDCConfigMutation) EmailDomainCleared() bool { + _, ok := m.clearedFields[teamoidcconfig.FieldEmailDomain] + return ok +} + +// ResetEmailDomain resets all changes to the "email_domain" field. +func (m *TeamOIDCConfigMutation) ResetEmailDomain() { + m.email_domain = nil + delete(m.clearedFields, teamoidcconfig.FieldEmailDomain) +} + +// SetAutoCreateMember sets the "auto_create_member" field. +func (m *TeamOIDCConfigMutation) SetAutoCreateMember(b bool) { + m.auto_create_member = &b +} + +// AutoCreateMember returns the value of the "auto_create_member" field in the mutation. +func (m *TeamOIDCConfigMutation) AutoCreateMember() (r bool, exists bool) { + v := m.auto_create_member + if v == nil { + return + } + return *v, true +} + +// OldAutoCreateMember returns the old "auto_create_member" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldAutoCreateMember(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAutoCreateMember is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAutoCreateMember requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAutoCreateMember: %w", err) + } + return oldValue.AutoCreateMember, nil +} + +// ResetAutoCreateMember resets all changes to the "auto_create_member" field. +func (m *TeamOIDCConfigMutation) ResetAutoCreateMember() { + m.auto_create_member = nil +} + +// SetAllowPasswordLogin sets the "allow_password_login" field. +func (m *TeamOIDCConfigMutation) SetAllowPasswordLogin(b bool) { + m.allow_password_login = &b +} + +// AllowPasswordLogin returns the value of the "allow_password_login" field in the mutation. +func (m *TeamOIDCConfigMutation) AllowPasswordLogin() (r bool, exists bool) { + v := m.allow_password_login + if v == nil { + return + } + return *v, true +} + +// OldAllowPasswordLogin returns the old "allow_password_login" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldAllowPasswordLogin(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAllowPasswordLogin is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAllowPasswordLogin requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAllowPasswordLogin: %w", err) + } + return oldValue.AllowPasswordLogin, nil +} + +// ResetAllowPasswordLogin resets all changes to the "allow_password_login" field. +func (m *TeamOIDCConfigMutation) ResetAllowPasswordLogin() { + m.allow_password_login = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *TeamOIDCConfigMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *TeamOIDCConfigMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *TeamOIDCConfigMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *TeamOIDCConfigMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *TeamOIDCConfigMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the TeamOIDCConfig entity. +// If the TeamOIDCConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TeamOIDCConfigMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *TeamOIDCConfigMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// ClearTeam clears the "team" edge to the Team entity. +func (m *TeamOIDCConfigMutation) ClearTeam() { + m.clearedteam = true + m.clearedFields[teamoidcconfig.FieldTeamID] = struct{}{} +} + +// TeamCleared reports if the "team" edge to the Team entity was cleared. +func (m *TeamOIDCConfigMutation) TeamCleared() bool { + return m.clearedteam +} + +// TeamIDs returns the "team" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// TeamID instead. It exists only for internal usage by the builders. +func (m *TeamOIDCConfigMutation) TeamIDs() (ids []uuid.UUID) { + if id := m.team; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetTeam resets all changes to the "team" edge. +func (m *TeamOIDCConfigMutation) ResetTeam() { + m.team = nil + m.clearedteam = false +} + +// Where appends a list predicates to the TeamOIDCConfigMutation builder. +func (m *TeamOIDCConfigMutation) Where(ps ...predicate.TeamOIDCConfig) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the TeamOIDCConfigMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *TeamOIDCConfigMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.TeamOIDCConfig, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *TeamOIDCConfigMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *TeamOIDCConfigMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (TeamOIDCConfig). +func (m *TeamOIDCConfigMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *TeamOIDCConfigMutation) Fields() []string { + fields := make([]string, 0, 12) + if m.team != nil { + fields = append(fields, teamoidcconfig.FieldTeamID) + } + if m.enabled != nil { + fields = append(fields, teamoidcconfig.FieldEnabled) + } + if m.display_name != nil { + fields = append(fields, teamoidcconfig.FieldDisplayName) + } + if m.issuer != nil { + fields = append(fields, teamoidcconfig.FieldIssuer) + } + if m.client_id != nil { + fields = append(fields, teamoidcconfig.FieldClientID) + } + if m.client_secret_ciphertext != nil { + fields = append(fields, teamoidcconfig.FieldClientSecretCiphertext) + } + if m.scopes != nil { + fields = append(fields, teamoidcconfig.FieldScopes) + } + if m.email_domain != nil { + fields = append(fields, teamoidcconfig.FieldEmailDomain) + } + if m.auto_create_member != nil { + fields = append(fields, teamoidcconfig.FieldAutoCreateMember) + } + if m.allow_password_login != nil { + fields = append(fields, teamoidcconfig.FieldAllowPasswordLogin) + } + if m.created_at != nil { + fields = append(fields, teamoidcconfig.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, teamoidcconfig.FieldUpdatedAt) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *TeamOIDCConfigMutation) Field(name string) (ent.Value, bool) { + switch name { + case teamoidcconfig.FieldTeamID: + return m.TeamID() + case teamoidcconfig.FieldEnabled: + return m.Enabled() + case teamoidcconfig.FieldDisplayName: + return m.DisplayName() + case teamoidcconfig.FieldIssuer: + return m.Issuer() + case teamoidcconfig.FieldClientID: + return m.ClientID() + case teamoidcconfig.FieldClientSecretCiphertext: + return m.ClientSecretCiphertext() + case teamoidcconfig.FieldScopes: + return m.Scopes() + case teamoidcconfig.FieldEmailDomain: + return m.EmailDomain() + case teamoidcconfig.FieldAutoCreateMember: + return m.AutoCreateMember() + case teamoidcconfig.FieldAllowPasswordLogin: + return m.AllowPasswordLogin() + case teamoidcconfig.FieldCreatedAt: + return m.CreatedAt() + case teamoidcconfig.FieldUpdatedAt: + return m.UpdatedAt() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *TeamOIDCConfigMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case teamoidcconfig.FieldTeamID: + return m.OldTeamID(ctx) + case teamoidcconfig.FieldEnabled: + return m.OldEnabled(ctx) + case teamoidcconfig.FieldDisplayName: + return m.OldDisplayName(ctx) + case teamoidcconfig.FieldIssuer: + return m.OldIssuer(ctx) + case teamoidcconfig.FieldClientID: + return m.OldClientID(ctx) + case teamoidcconfig.FieldClientSecretCiphertext: + return m.OldClientSecretCiphertext(ctx) + case teamoidcconfig.FieldScopes: + return m.OldScopes(ctx) + case teamoidcconfig.FieldEmailDomain: + return m.OldEmailDomain(ctx) + case teamoidcconfig.FieldAutoCreateMember: + return m.OldAutoCreateMember(ctx) + case teamoidcconfig.FieldAllowPasswordLogin: + return m.OldAllowPasswordLogin(ctx) + case teamoidcconfig.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case teamoidcconfig.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown TeamOIDCConfig field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *TeamOIDCConfigMutation) SetField(name string, value ent.Value) error { + switch name { + case teamoidcconfig.FieldTeamID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTeamID(v) + return nil + case teamoidcconfig.FieldEnabled: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEnabled(v) + return nil + case teamoidcconfig.FieldDisplayName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDisplayName(v) + return nil + case teamoidcconfig.FieldIssuer: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetIssuer(v) + return nil + case teamoidcconfig.FieldClientID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetClientID(v) + return nil + case teamoidcconfig.FieldClientSecretCiphertext: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetClientSecretCiphertext(v) + return nil + case teamoidcconfig.FieldScopes: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScopes(v) + return nil + case teamoidcconfig.FieldEmailDomain: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEmailDomain(v) + return nil + case teamoidcconfig.FieldAutoCreateMember: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAutoCreateMember(v) + return nil + case teamoidcconfig.FieldAllowPasswordLogin: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAllowPasswordLogin(v) + return nil + case teamoidcconfig.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case teamoidcconfig.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown TeamOIDCConfig field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *TeamOIDCConfigMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *TeamOIDCConfigMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *TeamOIDCConfigMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown TeamOIDCConfig numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *TeamOIDCConfigMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(teamoidcconfig.FieldClientSecretCiphertext) { + fields = append(fields, teamoidcconfig.FieldClientSecretCiphertext) + } + if m.FieldCleared(teamoidcconfig.FieldEmailDomain) { + fields = append(fields, teamoidcconfig.FieldEmailDomain) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *TeamOIDCConfigMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *TeamOIDCConfigMutation) ClearField(name string) error { + switch name { + case teamoidcconfig.FieldClientSecretCiphertext: + m.ClearClientSecretCiphertext() + return nil + case teamoidcconfig.FieldEmailDomain: + m.ClearEmailDomain() + return nil + } + return fmt.Errorf("unknown TeamOIDCConfig nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *TeamOIDCConfigMutation) ResetField(name string) error { + switch name { + case teamoidcconfig.FieldTeamID: + m.ResetTeamID() + return nil + case teamoidcconfig.FieldEnabled: + m.ResetEnabled() + return nil + case teamoidcconfig.FieldDisplayName: + m.ResetDisplayName() + return nil + case teamoidcconfig.FieldIssuer: + m.ResetIssuer() + return nil + case teamoidcconfig.FieldClientID: + m.ResetClientID() + return nil + case teamoidcconfig.FieldClientSecretCiphertext: + m.ResetClientSecretCiphertext() + return nil + case teamoidcconfig.FieldScopes: + m.ResetScopes() + return nil + case teamoidcconfig.FieldEmailDomain: + m.ResetEmailDomain() + return nil + case teamoidcconfig.FieldAutoCreateMember: + m.ResetAutoCreateMember() + return nil + case teamoidcconfig.FieldAllowPasswordLogin: + m.ResetAllowPasswordLogin() + return nil + case teamoidcconfig.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case teamoidcconfig.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown TeamOIDCConfig field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *TeamOIDCConfigMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.team != nil { + edges = append(edges, teamoidcconfig.EdgeTeam) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *TeamOIDCConfigMutation) AddedIDs(name string) []ent.Value { + switch name { + case teamoidcconfig.EdgeTeam: + if id := m.team; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *TeamOIDCConfigMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *TeamOIDCConfigMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *TeamOIDCConfigMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedteam { + edges = append(edges, teamoidcconfig.EdgeTeam) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *TeamOIDCConfigMutation) EdgeCleared(name string) bool { + switch name { + case teamoidcconfig.EdgeTeam: + return m.clearedteam + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *TeamOIDCConfigMutation) ClearEdge(name string) error { + switch name { + case teamoidcconfig.EdgeTeam: + m.ClearTeam() + return nil + } + return fmt.Errorf("unknown TeamOIDCConfig unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *TeamOIDCConfigMutation) ResetEdge(name string) error { + switch name { + case teamoidcconfig.EdgeTeam: + m.ResetTeam() + return nil + } + return fmt.Errorf("unknown TeamOIDCConfig edge %s", name) +} + // UserMutation represents an operation that mutates the User nodes in the graph. type UserMutation struct { config diff --git a/backend/db/page.go b/backend/db/page.go index d5eed556..a0f11c2a 100644 --- a/backend/db/page.go +++ b/backend/db/page.go @@ -529,6 +529,20 @@ func (_m *TeamModelQuery) Page(ctx context.Context, page, size int) ([]*TeamMode return rs, &PageInfo{HasNextPage: has, TotalCount: int64(cnt)}, nil } +func (_m *TeamOIDCConfigQuery) Page(ctx context.Context, page, size int) ([]*TeamOIDCConfig, *PageInfo, error) { + cnt, err := _m.Count(ctx) + if err != nil { + return nil, nil, err + } + offset := size * (page - 1) + rs, err := _m.Offset(offset).Limit(size).All(ctx) + if err != nil { + return nil, nil, err + } + has := (page * size) < cnt + return rs, &PageInfo{HasNextPage: has, TotalCount: int64(cnt)}, nil +} + func (_m *UserQuery) Page(ctx context.Context, page, size int) ([]*User, *PageInfo, error) { cnt, err := _m.Count(ctx) if err != nil { diff --git a/backend/db/predicate/predicate.go b/backend/db/predicate/predicate.go index 6764d5bb..b8e431ea 100644 --- a/backend/db/predicate/predicate.go +++ b/backend/db/predicate/predicate.go @@ -117,6 +117,9 @@ type TeamMember func(*sql.Selector) // TeamModel is the predicate function for teammodel builders. type TeamModel func(*sql.Selector) +// TeamOIDCConfig is the predicate function for teamoidcconfig builders. +type TeamOIDCConfig func(*sql.Selector) + // User is the predicate function for user builders. type User func(*sql.Selector) diff --git a/backend/db/runtime/runtime.go b/backend/db/runtime/runtime.go index 3d784fc7..c242efb1 100644 --- a/backend/db/runtime/runtime.go +++ b/backend/db/runtime/runtime.go @@ -43,6 +43,7 @@ import ( "github.com/chaitin/MonkeyCode/backend/db/teamimage" "github.com/chaitin/MonkeyCode/backend/db/teammember" "github.com/chaitin/MonkeyCode/backend/db/teammodel" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" "github.com/chaitin/MonkeyCode/backend/db/user" "github.com/chaitin/MonkeyCode/backend/db/useridentity" "github.com/chaitin/MonkeyCode/backend/db/virtualmachine" @@ -845,6 +846,46 @@ func init() { teammodelDescCreatedAt := teammodelFields[3].Descriptor() // teammodel.DefaultCreatedAt holds the default value on creation for the created_at field. teammodel.DefaultCreatedAt = teammodelDescCreatedAt.Default.(func() time.Time) + teamoidcconfigFields := schema.TeamOIDCConfig{}.Fields() + _ = teamoidcconfigFields + // teamoidcconfigDescEnabled is the schema descriptor for enabled field. + teamoidcconfigDescEnabled := teamoidcconfigFields[2].Descriptor() + // teamoidcconfig.DefaultEnabled holds the default value on creation for the enabled field. + teamoidcconfig.DefaultEnabled = teamoidcconfigDescEnabled.Default.(bool) + // teamoidcconfigDescDisplayName is the schema descriptor for display_name field. + teamoidcconfigDescDisplayName := teamoidcconfigFields[3].Descriptor() + // teamoidcconfig.DefaultDisplayName holds the default value on creation for the display_name field. + teamoidcconfig.DefaultDisplayName = teamoidcconfigDescDisplayName.Default.(string) + // teamoidcconfigDescIssuer is the schema descriptor for issuer field. + teamoidcconfigDescIssuer := teamoidcconfigFields[4].Descriptor() + // teamoidcconfig.IssuerValidator is a validator for the "issuer" field. It is called by the builders before save. + teamoidcconfig.IssuerValidator = teamoidcconfigDescIssuer.Validators[0].(func(string) error) + // teamoidcconfigDescClientID is the schema descriptor for client_id field. + teamoidcconfigDescClientID := teamoidcconfigFields[5].Descriptor() + // teamoidcconfig.ClientIDValidator is a validator for the "client_id" field. It is called by the builders before save. + teamoidcconfig.ClientIDValidator = teamoidcconfigDescClientID.Validators[0].(func(string) error) + // teamoidcconfigDescScopes is the schema descriptor for scopes field. + teamoidcconfigDescScopes := teamoidcconfigFields[7].Descriptor() + // teamoidcconfig.DefaultScopes holds the default value on creation for the scopes field. + teamoidcconfig.DefaultScopes = teamoidcconfigDescScopes.Default.(string) + // teamoidcconfigDescAutoCreateMember is the schema descriptor for auto_create_member field. + teamoidcconfigDescAutoCreateMember := teamoidcconfigFields[9].Descriptor() + // teamoidcconfig.DefaultAutoCreateMember holds the default value on creation for the auto_create_member field. + teamoidcconfig.DefaultAutoCreateMember = teamoidcconfigDescAutoCreateMember.Default.(bool) + // teamoidcconfigDescAllowPasswordLogin is the schema descriptor for allow_password_login field. + teamoidcconfigDescAllowPasswordLogin := teamoidcconfigFields[10].Descriptor() + // teamoidcconfig.DefaultAllowPasswordLogin holds the default value on creation for the allow_password_login field. + teamoidcconfig.DefaultAllowPasswordLogin = teamoidcconfigDescAllowPasswordLogin.Default.(bool) + // teamoidcconfigDescCreatedAt is the schema descriptor for created_at field. + teamoidcconfigDescCreatedAt := teamoidcconfigFields[11].Descriptor() + // teamoidcconfig.DefaultCreatedAt holds the default value on creation for the created_at field. + teamoidcconfig.DefaultCreatedAt = teamoidcconfigDescCreatedAt.Default.(func() time.Time) + // teamoidcconfigDescUpdatedAt is the schema descriptor for updated_at field. + teamoidcconfigDescUpdatedAt := teamoidcconfigFields[12].Descriptor() + // teamoidcconfig.DefaultUpdatedAt holds the default value on creation for the updated_at field. + teamoidcconfig.DefaultUpdatedAt = teamoidcconfigDescUpdatedAt.Default.(func() time.Time) + // teamoidcconfig.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + teamoidcconfig.UpdateDefaultUpdatedAt = teamoidcconfigDescUpdatedAt.UpdateDefault.(func() time.Time) userMixin := schema.User{}.Mixin() userMixinHooks0 := userMixin[0].Hooks() user.Hooks[0] = userMixinHooks0[0] diff --git a/backend/db/teamoidcconfig.go b/backend/db/teamoidcconfig.go new file mode 100644 index 00000000..0e2c8d65 --- /dev/null +++ b/backend/db/teamoidcconfig.go @@ -0,0 +1,259 @@ +// Code generated by ent, DO NOT EDIT. + +package db + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/chaitin/MonkeyCode/backend/db/team" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" + "github.com/google/uuid" +) + +// TeamOIDCConfig is the model entity for the TeamOIDCConfig schema. +type TeamOIDCConfig struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // TeamID holds the value of the "team_id" field. + TeamID uuid.UUID `json:"team_id,omitempty"` + // Enabled holds the value of the "enabled" field. + Enabled bool `json:"enabled,omitempty"` + // DisplayName holds the value of the "display_name" field. + DisplayName string `json:"display_name,omitempty"` + // Issuer holds the value of the "issuer" field. + Issuer string `json:"issuer,omitempty"` + // ClientID holds the value of the "client_id" field. + ClientID string `json:"client_id,omitempty"` + // ClientSecretCiphertext holds the value of the "client_secret_ciphertext" field. + ClientSecretCiphertext string `json:"client_secret_ciphertext,omitempty"` + // Scopes holds the value of the "scopes" field. + Scopes string `json:"scopes,omitempty"` + // EmailDomain holds the value of the "email_domain" field. + EmailDomain string `json:"email_domain,omitempty"` + // AutoCreateMember holds the value of the "auto_create_member" field. + AutoCreateMember bool `json:"auto_create_member,omitempty"` + // AllowPasswordLogin holds the value of the "allow_password_login" field. + AllowPasswordLogin bool `json:"allow_password_login,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the TeamOIDCConfigQuery when eager-loading is set. + Edges TeamOIDCConfigEdges `json:"edges"` + selectValues sql.SelectValues +} + +// TeamOIDCConfigEdges holds the relations/edges for other nodes in the graph. +type TeamOIDCConfigEdges struct { + // Team holds the value of the team edge. + Team *Team `json:"team,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// TeamOrErr returns the Team value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e TeamOIDCConfigEdges) TeamOrErr() (*Team, error) { + if e.Team != nil { + return e.Team, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: team.Label} + } + return nil, &NotLoadedError{edge: "team"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*TeamOIDCConfig) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case teamoidcconfig.FieldEnabled, teamoidcconfig.FieldAutoCreateMember, teamoidcconfig.FieldAllowPasswordLogin: + values[i] = new(sql.NullBool) + case teamoidcconfig.FieldDisplayName, teamoidcconfig.FieldIssuer, teamoidcconfig.FieldClientID, teamoidcconfig.FieldClientSecretCiphertext, teamoidcconfig.FieldScopes, teamoidcconfig.FieldEmailDomain: + values[i] = new(sql.NullString) + case teamoidcconfig.FieldCreatedAt, teamoidcconfig.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case teamoidcconfig.FieldID, teamoidcconfig.FieldTeamID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the TeamOIDCConfig fields. +func (_m *TeamOIDCConfig) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case teamoidcconfig.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case teamoidcconfig.FieldTeamID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field team_id", values[i]) + } else if value != nil { + _m.TeamID = *value + } + case teamoidcconfig.FieldEnabled: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field enabled", values[i]) + } else if value.Valid { + _m.Enabled = value.Bool + } + case teamoidcconfig.FieldDisplayName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field display_name", values[i]) + } else if value.Valid { + _m.DisplayName = value.String + } + case teamoidcconfig.FieldIssuer: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field issuer", values[i]) + } else if value.Valid { + _m.Issuer = value.String + } + case teamoidcconfig.FieldClientID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field client_id", values[i]) + } else if value.Valid { + _m.ClientID = value.String + } + case teamoidcconfig.FieldClientSecretCiphertext: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field client_secret_ciphertext", values[i]) + } else if value.Valid { + _m.ClientSecretCiphertext = value.String + } + case teamoidcconfig.FieldScopes: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scopes", values[i]) + } else if value.Valid { + _m.Scopes = value.String + } + case teamoidcconfig.FieldEmailDomain: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field email_domain", values[i]) + } else if value.Valid { + _m.EmailDomain = value.String + } + case teamoidcconfig.FieldAutoCreateMember: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field auto_create_member", values[i]) + } else if value.Valid { + _m.AutoCreateMember = value.Bool + } + case teamoidcconfig.FieldAllowPasswordLogin: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field allow_password_login", values[i]) + } else if value.Valid { + _m.AllowPasswordLogin = value.Bool + } + case teamoidcconfig.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case teamoidcconfig.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the TeamOIDCConfig. +// This includes values selected through modifiers, order, etc. +func (_m *TeamOIDCConfig) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryTeam queries the "team" edge of the TeamOIDCConfig entity. +func (_m *TeamOIDCConfig) QueryTeam() *TeamQuery { + return NewTeamOIDCConfigClient(_m.config).QueryTeam(_m) +} + +// Update returns a builder for updating this TeamOIDCConfig. +// Note that you need to call TeamOIDCConfig.Unwrap() before calling this method if this TeamOIDCConfig +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *TeamOIDCConfig) Update() *TeamOIDCConfigUpdateOne { + return NewTeamOIDCConfigClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the TeamOIDCConfig entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *TeamOIDCConfig) Unwrap() *TeamOIDCConfig { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("db: TeamOIDCConfig is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *TeamOIDCConfig) String() string { + var builder strings.Builder + builder.WriteString("TeamOIDCConfig(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("team_id=") + builder.WriteString(fmt.Sprintf("%v", _m.TeamID)) + builder.WriteString(", ") + builder.WriteString("enabled=") + builder.WriteString(fmt.Sprintf("%v", _m.Enabled)) + builder.WriteString(", ") + builder.WriteString("display_name=") + builder.WriteString(_m.DisplayName) + builder.WriteString(", ") + builder.WriteString("issuer=") + builder.WriteString(_m.Issuer) + builder.WriteString(", ") + builder.WriteString("client_id=") + builder.WriteString(_m.ClientID) + builder.WriteString(", ") + builder.WriteString("client_secret_ciphertext=") + builder.WriteString(_m.ClientSecretCiphertext) + builder.WriteString(", ") + builder.WriteString("scopes=") + builder.WriteString(_m.Scopes) + builder.WriteString(", ") + builder.WriteString("email_domain=") + builder.WriteString(_m.EmailDomain) + builder.WriteString(", ") + builder.WriteString("auto_create_member=") + builder.WriteString(fmt.Sprintf("%v", _m.AutoCreateMember)) + builder.WriteString(", ") + builder.WriteString("allow_password_login=") + builder.WriteString(fmt.Sprintf("%v", _m.AllowPasswordLogin)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// TeamOIDCConfigs is a parsable slice of TeamOIDCConfig. +type TeamOIDCConfigs []*TeamOIDCConfig diff --git a/backend/db/teamoidcconfig/teamoidcconfig.go b/backend/db/teamoidcconfig/teamoidcconfig.go new file mode 100644 index 00000000..5de65710 --- /dev/null +++ b/backend/db/teamoidcconfig/teamoidcconfig.go @@ -0,0 +1,184 @@ +// Code generated by ent, DO NOT EDIT. + +package teamoidcconfig + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the teamoidcconfig type in the database. + Label = "team_oidc_config" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldTeamID holds the string denoting the team_id field in the database. + FieldTeamID = "team_id" + // FieldEnabled holds the string denoting the enabled field in the database. + FieldEnabled = "enabled" + // FieldDisplayName holds the string denoting the display_name field in the database. + FieldDisplayName = "display_name" + // FieldIssuer holds the string denoting the issuer field in the database. + FieldIssuer = "issuer" + // FieldClientID holds the string denoting the client_id field in the database. + FieldClientID = "client_id" + // FieldClientSecretCiphertext holds the string denoting the client_secret_ciphertext field in the database. + FieldClientSecretCiphertext = "client_secret_ciphertext" + // FieldScopes holds the string denoting the scopes field in the database. + FieldScopes = "scopes" + // FieldEmailDomain holds the string denoting the email_domain field in the database. + FieldEmailDomain = "email_domain" + // FieldAutoCreateMember holds the string denoting the auto_create_member field in the database. + FieldAutoCreateMember = "auto_create_member" + // FieldAllowPasswordLogin holds the string denoting the allow_password_login field in the database. + FieldAllowPasswordLogin = "allow_password_login" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // EdgeTeam holds the string denoting the team edge name in mutations. + EdgeTeam = "team" + // Table holds the table name of the teamoidcconfig in the database. + Table = "team_oidc_configs" + // TeamTable is the table that holds the team relation/edge. + TeamTable = "team_oidc_configs" + // TeamInverseTable is the table name for the Team entity. + // It exists in this package in order to avoid circular dependency with the "team" package. + TeamInverseTable = "teams" + // TeamColumn is the table column denoting the team relation/edge. + TeamColumn = "team_id" +) + +// Columns holds all SQL columns for teamoidcconfig fields. +var Columns = []string{ + FieldID, + FieldTeamID, + FieldEnabled, + FieldDisplayName, + FieldIssuer, + FieldClientID, + FieldClientSecretCiphertext, + FieldScopes, + FieldEmailDomain, + FieldAutoCreateMember, + FieldAllowPasswordLogin, + FieldCreatedAt, + FieldUpdatedAt, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultEnabled holds the default value on creation for the "enabled" field. + DefaultEnabled bool + // DefaultDisplayName holds the default value on creation for the "display_name" field. + DefaultDisplayName string + // IssuerValidator is a validator for the "issuer" field. It is called by the builders before save. + IssuerValidator func(string) error + // ClientIDValidator is a validator for the "client_id" field. It is called by the builders before save. + ClientIDValidator func(string) error + // DefaultScopes holds the default value on creation for the "scopes" field. + DefaultScopes string + // DefaultAutoCreateMember holds the default value on creation for the "auto_create_member" field. + DefaultAutoCreateMember bool + // DefaultAllowPasswordLogin holds the default value on creation for the "allow_password_login" field. + DefaultAllowPasswordLogin bool + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time +) + +// OrderOption defines the ordering options for the TeamOIDCConfig queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByTeamID orders the results by the team_id field. +func ByTeamID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTeamID, opts...).ToFunc() +} + +// ByEnabled orders the results by the enabled field. +func ByEnabled(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEnabled, opts...).ToFunc() +} + +// ByDisplayName orders the results by the display_name field. +func ByDisplayName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDisplayName, opts...).ToFunc() +} + +// ByIssuer orders the results by the issuer field. +func ByIssuer(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldIssuer, opts...).ToFunc() +} + +// ByClientID orders the results by the client_id field. +func ByClientID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldClientID, opts...).ToFunc() +} + +// ByClientSecretCiphertext orders the results by the client_secret_ciphertext field. +func ByClientSecretCiphertext(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldClientSecretCiphertext, opts...).ToFunc() +} + +// ByScopes orders the results by the scopes field. +func ByScopes(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScopes, opts...).ToFunc() +} + +// ByEmailDomain orders the results by the email_domain field. +func ByEmailDomain(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEmailDomain, opts...).ToFunc() +} + +// ByAutoCreateMember orders the results by the auto_create_member field. +func ByAutoCreateMember(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAutoCreateMember, opts...).ToFunc() +} + +// ByAllowPasswordLogin orders the results by the allow_password_login field. +func ByAllowPasswordLogin(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAllowPasswordLogin, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByTeamField orders the results by team field. +func ByTeamField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newTeamStep(), sql.OrderByField(field, opts...)) + } +} +func newTeamStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(TeamInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, TeamTable, TeamColumn), + ) +} diff --git a/backend/db/teamoidcconfig/where.go b/backend/db/teamoidcconfig/where.go new file mode 100644 index 00000000..59645189 --- /dev/null +++ b/backend/db/teamoidcconfig/where.go @@ -0,0 +1,695 @@ +// Code generated by ent, DO NOT EDIT. + +package teamoidcconfig + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/chaitin/MonkeyCode/backend/db/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLTE(FieldID, id)) +} + +// TeamID applies equality check predicate on the "team_id" field. It's identical to TeamIDEQ. +func TeamID(v uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldTeamID, v)) +} + +// Enabled applies equality check predicate on the "enabled" field. It's identical to EnabledEQ. +func Enabled(v bool) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldEnabled, v)) +} + +// DisplayName applies equality check predicate on the "display_name" field. It's identical to DisplayNameEQ. +func DisplayName(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldDisplayName, v)) +} + +// Issuer applies equality check predicate on the "issuer" field. It's identical to IssuerEQ. +func Issuer(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldIssuer, v)) +} + +// ClientID applies equality check predicate on the "client_id" field. It's identical to ClientIDEQ. +func ClientID(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldClientID, v)) +} + +// ClientSecretCiphertext applies equality check predicate on the "client_secret_ciphertext" field. It's identical to ClientSecretCiphertextEQ. +func ClientSecretCiphertext(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldClientSecretCiphertext, v)) +} + +// Scopes applies equality check predicate on the "scopes" field. It's identical to ScopesEQ. +func Scopes(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldScopes, v)) +} + +// EmailDomain applies equality check predicate on the "email_domain" field. It's identical to EmailDomainEQ. +func EmailDomain(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldEmailDomain, v)) +} + +// AutoCreateMember applies equality check predicate on the "auto_create_member" field. It's identical to AutoCreateMemberEQ. +func AutoCreateMember(v bool) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldAutoCreateMember, v)) +} + +// AllowPasswordLogin applies equality check predicate on the "allow_password_login" field. It's identical to AllowPasswordLoginEQ. +func AllowPasswordLogin(v bool) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldAllowPasswordLogin, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// TeamIDEQ applies the EQ predicate on the "team_id" field. +func TeamIDEQ(v uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldTeamID, v)) +} + +// TeamIDNEQ applies the NEQ predicate on the "team_id" field. +func TeamIDNEQ(v uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldTeamID, v)) +} + +// TeamIDIn applies the In predicate on the "team_id" field. +func TeamIDIn(vs ...uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIn(FieldTeamID, vs...)) +} + +// TeamIDNotIn applies the NotIn predicate on the "team_id" field. +func TeamIDNotIn(vs ...uuid.UUID) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotIn(FieldTeamID, vs...)) +} + +// EnabledEQ applies the EQ predicate on the "enabled" field. +func EnabledEQ(v bool) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldEnabled, v)) +} + +// EnabledNEQ applies the NEQ predicate on the "enabled" field. +func EnabledNEQ(v bool) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldEnabled, v)) +} + +// DisplayNameEQ applies the EQ predicate on the "display_name" field. +func DisplayNameEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldDisplayName, v)) +} + +// DisplayNameNEQ applies the NEQ predicate on the "display_name" field. +func DisplayNameNEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldDisplayName, v)) +} + +// DisplayNameIn applies the In predicate on the "display_name" field. +func DisplayNameIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIn(FieldDisplayName, vs...)) +} + +// DisplayNameNotIn applies the NotIn predicate on the "display_name" field. +func DisplayNameNotIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotIn(FieldDisplayName, vs...)) +} + +// DisplayNameGT applies the GT predicate on the "display_name" field. +func DisplayNameGT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGT(FieldDisplayName, v)) +} + +// DisplayNameGTE applies the GTE predicate on the "display_name" field. +func DisplayNameGTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGTE(FieldDisplayName, v)) +} + +// DisplayNameLT applies the LT predicate on the "display_name" field. +func DisplayNameLT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLT(FieldDisplayName, v)) +} + +// DisplayNameLTE applies the LTE predicate on the "display_name" field. +func DisplayNameLTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLTE(FieldDisplayName, v)) +} + +// DisplayNameContains applies the Contains predicate on the "display_name" field. +func DisplayNameContains(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContains(FieldDisplayName, v)) +} + +// DisplayNameHasPrefix applies the HasPrefix predicate on the "display_name" field. +func DisplayNameHasPrefix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasPrefix(FieldDisplayName, v)) +} + +// DisplayNameHasSuffix applies the HasSuffix predicate on the "display_name" field. +func DisplayNameHasSuffix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasSuffix(FieldDisplayName, v)) +} + +// DisplayNameEqualFold applies the EqualFold predicate on the "display_name" field. +func DisplayNameEqualFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEqualFold(FieldDisplayName, v)) +} + +// DisplayNameContainsFold applies the ContainsFold predicate on the "display_name" field. +func DisplayNameContainsFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContainsFold(FieldDisplayName, v)) +} + +// IssuerEQ applies the EQ predicate on the "issuer" field. +func IssuerEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldIssuer, v)) +} + +// IssuerNEQ applies the NEQ predicate on the "issuer" field. +func IssuerNEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldIssuer, v)) +} + +// IssuerIn applies the In predicate on the "issuer" field. +func IssuerIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIn(FieldIssuer, vs...)) +} + +// IssuerNotIn applies the NotIn predicate on the "issuer" field. +func IssuerNotIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotIn(FieldIssuer, vs...)) +} + +// IssuerGT applies the GT predicate on the "issuer" field. +func IssuerGT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGT(FieldIssuer, v)) +} + +// IssuerGTE applies the GTE predicate on the "issuer" field. +func IssuerGTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGTE(FieldIssuer, v)) +} + +// IssuerLT applies the LT predicate on the "issuer" field. +func IssuerLT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLT(FieldIssuer, v)) +} + +// IssuerLTE applies the LTE predicate on the "issuer" field. +func IssuerLTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLTE(FieldIssuer, v)) +} + +// IssuerContains applies the Contains predicate on the "issuer" field. +func IssuerContains(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContains(FieldIssuer, v)) +} + +// IssuerHasPrefix applies the HasPrefix predicate on the "issuer" field. +func IssuerHasPrefix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasPrefix(FieldIssuer, v)) +} + +// IssuerHasSuffix applies the HasSuffix predicate on the "issuer" field. +func IssuerHasSuffix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasSuffix(FieldIssuer, v)) +} + +// IssuerEqualFold applies the EqualFold predicate on the "issuer" field. +func IssuerEqualFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEqualFold(FieldIssuer, v)) +} + +// IssuerContainsFold applies the ContainsFold predicate on the "issuer" field. +func IssuerContainsFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContainsFold(FieldIssuer, v)) +} + +// ClientIDEQ applies the EQ predicate on the "client_id" field. +func ClientIDEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldClientID, v)) +} + +// ClientIDNEQ applies the NEQ predicate on the "client_id" field. +func ClientIDNEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldClientID, v)) +} + +// ClientIDIn applies the In predicate on the "client_id" field. +func ClientIDIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIn(FieldClientID, vs...)) +} + +// ClientIDNotIn applies the NotIn predicate on the "client_id" field. +func ClientIDNotIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotIn(FieldClientID, vs...)) +} + +// ClientIDGT applies the GT predicate on the "client_id" field. +func ClientIDGT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGT(FieldClientID, v)) +} + +// ClientIDGTE applies the GTE predicate on the "client_id" field. +func ClientIDGTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGTE(FieldClientID, v)) +} + +// ClientIDLT applies the LT predicate on the "client_id" field. +func ClientIDLT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLT(FieldClientID, v)) +} + +// ClientIDLTE applies the LTE predicate on the "client_id" field. +func ClientIDLTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLTE(FieldClientID, v)) +} + +// ClientIDContains applies the Contains predicate on the "client_id" field. +func ClientIDContains(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContains(FieldClientID, v)) +} + +// ClientIDHasPrefix applies the HasPrefix predicate on the "client_id" field. +func ClientIDHasPrefix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasPrefix(FieldClientID, v)) +} + +// ClientIDHasSuffix applies the HasSuffix predicate on the "client_id" field. +func ClientIDHasSuffix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasSuffix(FieldClientID, v)) +} + +// ClientIDEqualFold applies the EqualFold predicate on the "client_id" field. +func ClientIDEqualFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEqualFold(FieldClientID, v)) +} + +// ClientIDContainsFold applies the ContainsFold predicate on the "client_id" field. +func ClientIDContainsFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContainsFold(FieldClientID, v)) +} + +// ClientSecretCiphertextEQ applies the EQ predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldClientSecretCiphertext, v)) +} + +// ClientSecretCiphertextNEQ applies the NEQ predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextNEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldClientSecretCiphertext, v)) +} + +// ClientSecretCiphertextIn applies the In predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIn(FieldClientSecretCiphertext, vs...)) +} + +// ClientSecretCiphertextNotIn applies the NotIn predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextNotIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotIn(FieldClientSecretCiphertext, vs...)) +} + +// ClientSecretCiphertextGT applies the GT predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextGT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGT(FieldClientSecretCiphertext, v)) +} + +// ClientSecretCiphertextGTE applies the GTE predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextGTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGTE(FieldClientSecretCiphertext, v)) +} + +// ClientSecretCiphertextLT applies the LT predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextLT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLT(FieldClientSecretCiphertext, v)) +} + +// ClientSecretCiphertextLTE applies the LTE predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextLTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLTE(FieldClientSecretCiphertext, v)) +} + +// ClientSecretCiphertextContains applies the Contains predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextContains(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContains(FieldClientSecretCiphertext, v)) +} + +// ClientSecretCiphertextHasPrefix applies the HasPrefix predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextHasPrefix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasPrefix(FieldClientSecretCiphertext, v)) +} + +// ClientSecretCiphertextHasSuffix applies the HasSuffix predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextHasSuffix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasSuffix(FieldClientSecretCiphertext, v)) +} + +// ClientSecretCiphertextIsNil applies the IsNil predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextIsNil() predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIsNull(FieldClientSecretCiphertext)) +} + +// ClientSecretCiphertextNotNil applies the NotNil predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextNotNil() predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotNull(FieldClientSecretCiphertext)) +} + +// ClientSecretCiphertextEqualFold applies the EqualFold predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextEqualFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEqualFold(FieldClientSecretCiphertext, v)) +} + +// ClientSecretCiphertextContainsFold applies the ContainsFold predicate on the "client_secret_ciphertext" field. +func ClientSecretCiphertextContainsFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContainsFold(FieldClientSecretCiphertext, v)) +} + +// ScopesEQ applies the EQ predicate on the "scopes" field. +func ScopesEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldScopes, v)) +} + +// ScopesNEQ applies the NEQ predicate on the "scopes" field. +func ScopesNEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldScopes, v)) +} + +// ScopesIn applies the In predicate on the "scopes" field. +func ScopesIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIn(FieldScopes, vs...)) +} + +// ScopesNotIn applies the NotIn predicate on the "scopes" field. +func ScopesNotIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotIn(FieldScopes, vs...)) +} + +// ScopesGT applies the GT predicate on the "scopes" field. +func ScopesGT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGT(FieldScopes, v)) +} + +// ScopesGTE applies the GTE predicate on the "scopes" field. +func ScopesGTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGTE(FieldScopes, v)) +} + +// ScopesLT applies the LT predicate on the "scopes" field. +func ScopesLT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLT(FieldScopes, v)) +} + +// ScopesLTE applies the LTE predicate on the "scopes" field. +func ScopesLTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLTE(FieldScopes, v)) +} + +// ScopesContains applies the Contains predicate on the "scopes" field. +func ScopesContains(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContains(FieldScopes, v)) +} + +// ScopesHasPrefix applies the HasPrefix predicate on the "scopes" field. +func ScopesHasPrefix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasPrefix(FieldScopes, v)) +} + +// ScopesHasSuffix applies the HasSuffix predicate on the "scopes" field. +func ScopesHasSuffix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasSuffix(FieldScopes, v)) +} + +// ScopesEqualFold applies the EqualFold predicate on the "scopes" field. +func ScopesEqualFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEqualFold(FieldScopes, v)) +} + +// ScopesContainsFold applies the ContainsFold predicate on the "scopes" field. +func ScopesContainsFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContainsFold(FieldScopes, v)) +} + +// EmailDomainEQ applies the EQ predicate on the "email_domain" field. +func EmailDomainEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldEmailDomain, v)) +} + +// EmailDomainNEQ applies the NEQ predicate on the "email_domain" field. +func EmailDomainNEQ(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldEmailDomain, v)) +} + +// EmailDomainIn applies the In predicate on the "email_domain" field. +func EmailDomainIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIn(FieldEmailDomain, vs...)) +} + +// EmailDomainNotIn applies the NotIn predicate on the "email_domain" field. +func EmailDomainNotIn(vs ...string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotIn(FieldEmailDomain, vs...)) +} + +// EmailDomainGT applies the GT predicate on the "email_domain" field. +func EmailDomainGT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGT(FieldEmailDomain, v)) +} + +// EmailDomainGTE applies the GTE predicate on the "email_domain" field. +func EmailDomainGTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGTE(FieldEmailDomain, v)) +} + +// EmailDomainLT applies the LT predicate on the "email_domain" field. +func EmailDomainLT(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLT(FieldEmailDomain, v)) +} + +// EmailDomainLTE applies the LTE predicate on the "email_domain" field. +func EmailDomainLTE(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLTE(FieldEmailDomain, v)) +} + +// EmailDomainContains applies the Contains predicate on the "email_domain" field. +func EmailDomainContains(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContains(FieldEmailDomain, v)) +} + +// EmailDomainHasPrefix applies the HasPrefix predicate on the "email_domain" field. +func EmailDomainHasPrefix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasPrefix(FieldEmailDomain, v)) +} + +// EmailDomainHasSuffix applies the HasSuffix predicate on the "email_domain" field. +func EmailDomainHasSuffix(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldHasSuffix(FieldEmailDomain, v)) +} + +// EmailDomainIsNil applies the IsNil predicate on the "email_domain" field. +func EmailDomainIsNil() predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIsNull(FieldEmailDomain)) +} + +// EmailDomainNotNil applies the NotNil predicate on the "email_domain" field. +func EmailDomainNotNil() predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotNull(FieldEmailDomain)) +} + +// EmailDomainEqualFold applies the EqualFold predicate on the "email_domain" field. +func EmailDomainEqualFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEqualFold(FieldEmailDomain, v)) +} + +// EmailDomainContainsFold applies the ContainsFold predicate on the "email_domain" field. +func EmailDomainContainsFold(v string) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldContainsFold(FieldEmailDomain, v)) +} + +// AutoCreateMemberEQ applies the EQ predicate on the "auto_create_member" field. +func AutoCreateMemberEQ(v bool) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldAutoCreateMember, v)) +} + +// AutoCreateMemberNEQ applies the NEQ predicate on the "auto_create_member" field. +func AutoCreateMemberNEQ(v bool) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldAutoCreateMember, v)) +} + +// AllowPasswordLoginEQ applies the EQ predicate on the "allow_password_login" field. +func AllowPasswordLoginEQ(v bool) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldAllowPasswordLogin, v)) +} + +// AllowPasswordLoginNEQ applies the NEQ predicate on the "allow_password_login" field. +func AllowPasswordLoginNEQ(v bool) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldAllowPasswordLogin, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// HasTeam applies the HasEdge predicate on the "team" edge. +func HasTeam() predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, TeamTable, TeamColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasTeamWith applies the HasEdge predicate on the "team" edge with a given conditions (other predicates). +func HasTeamWith(preds ...predicate.Team) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(func(s *sql.Selector) { + step := newTeamStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.TeamOIDCConfig) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.TeamOIDCConfig) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.TeamOIDCConfig) predicate.TeamOIDCConfig { + return predicate.TeamOIDCConfig(sql.NotPredicates(p)) +} diff --git a/backend/db/teamoidcconfig_create.go b/backend/db/teamoidcconfig_create.go new file mode 100644 index 00000000..e58b9502 --- /dev/null +++ b/backend/db/teamoidcconfig_create.go @@ -0,0 +1,1246 @@ +// Code generated by ent, DO NOT EDIT. + +package db + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chaitin/MonkeyCode/backend/db/team" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" + "github.com/google/uuid" +) + +// TeamOIDCConfigCreate is the builder for creating a TeamOIDCConfig entity. +type TeamOIDCConfigCreate struct { + config + mutation *TeamOIDCConfigMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetTeamID sets the "team_id" field. +func (_c *TeamOIDCConfigCreate) SetTeamID(v uuid.UUID) *TeamOIDCConfigCreate { + _c.mutation.SetTeamID(v) + return _c +} + +// SetEnabled sets the "enabled" field. +func (_c *TeamOIDCConfigCreate) SetEnabled(v bool) *TeamOIDCConfigCreate { + _c.mutation.SetEnabled(v) + return _c +} + +// SetNillableEnabled sets the "enabled" field if the given value is not nil. +func (_c *TeamOIDCConfigCreate) SetNillableEnabled(v *bool) *TeamOIDCConfigCreate { + if v != nil { + _c.SetEnabled(*v) + } + return _c +} + +// SetDisplayName sets the "display_name" field. +func (_c *TeamOIDCConfigCreate) SetDisplayName(v string) *TeamOIDCConfigCreate { + _c.mutation.SetDisplayName(v) + return _c +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_c *TeamOIDCConfigCreate) SetNillableDisplayName(v *string) *TeamOIDCConfigCreate { + if v != nil { + _c.SetDisplayName(*v) + } + return _c +} + +// SetIssuer sets the "issuer" field. +func (_c *TeamOIDCConfigCreate) SetIssuer(v string) *TeamOIDCConfigCreate { + _c.mutation.SetIssuer(v) + return _c +} + +// SetClientID sets the "client_id" field. +func (_c *TeamOIDCConfigCreate) SetClientID(v string) *TeamOIDCConfigCreate { + _c.mutation.SetClientID(v) + return _c +} + +// SetClientSecretCiphertext sets the "client_secret_ciphertext" field. +func (_c *TeamOIDCConfigCreate) SetClientSecretCiphertext(v string) *TeamOIDCConfigCreate { + _c.mutation.SetClientSecretCiphertext(v) + return _c +} + +// SetNillableClientSecretCiphertext sets the "client_secret_ciphertext" field if the given value is not nil. +func (_c *TeamOIDCConfigCreate) SetNillableClientSecretCiphertext(v *string) *TeamOIDCConfigCreate { + if v != nil { + _c.SetClientSecretCiphertext(*v) + } + return _c +} + +// SetScopes sets the "scopes" field. +func (_c *TeamOIDCConfigCreate) SetScopes(v string) *TeamOIDCConfigCreate { + _c.mutation.SetScopes(v) + return _c +} + +// SetNillableScopes sets the "scopes" field if the given value is not nil. +func (_c *TeamOIDCConfigCreate) SetNillableScopes(v *string) *TeamOIDCConfigCreate { + if v != nil { + _c.SetScopes(*v) + } + return _c +} + +// SetEmailDomain sets the "email_domain" field. +func (_c *TeamOIDCConfigCreate) SetEmailDomain(v string) *TeamOIDCConfigCreate { + _c.mutation.SetEmailDomain(v) + return _c +} + +// SetNillableEmailDomain sets the "email_domain" field if the given value is not nil. +func (_c *TeamOIDCConfigCreate) SetNillableEmailDomain(v *string) *TeamOIDCConfigCreate { + if v != nil { + _c.SetEmailDomain(*v) + } + return _c +} + +// SetAutoCreateMember sets the "auto_create_member" field. +func (_c *TeamOIDCConfigCreate) SetAutoCreateMember(v bool) *TeamOIDCConfigCreate { + _c.mutation.SetAutoCreateMember(v) + return _c +} + +// SetNillableAutoCreateMember sets the "auto_create_member" field if the given value is not nil. +func (_c *TeamOIDCConfigCreate) SetNillableAutoCreateMember(v *bool) *TeamOIDCConfigCreate { + if v != nil { + _c.SetAutoCreateMember(*v) + } + return _c +} + +// SetAllowPasswordLogin sets the "allow_password_login" field. +func (_c *TeamOIDCConfigCreate) SetAllowPasswordLogin(v bool) *TeamOIDCConfigCreate { + _c.mutation.SetAllowPasswordLogin(v) + return _c +} + +// SetNillableAllowPasswordLogin sets the "allow_password_login" field if the given value is not nil. +func (_c *TeamOIDCConfigCreate) SetNillableAllowPasswordLogin(v *bool) *TeamOIDCConfigCreate { + if v != nil { + _c.SetAllowPasswordLogin(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *TeamOIDCConfigCreate) SetCreatedAt(v time.Time) *TeamOIDCConfigCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *TeamOIDCConfigCreate) SetNillableCreatedAt(v *time.Time) *TeamOIDCConfigCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *TeamOIDCConfigCreate) SetUpdatedAt(v time.Time) *TeamOIDCConfigCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *TeamOIDCConfigCreate) SetNillableUpdatedAt(v *time.Time) *TeamOIDCConfigCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *TeamOIDCConfigCreate) SetID(v uuid.UUID) *TeamOIDCConfigCreate { + _c.mutation.SetID(v) + return _c +} + +// SetTeam sets the "team" edge to the Team entity. +func (_c *TeamOIDCConfigCreate) SetTeam(v *Team) *TeamOIDCConfigCreate { + return _c.SetTeamID(v.ID) +} + +// Mutation returns the TeamOIDCConfigMutation object of the builder. +func (_c *TeamOIDCConfigCreate) Mutation() *TeamOIDCConfigMutation { + return _c.mutation +} + +// Save creates the TeamOIDCConfig in the database. +func (_c *TeamOIDCConfigCreate) Save(ctx context.Context) (*TeamOIDCConfig, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *TeamOIDCConfigCreate) SaveX(ctx context.Context) *TeamOIDCConfig { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *TeamOIDCConfigCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *TeamOIDCConfigCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *TeamOIDCConfigCreate) defaults() { + if _, ok := _c.mutation.Enabled(); !ok { + v := teamoidcconfig.DefaultEnabled + _c.mutation.SetEnabled(v) + } + if _, ok := _c.mutation.DisplayName(); !ok { + v := teamoidcconfig.DefaultDisplayName + _c.mutation.SetDisplayName(v) + } + if _, ok := _c.mutation.Scopes(); !ok { + v := teamoidcconfig.DefaultScopes + _c.mutation.SetScopes(v) + } + if _, ok := _c.mutation.AutoCreateMember(); !ok { + v := teamoidcconfig.DefaultAutoCreateMember + _c.mutation.SetAutoCreateMember(v) + } + if _, ok := _c.mutation.AllowPasswordLogin(); !ok { + v := teamoidcconfig.DefaultAllowPasswordLogin + _c.mutation.SetAllowPasswordLogin(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := teamoidcconfig.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := teamoidcconfig.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *TeamOIDCConfigCreate) check() error { + if _, ok := _c.mutation.TeamID(); !ok { + return &ValidationError{Name: "team_id", err: errors.New(`db: missing required field "TeamOIDCConfig.team_id"`)} + } + if _, ok := _c.mutation.Enabled(); !ok { + return &ValidationError{Name: "enabled", err: errors.New(`db: missing required field "TeamOIDCConfig.enabled"`)} + } + if _, ok := _c.mutation.DisplayName(); !ok { + return &ValidationError{Name: "display_name", err: errors.New(`db: missing required field "TeamOIDCConfig.display_name"`)} + } + if _, ok := _c.mutation.Issuer(); !ok { + return &ValidationError{Name: "issuer", err: errors.New(`db: missing required field "TeamOIDCConfig.issuer"`)} + } + if v, ok := _c.mutation.Issuer(); ok { + if err := teamoidcconfig.IssuerValidator(v); err != nil { + return &ValidationError{Name: "issuer", err: fmt.Errorf(`db: validator failed for field "TeamOIDCConfig.issuer": %w`, err)} + } + } + if _, ok := _c.mutation.ClientID(); !ok { + return &ValidationError{Name: "client_id", err: errors.New(`db: missing required field "TeamOIDCConfig.client_id"`)} + } + if v, ok := _c.mutation.ClientID(); ok { + if err := teamoidcconfig.ClientIDValidator(v); err != nil { + return &ValidationError{Name: "client_id", err: fmt.Errorf(`db: validator failed for field "TeamOIDCConfig.client_id": %w`, err)} + } + } + if _, ok := _c.mutation.Scopes(); !ok { + return &ValidationError{Name: "scopes", err: errors.New(`db: missing required field "TeamOIDCConfig.scopes"`)} + } + if _, ok := _c.mutation.AutoCreateMember(); !ok { + return &ValidationError{Name: "auto_create_member", err: errors.New(`db: missing required field "TeamOIDCConfig.auto_create_member"`)} + } + if _, ok := _c.mutation.AllowPasswordLogin(); !ok { + return &ValidationError{Name: "allow_password_login", err: errors.New(`db: missing required field "TeamOIDCConfig.allow_password_login"`)} + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`db: missing required field "TeamOIDCConfig.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`db: missing required field "TeamOIDCConfig.updated_at"`)} + } + if len(_c.mutation.TeamIDs()) == 0 { + return &ValidationError{Name: "team", err: errors.New(`db: missing required edge "TeamOIDCConfig.team"`)} + } + return nil +} + +func (_c *TeamOIDCConfigCreate) sqlSave(ctx context.Context) (*TeamOIDCConfig, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *TeamOIDCConfigCreate) createSpec() (*TeamOIDCConfig, *sqlgraph.CreateSpec) { + var ( + _node = &TeamOIDCConfig{config: _c.config} + _spec = sqlgraph.NewCreateSpec(teamoidcconfig.Table, sqlgraph.NewFieldSpec(teamoidcconfig.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Enabled(); ok { + _spec.SetField(teamoidcconfig.FieldEnabled, field.TypeBool, value) + _node.Enabled = value + } + if value, ok := _c.mutation.DisplayName(); ok { + _spec.SetField(teamoidcconfig.FieldDisplayName, field.TypeString, value) + _node.DisplayName = value + } + if value, ok := _c.mutation.Issuer(); ok { + _spec.SetField(teamoidcconfig.FieldIssuer, field.TypeString, value) + _node.Issuer = value + } + if value, ok := _c.mutation.ClientID(); ok { + _spec.SetField(teamoidcconfig.FieldClientID, field.TypeString, value) + _node.ClientID = value + } + if value, ok := _c.mutation.ClientSecretCiphertext(); ok { + _spec.SetField(teamoidcconfig.FieldClientSecretCiphertext, field.TypeString, value) + _node.ClientSecretCiphertext = value + } + if value, ok := _c.mutation.Scopes(); ok { + _spec.SetField(teamoidcconfig.FieldScopes, field.TypeString, value) + _node.Scopes = value + } + if value, ok := _c.mutation.EmailDomain(); ok { + _spec.SetField(teamoidcconfig.FieldEmailDomain, field.TypeString, value) + _node.EmailDomain = value + } + if value, ok := _c.mutation.AutoCreateMember(); ok { + _spec.SetField(teamoidcconfig.FieldAutoCreateMember, field.TypeBool, value) + _node.AutoCreateMember = value + } + if value, ok := _c.mutation.AllowPasswordLogin(); ok { + _spec.SetField(teamoidcconfig.FieldAllowPasswordLogin, field.TypeBool, value) + _node.AllowPasswordLogin = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(teamoidcconfig.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(teamoidcconfig.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if nodes := _c.mutation.TeamIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: teamoidcconfig.TeamTable, + Columns: []string{teamoidcconfig.TeamColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(team.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.TeamID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.TeamOIDCConfig.Create(). +// SetTeamID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.TeamOIDCConfigUpsert) { +// SetTeamID(v+v). +// }). +// Exec(ctx) +func (_c *TeamOIDCConfigCreate) OnConflict(opts ...sql.ConflictOption) *TeamOIDCConfigUpsertOne { + _c.conflict = opts + return &TeamOIDCConfigUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.TeamOIDCConfig.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *TeamOIDCConfigCreate) OnConflictColumns(columns ...string) *TeamOIDCConfigUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &TeamOIDCConfigUpsertOne{ + create: _c, + } +} + +type ( + // TeamOIDCConfigUpsertOne is the builder for "upsert"-ing + // one TeamOIDCConfig node. + TeamOIDCConfigUpsertOne struct { + create *TeamOIDCConfigCreate + } + + // TeamOIDCConfigUpsert is the "OnConflict" setter. + TeamOIDCConfigUpsert struct { + *sql.UpdateSet + } +) + +// SetTeamID sets the "team_id" field. +func (u *TeamOIDCConfigUpsert) SetTeamID(v uuid.UUID) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldTeamID, v) + return u +} + +// UpdateTeamID sets the "team_id" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateTeamID() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldTeamID) + return u +} + +// SetEnabled sets the "enabled" field. +func (u *TeamOIDCConfigUpsert) SetEnabled(v bool) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldEnabled, v) + return u +} + +// UpdateEnabled sets the "enabled" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateEnabled() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldEnabled) + return u +} + +// SetDisplayName sets the "display_name" field. +func (u *TeamOIDCConfigUpsert) SetDisplayName(v string) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldDisplayName, v) + return u +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateDisplayName() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldDisplayName) + return u +} + +// SetIssuer sets the "issuer" field. +func (u *TeamOIDCConfigUpsert) SetIssuer(v string) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldIssuer, v) + return u +} + +// UpdateIssuer sets the "issuer" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateIssuer() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldIssuer) + return u +} + +// SetClientID sets the "client_id" field. +func (u *TeamOIDCConfigUpsert) SetClientID(v string) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldClientID, v) + return u +} + +// UpdateClientID sets the "client_id" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateClientID() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldClientID) + return u +} + +// SetClientSecretCiphertext sets the "client_secret_ciphertext" field. +func (u *TeamOIDCConfigUpsert) SetClientSecretCiphertext(v string) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldClientSecretCiphertext, v) + return u +} + +// UpdateClientSecretCiphertext sets the "client_secret_ciphertext" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateClientSecretCiphertext() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldClientSecretCiphertext) + return u +} + +// ClearClientSecretCiphertext clears the value of the "client_secret_ciphertext" field. +func (u *TeamOIDCConfigUpsert) ClearClientSecretCiphertext() *TeamOIDCConfigUpsert { + u.SetNull(teamoidcconfig.FieldClientSecretCiphertext) + return u +} + +// SetScopes sets the "scopes" field. +func (u *TeamOIDCConfigUpsert) SetScopes(v string) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldScopes, v) + return u +} + +// UpdateScopes sets the "scopes" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateScopes() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldScopes) + return u +} + +// SetEmailDomain sets the "email_domain" field. +func (u *TeamOIDCConfigUpsert) SetEmailDomain(v string) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldEmailDomain, v) + return u +} + +// UpdateEmailDomain sets the "email_domain" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateEmailDomain() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldEmailDomain) + return u +} + +// ClearEmailDomain clears the value of the "email_domain" field. +func (u *TeamOIDCConfigUpsert) ClearEmailDomain() *TeamOIDCConfigUpsert { + u.SetNull(teamoidcconfig.FieldEmailDomain) + return u +} + +// SetAutoCreateMember sets the "auto_create_member" field. +func (u *TeamOIDCConfigUpsert) SetAutoCreateMember(v bool) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldAutoCreateMember, v) + return u +} + +// UpdateAutoCreateMember sets the "auto_create_member" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateAutoCreateMember() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldAutoCreateMember) + return u +} + +// SetAllowPasswordLogin sets the "allow_password_login" field. +func (u *TeamOIDCConfigUpsert) SetAllowPasswordLogin(v bool) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldAllowPasswordLogin, v) + return u +} + +// UpdateAllowPasswordLogin sets the "allow_password_login" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateAllowPasswordLogin() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldAllowPasswordLogin) + return u +} + +// SetCreatedAt sets the "created_at" field. +func (u *TeamOIDCConfigUpsert) SetCreatedAt(v time.Time) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldCreatedAt, v) + return u +} + +// UpdateCreatedAt sets the "created_at" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateCreatedAt() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldCreatedAt) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *TeamOIDCConfigUpsert) SetUpdatedAt(v time.Time) *TeamOIDCConfigUpsert { + u.Set(teamoidcconfig.FieldUpdatedAt, v) + return u +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsert) UpdateUpdatedAt() *TeamOIDCConfigUpsert { + u.SetExcluded(teamoidcconfig.FieldUpdatedAt) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.TeamOIDCConfig.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(teamoidcconfig.FieldID) +// }), +// ). +// Exec(ctx) +func (u *TeamOIDCConfigUpsertOne) UpdateNewValues() *TeamOIDCConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(teamoidcconfig.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.TeamOIDCConfig.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *TeamOIDCConfigUpsertOne) Ignore() *TeamOIDCConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *TeamOIDCConfigUpsertOne) DoNothing() *TeamOIDCConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the TeamOIDCConfigCreate.OnConflict +// documentation for more info. +func (u *TeamOIDCConfigUpsertOne) Update(set func(*TeamOIDCConfigUpsert)) *TeamOIDCConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&TeamOIDCConfigUpsert{UpdateSet: update}) + })) + return u +} + +// SetTeamID sets the "team_id" field. +func (u *TeamOIDCConfigUpsertOne) SetTeamID(v uuid.UUID) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetTeamID(v) + }) +} + +// UpdateTeamID sets the "team_id" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateTeamID() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateTeamID() + }) +} + +// SetEnabled sets the "enabled" field. +func (u *TeamOIDCConfigUpsertOne) SetEnabled(v bool) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetEnabled(v) + }) +} + +// UpdateEnabled sets the "enabled" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateEnabled() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateEnabled() + }) +} + +// SetDisplayName sets the "display_name" field. +func (u *TeamOIDCConfigUpsertOne) SetDisplayName(v string) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetDisplayName(v) + }) +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateDisplayName() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateDisplayName() + }) +} + +// SetIssuer sets the "issuer" field. +func (u *TeamOIDCConfigUpsertOne) SetIssuer(v string) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetIssuer(v) + }) +} + +// UpdateIssuer sets the "issuer" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateIssuer() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateIssuer() + }) +} + +// SetClientID sets the "client_id" field. +func (u *TeamOIDCConfigUpsertOne) SetClientID(v string) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetClientID(v) + }) +} + +// UpdateClientID sets the "client_id" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateClientID() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateClientID() + }) +} + +// SetClientSecretCiphertext sets the "client_secret_ciphertext" field. +func (u *TeamOIDCConfigUpsertOne) SetClientSecretCiphertext(v string) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetClientSecretCiphertext(v) + }) +} + +// UpdateClientSecretCiphertext sets the "client_secret_ciphertext" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateClientSecretCiphertext() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateClientSecretCiphertext() + }) +} + +// ClearClientSecretCiphertext clears the value of the "client_secret_ciphertext" field. +func (u *TeamOIDCConfigUpsertOne) ClearClientSecretCiphertext() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.ClearClientSecretCiphertext() + }) +} + +// SetScopes sets the "scopes" field. +func (u *TeamOIDCConfigUpsertOne) SetScopes(v string) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetScopes(v) + }) +} + +// UpdateScopes sets the "scopes" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateScopes() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateScopes() + }) +} + +// SetEmailDomain sets the "email_domain" field. +func (u *TeamOIDCConfigUpsertOne) SetEmailDomain(v string) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetEmailDomain(v) + }) +} + +// UpdateEmailDomain sets the "email_domain" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateEmailDomain() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateEmailDomain() + }) +} + +// ClearEmailDomain clears the value of the "email_domain" field. +func (u *TeamOIDCConfigUpsertOne) ClearEmailDomain() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.ClearEmailDomain() + }) +} + +// SetAutoCreateMember sets the "auto_create_member" field. +func (u *TeamOIDCConfigUpsertOne) SetAutoCreateMember(v bool) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetAutoCreateMember(v) + }) +} + +// UpdateAutoCreateMember sets the "auto_create_member" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateAutoCreateMember() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateAutoCreateMember() + }) +} + +// SetAllowPasswordLogin sets the "allow_password_login" field. +func (u *TeamOIDCConfigUpsertOne) SetAllowPasswordLogin(v bool) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetAllowPasswordLogin(v) + }) +} + +// UpdateAllowPasswordLogin sets the "allow_password_login" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateAllowPasswordLogin() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateAllowPasswordLogin() + }) +} + +// SetCreatedAt sets the "created_at" field. +func (u *TeamOIDCConfigUpsertOne) SetCreatedAt(v time.Time) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetCreatedAt(v) + }) +} + +// UpdateCreatedAt sets the "created_at" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateCreatedAt() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateCreatedAt() + }) +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *TeamOIDCConfigUpsertOne) SetUpdatedAt(v time.Time) *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertOne) UpdateUpdatedAt() *TeamOIDCConfigUpsertOne { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateUpdatedAt() + }) +} + +// Exec executes the query. +func (u *TeamOIDCConfigUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("db: missing options for TeamOIDCConfigCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *TeamOIDCConfigUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *TeamOIDCConfigUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("db: TeamOIDCConfigUpsertOne.ID is not supported by MySQL driver. Use TeamOIDCConfigUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *TeamOIDCConfigUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// TeamOIDCConfigCreateBulk is the builder for creating many TeamOIDCConfig entities in bulk. +type TeamOIDCConfigCreateBulk struct { + config + err error + builders []*TeamOIDCConfigCreate + conflict []sql.ConflictOption +} + +// Save creates the TeamOIDCConfig entities in the database. +func (_c *TeamOIDCConfigCreateBulk) Save(ctx context.Context) ([]*TeamOIDCConfig, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*TeamOIDCConfig, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*TeamOIDCConfigMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *TeamOIDCConfigCreateBulk) SaveX(ctx context.Context) []*TeamOIDCConfig { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *TeamOIDCConfigCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *TeamOIDCConfigCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.TeamOIDCConfig.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.TeamOIDCConfigUpsert) { +// SetTeamID(v+v). +// }). +// Exec(ctx) +func (_c *TeamOIDCConfigCreateBulk) OnConflict(opts ...sql.ConflictOption) *TeamOIDCConfigUpsertBulk { + _c.conflict = opts + return &TeamOIDCConfigUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.TeamOIDCConfig.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *TeamOIDCConfigCreateBulk) OnConflictColumns(columns ...string) *TeamOIDCConfigUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &TeamOIDCConfigUpsertBulk{ + create: _c, + } +} + +// TeamOIDCConfigUpsertBulk is the builder for "upsert"-ing +// a bulk of TeamOIDCConfig nodes. +type TeamOIDCConfigUpsertBulk struct { + create *TeamOIDCConfigCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.TeamOIDCConfig.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(teamoidcconfig.FieldID) +// }), +// ). +// Exec(ctx) +func (u *TeamOIDCConfigUpsertBulk) UpdateNewValues() *TeamOIDCConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(teamoidcconfig.FieldID) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.TeamOIDCConfig.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *TeamOIDCConfigUpsertBulk) Ignore() *TeamOIDCConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *TeamOIDCConfigUpsertBulk) DoNothing() *TeamOIDCConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the TeamOIDCConfigCreateBulk.OnConflict +// documentation for more info. +func (u *TeamOIDCConfigUpsertBulk) Update(set func(*TeamOIDCConfigUpsert)) *TeamOIDCConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&TeamOIDCConfigUpsert{UpdateSet: update}) + })) + return u +} + +// SetTeamID sets the "team_id" field. +func (u *TeamOIDCConfigUpsertBulk) SetTeamID(v uuid.UUID) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetTeamID(v) + }) +} + +// UpdateTeamID sets the "team_id" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateTeamID() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateTeamID() + }) +} + +// SetEnabled sets the "enabled" field. +func (u *TeamOIDCConfigUpsertBulk) SetEnabled(v bool) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetEnabled(v) + }) +} + +// UpdateEnabled sets the "enabled" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateEnabled() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateEnabled() + }) +} + +// SetDisplayName sets the "display_name" field. +func (u *TeamOIDCConfigUpsertBulk) SetDisplayName(v string) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetDisplayName(v) + }) +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateDisplayName() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateDisplayName() + }) +} + +// SetIssuer sets the "issuer" field. +func (u *TeamOIDCConfigUpsertBulk) SetIssuer(v string) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetIssuer(v) + }) +} + +// UpdateIssuer sets the "issuer" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateIssuer() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateIssuer() + }) +} + +// SetClientID sets the "client_id" field. +func (u *TeamOIDCConfigUpsertBulk) SetClientID(v string) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetClientID(v) + }) +} + +// UpdateClientID sets the "client_id" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateClientID() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateClientID() + }) +} + +// SetClientSecretCiphertext sets the "client_secret_ciphertext" field. +func (u *TeamOIDCConfigUpsertBulk) SetClientSecretCiphertext(v string) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetClientSecretCiphertext(v) + }) +} + +// UpdateClientSecretCiphertext sets the "client_secret_ciphertext" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateClientSecretCiphertext() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateClientSecretCiphertext() + }) +} + +// ClearClientSecretCiphertext clears the value of the "client_secret_ciphertext" field. +func (u *TeamOIDCConfigUpsertBulk) ClearClientSecretCiphertext() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.ClearClientSecretCiphertext() + }) +} + +// SetScopes sets the "scopes" field. +func (u *TeamOIDCConfigUpsertBulk) SetScopes(v string) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetScopes(v) + }) +} + +// UpdateScopes sets the "scopes" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateScopes() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateScopes() + }) +} + +// SetEmailDomain sets the "email_domain" field. +func (u *TeamOIDCConfigUpsertBulk) SetEmailDomain(v string) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetEmailDomain(v) + }) +} + +// UpdateEmailDomain sets the "email_domain" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateEmailDomain() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateEmailDomain() + }) +} + +// ClearEmailDomain clears the value of the "email_domain" field. +func (u *TeamOIDCConfigUpsertBulk) ClearEmailDomain() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.ClearEmailDomain() + }) +} + +// SetAutoCreateMember sets the "auto_create_member" field. +func (u *TeamOIDCConfigUpsertBulk) SetAutoCreateMember(v bool) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetAutoCreateMember(v) + }) +} + +// UpdateAutoCreateMember sets the "auto_create_member" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateAutoCreateMember() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateAutoCreateMember() + }) +} + +// SetAllowPasswordLogin sets the "allow_password_login" field. +func (u *TeamOIDCConfigUpsertBulk) SetAllowPasswordLogin(v bool) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetAllowPasswordLogin(v) + }) +} + +// UpdateAllowPasswordLogin sets the "allow_password_login" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateAllowPasswordLogin() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateAllowPasswordLogin() + }) +} + +// SetCreatedAt sets the "created_at" field. +func (u *TeamOIDCConfigUpsertBulk) SetCreatedAt(v time.Time) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetCreatedAt(v) + }) +} + +// UpdateCreatedAt sets the "created_at" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateCreatedAt() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateCreatedAt() + }) +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *TeamOIDCConfigUpsertBulk) SetUpdatedAt(v time.Time) *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *TeamOIDCConfigUpsertBulk) UpdateUpdatedAt() *TeamOIDCConfigUpsertBulk { + return u.Update(func(s *TeamOIDCConfigUpsert) { + s.UpdateUpdatedAt() + }) +} + +// Exec executes the query. +func (u *TeamOIDCConfigUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("db: OnConflict was set for builder %d. Set it on the TeamOIDCConfigCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("db: missing options for TeamOIDCConfigCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *TeamOIDCConfigUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/db/teamoidcconfig_delete.go b/backend/db/teamoidcconfig_delete.go new file mode 100644 index 00000000..6604d271 --- /dev/null +++ b/backend/db/teamoidcconfig_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package db + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chaitin/MonkeyCode/backend/db/predicate" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" +) + +// TeamOIDCConfigDelete is the builder for deleting a TeamOIDCConfig entity. +type TeamOIDCConfigDelete struct { + config + hooks []Hook + mutation *TeamOIDCConfigMutation +} + +// Where appends a list predicates to the TeamOIDCConfigDelete builder. +func (_d *TeamOIDCConfigDelete) Where(ps ...predicate.TeamOIDCConfig) *TeamOIDCConfigDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *TeamOIDCConfigDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *TeamOIDCConfigDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *TeamOIDCConfigDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(teamoidcconfig.Table, sqlgraph.NewFieldSpec(teamoidcconfig.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// TeamOIDCConfigDeleteOne is the builder for deleting a single TeamOIDCConfig entity. +type TeamOIDCConfigDeleteOne struct { + _d *TeamOIDCConfigDelete +} + +// Where appends a list predicates to the TeamOIDCConfigDelete builder. +func (_d *TeamOIDCConfigDeleteOne) Where(ps ...predicate.TeamOIDCConfig) *TeamOIDCConfigDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *TeamOIDCConfigDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{teamoidcconfig.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *TeamOIDCConfigDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/db/teamoidcconfig_query.go b/backend/db/teamoidcconfig_query.go new file mode 100644 index 00000000..1b0d5511 --- /dev/null +++ b/backend/db/teamoidcconfig_query.go @@ -0,0 +1,657 @@ +// Code generated by ent, DO NOT EDIT. + +package db + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chaitin/MonkeyCode/backend/db/predicate" + "github.com/chaitin/MonkeyCode/backend/db/team" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" + "github.com/google/uuid" +) + +// TeamOIDCConfigQuery is the builder for querying TeamOIDCConfig entities. +type TeamOIDCConfigQuery struct { + config + ctx *QueryContext + order []teamoidcconfig.OrderOption + inters []Interceptor + predicates []predicate.TeamOIDCConfig + withTeam *TeamQuery + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the TeamOIDCConfigQuery builder. +func (_q *TeamOIDCConfigQuery) Where(ps ...predicate.TeamOIDCConfig) *TeamOIDCConfigQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *TeamOIDCConfigQuery) Limit(limit int) *TeamOIDCConfigQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *TeamOIDCConfigQuery) Offset(offset int) *TeamOIDCConfigQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *TeamOIDCConfigQuery) Unique(unique bool) *TeamOIDCConfigQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *TeamOIDCConfigQuery) Order(o ...teamoidcconfig.OrderOption) *TeamOIDCConfigQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryTeam chains the current query on the "team" edge. +func (_q *TeamOIDCConfigQuery) QueryTeam() *TeamQuery { + query := (&TeamClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(teamoidcconfig.Table, teamoidcconfig.FieldID, selector), + sqlgraph.To(team.Table, team.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, teamoidcconfig.TeamTable, teamoidcconfig.TeamColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first TeamOIDCConfig entity from the query. +// Returns a *NotFoundError when no TeamOIDCConfig was found. +func (_q *TeamOIDCConfigQuery) First(ctx context.Context) (*TeamOIDCConfig, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{teamoidcconfig.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *TeamOIDCConfigQuery) FirstX(ctx context.Context) *TeamOIDCConfig { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first TeamOIDCConfig ID from the query. +// Returns a *NotFoundError when no TeamOIDCConfig ID was found. +func (_q *TeamOIDCConfigQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{teamoidcconfig.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *TeamOIDCConfigQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single TeamOIDCConfig entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one TeamOIDCConfig entity is found. +// Returns a *NotFoundError when no TeamOIDCConfig entities are found. +func (_q *TeamOIDCConfigQuery) Only(ctx context.Context) (*TeamOIDCConfig, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{teamoidcconfig.Label} + default: + return nil, &NotSingularError{teamoidcconfig.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *TeamOIDCConfigQuery) OnlyX(ctx context.Context) *TeamOIDCConfig { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only TeamOIDCConfig ID in the query. +// Returns a *NotSingularError when more than one TeamOIDCConfig ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *TeamOIDCConfigQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{teamoidcconfig.Label} + default: + err = &NotSingularError{teamoidcconfig.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *TeamOIDCConfigQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of TeamOIDCConfigs. +func (_q *TeamOIDCConfigQuery) All(ctx context.Context) ([]*TeamOIDCConfig, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*TeamOIDCConfig, *TeamOIDCConfigQuery]() + return withInterceptors[[]*TeamOIDCConfig](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *TeamOIDCConfigQuery) AllX(ctx context.Context) []*TeamOIDCConfig { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of TeamOIDCConfig IDs. +func (_q *TeamOIDCConfigQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(teamoidcconfig.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *TeamOIDCConfigQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *TeamOIDCConfigQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*TeamOIDCConfigQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *TeamOIDCConfigQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *TeamOIDCConfigQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("db: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *TeamOIDCConfigQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the TeamOIDCConfigQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *TeamOIDCConfigQuery) Clone() *TeamOIDCConfigQuery { + if _q == nil { + return nil + } + return &TeamOIDCConfigQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]teamoidcconfig.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.TeamOIDCConfig{}, _q.predicates...), + withTeam: _q.withTeam.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + modifiers: append([]func(*sql.Selector){}, _q.modifiers...), + } +} + +// WithTeam tells the query-builder to eager-load the nodes that are connected to +// the "team" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *TeamOIDCConfigQuery) WithTeam(opts ...func(*TeamQuery)) *TeamOIDCConfigQuery { + query := (&TeamClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withTeam = query + return _q +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// TeamID uuid.UUID `json:"team_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.TeamOIDCConfig.Query(). +// GroupBy(teamoidcconfig.FieldTeamID). +// Aggregate(db.Count()). +// Scan(ctx, &v) +func (_q *TeamOIDCConfigQuery) GroupBy(field string, fields ...string) *TeamOIDCConfigGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &TeamOIDCConfigGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = teamoidcconfig.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// TeamID uuid.UUID `json:"team_id,omitempty"` +// } +// +// client.TeamOIDCConfig.Query(). +// Select(teamoidcconfig.FieldTeamID). +// Scan(ctx, &v) +func (_q *TeamOIDCConfigQuery) Select(fields ...string) *TeamOIDCConfigSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &TeamOIDCConfigSelect{TeamOIDCConfigQuery: _q} + sbuild.label = teamoidcconfig.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a TeamOIDCConfigSelect configured with the given aggregations. +func (_q *TeamOIDCConfigQuery) Aggregate(fns ...AggregateFunc) *TeamOIDCConfigSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *TeamOIDCConfigQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("db: uninitialized interceptor (forgotten import db/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !teamoidcconfig.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("db: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *TeamOIDCConfigQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*TeamOIDCConfig, error) { + var ( + nodes = []*TeamOIDCConfig{} + _spec = _q.querySpec() + loadedTypes = [1]bool{ + _q.withTeam != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*TeamOIDCConfig).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &TeamOIDCConfig{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := _q.withTeam; query != nil { + if err := _q.loadTeam(ctx, query, nodes, nil, + func(n *TeamOIDCConfig, e *Team) { n.Edges.Team = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *TeamOIDCConfigQuery) loadTeam(ctx context.Context, query *TeamQuery, nodes []*TeamOIDCConfig, init func(*TeamOIDCConfig), assign func(*TeamOIDCConfig, *Team)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*TeamOIDCConfig) + for i := range nodes { + fk := nodes[i].TeamID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(team.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "team_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (_q *TeamOIDCConfigQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *TeamOIDCConfigQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(teamoidcconfig.Table, teamoidcconfig.Columns, sqlgraph.NewFieldSpec(teamoidcconfig.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, teamoidcconfig.FieldID) + for i := range fields { + if fields[i] != teamoidcconfig.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if _q.withTeam != nil { + _spec.Node.AddColumnOnce(teamoidcconfig.FieldTeamID) + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *TeamOIDCConfigQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(teamoidcconfig.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = teamoidcconfig.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *TeamOIDCConfigQuery) ForUpdate(opts ...sql.LockOption) *TeamOIDCConfigQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *TeamOIDCConfigQuery) ForShare(opts ...sql.LockOption) *TeamOIDCConfigQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// Modify adds a query modifier for attaching custom logic to queries. +func (_q *TeamOIDCConfigQuery) Modify(modifiers ...func(s *sql.Selector)) *TeamOIDCConfigSelect { + _q.modifiers = append(_q.modifiers, modifiers...) + return _q.Select() +} + +// TeamOIDCConfigGroupBy is the group-by builder for TeamOIDCConfig entities. +type TeamOIDCConfigGroupBy struct { + selector + build *TeamOIDCConfigQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *TeamOIDCConfigGroupBy) Aggregate(fns ...AggregateFunc) *TeamOIDCConfigGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *TeamOIDCConfigGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*TeamOIDCConfigQuery, *TeamOIDCConfigGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *TeamOIDCConfigGroupBy) sqlScan(ctx context.Context, root *TeamOIDCConfigQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// TeamOIDCConfigSelect is the builder for selecting fields of TeamOIDCConfig entities. +type TeamOIDCConfigSelect struct { + *TeamOIDCConfigQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *TeamOIDCConfigSelect) Aggregate(fns ...AggregateFunc) *TeamOIDCConfigSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *TeamOIDCConfigSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*TeamOIDCConfigQuery, *TeamOIDCConfigSelect](ctx, _s.TeamOIDCConfigQuery, _s, _s.inters, v) +} + +func (_s *TeamOIDCConfigSelect) sqlScan(ctx context.Context, root *TeamOIDCConfigQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// Modify adds a query modifier for attaching custom logic to queries. +func (_s *TeamOIDCConfigSelect) Modify(modifiers ...func(s *sql.Selector)) *TeamOIDCConfigSelect { + _s.modifiers = append(_s.modifiers, modifiers...) + return _s +} diff --git a/backend/db/teamoidcconfig_update.go b/backend/db/teamoidcconfig_update.go new file mode 100644 index 00000000..4967ce24 --- /dev/null +++ b/backend/db/teamoidcconfig_update.go @@ -0,0 +1,756 @@ +// Code generated by ent, DO NOT EDIT. + +package db + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chaitin/MonkeyCode/backend/db/predicate" + "github.com/chaitin/MonkeyCode/backend/db/team" + "github.com/chaitin/MonkeyCode/backend/db/teamoidcconfig" + "github.com/google/uuid" +) + +// TeamOIDCConfigUpdate is the builder for updating TeamOIDCConfig entities. +type TeamOIDCConfigUpdate struct { + config + hooks []Hook + mutation *TeamOIDCConfigMutation + modifiers []func(*sql.UpdateBuilder) +} + +// Where appends a list predicates to the TeamOIDCConfigUpdate builder. +func (_u *TeamOIDCConfigUpdate) Where(ps ...predicate.TeamOIDCConfig) *TeamOIDCConfigUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetTeamID sets the "team_id" field. +func (_u *TeamOIDCConfigUpdate) SetTeamID(v uuid.UUID) *TeamOIDCConfigUpdate { + _u.mutation.SetTeamID(v) + return _u +} + +// SetNillableTeamID sets the "team_id" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableTeamID(v *uuid.UUID) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetTeamID(*v) + } + return _u +} + +// SetEnabled sets the "enabled" field. +func (_u *TeamOIDCConfigUpdate) SetEnabled(v bool) *TeamOIDCConfigUpdate { + _u.mutation.SetEnabled(v) + return _u +} + +// SetNillableEnabled sets the "enabled" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableEnabled(v *bool) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetEnabled(*v) + } + return _u +} + +// SetDisplayName sets the "display_name" field. +func (_u *TeamOIDCConfigUpdate) SetDisplayName(v string) *TeamOIDCConfigUpdate { + _u.mutation.SetDisplayName(v) + return _u +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableDisplayName(v *string) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetDisplayName(*v) + } + return _u +} + +// SetIssuer sets the "issuer" field. +func (_u *TeamOIDCConfigUpdate) SetIssuer(v string) *TeamOIDCConfigUpdate { + _u.mutation.SetIssuer(v) + return _u +} + +// SetNillableIssuer sets the "issuer" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableIssuer(v *string) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetIssuer(*v) + } + return _u +} + +// SetClientID sets the "client_id" field. +func (_u *TeamOIDCConfigUpdate) SetClientID(v string) *TeamOIDCConfigUpdate { + _u.mutation.SetClientID(v) + return _u +} + +// SetNillableClientID sets the "client_id" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableClientID(v *string) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetClientID(*v) + } + return _u +} + +// SetClientSecretCiphertext sets the "client_secret_ciphertext" field. +func (_u *TeamOIDCConfigUpdate) SetClientSecretCiphertext(v string) *TeamOIDCConfigUpdate { + _u.mutation.SetClientSecretCiphertext(v) + return _u +} + +// SetNillableClientSecretCiphertext sets the "client_secret_ciphertext" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableClientSecretCiphertext(v *string) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetClientSecretCiphertext(*v) + } + return _u +} + +// ClearClientSecretCiphertext clears the value of the "client_secret_ciphertext" field. +func (_u *TeamOIDCConfigUpdate) ClearClientSecretCiphertext() *TeamOIDCConfigUpdate { + _u.mutation.ClearClientSecretCiphertext() + return _u +} + +// SetScopes sets the "scopes" field. +func (_u *TeamOIDCConfigUpdate) SetScopes(v string) *TeamOIDCConfigUpdate { + _u.mutation.SetScopes(v) + return _u +} + +// SetNillableScopes sets the "scopes" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableScopes(v *string) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetScopes(*v) + } + return _u +} + +// SetEmailDomain sets the "email_domain" field. +func (_u *TeamOIDCConfigUpdate) SetEmailDomain(v string) *TeamOIDCConfigUpdate { + _u.mutation.SetEmailDomain(v) + return _u +} + +// SetNillableEmailDomain sets the "email_domain" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableEmailDomain(v *string) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetEmailDomain(*v) + } + return _u +} + +// ClearEmailDomain clears the value of the "email_domain" field. +func (_u *TeamOIDCConfigUpdate) ClearEmailDomain() *TeamOIDCConfigUpdate { + _u.mutation.ClearEmailDomain() + return _u +} + +// SetAutoCreateMember sets the "auto_create_member" field. +func (_u *TeamOIDCConfigUpdate) SetAutoCreateMember(v bool) *TeamOIDCConfigUpdate { + _u.mutation.SetAutoCreateMember(v) + return _u +} + +// SetNillableAutoCreateMember sets the "auto_create_member" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableAutoCreateMember(v *bool) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetAutoCreateMember(*v) + } + return _u +} + +// SetAllowPasswordLogin sets the "allow_password_login" field. +func (_u *TeamOIDCConfigUpdate) SetAllowPasswordLogin(v bool) *TeamOIDCConfigUpdate { + _u.mutation.SetAllowPasswordLogin(v) + return _u +} + +// SetNillableAllowPasswordLogin sets the "allow_password_login" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableAllowPasswordLogin(v *bool) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetAllowPasswordLogin(*v) + } + return _u +} + +// SetCreatedAt sets the "created_at" field. +func (_u *TeamOIDCConfigUpdate) SetCreatedAt(v time.Time) *TeamOIDCConfigUpdate { + _u.mutation.SetCreatedAt(v) + return _u +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdate) SetNillableCreatedAt(v *time.Time) *TeamOIDCConfigUpdate { + if v != nil { + _u.SetCreatedAt(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *TeamOIDCConfigUpdate) SetUpdatedAt(v time.Time) *TeamOIDCConfigUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// SetTeam sets the "team" edge to the Team entity. +func (_u *TeamOIDCConfigUpdate) SetTeam(v *Team) *TeamOIDCConfigUpdate { + return _u.SetTeamID(v.ID) +} + +// Mutation returns the TeamOIDCConfigMutation object of the builder. +func (_u *TeamOIDCConfigUpdate) Mutation() *TeamOIDCConfigMutation { + return _u.mutation +} + +// ClearTeam clears the "team" edge to the Team entity. +func (_u *TeamOIDCConfigUpdate) ClearTeam() *TeamOIDCConfigUpdate { + _u.mutation.ClearTeam() + return _u +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *TeamOIDCConfigUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *TeamOIDCConfigUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *TeamOIDCConfigUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *TeamOIDCConfigUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *TeamOIDCConfigUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := teamoidcconfig.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *TeamOIDCConfigUpdate) check() error { + if v, ok := _u.mutation.Issuer(); ok { + if err := teamoidcconfig.IssuerValidator(v); err != nil { + return &ValidationError{Name: "issuer", err: fmt.Errorf(`db: validator failed for field "TeamOIDCConfig.issuer": %w`, err)} + } + } + if v, ok := _u.mutation.ClientID(); ok { + if err := teamoidcconfig.ClientIDValidator(v); err != nil { + return &ValidationError{Name: "client_id", err: fmt.Errorf(`db: validator failed for field "TeamOIDCConfig.client_id": %w`, err)} + } + } + if _u.mutation.TeamCleared() && len(_u.mutation.TeamIDs()) > 0 { + return errors.New(`db: clearing a required unique edge "TeamOIDCConfig.team"`) + } + return nil +} + +// Modify adds a statement modifier for attaching custom logic to the UPDATE statement. +func (_u *TeamOIDCConfigUpdate) Modify(modifiers ...func(u *sql.UpdateBuilder)) *TeamOIDCConfigUpdate { + _u.modifiers = append(_u.modifiers, modifiers...) + return _u +} + +func (_u *TeamOIDCConfigUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(teamoidcconfig.Table, teamoidcconfig.Columns, sqlgraph.NewFieldSpec(teamoidcconfig.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Enabled(); ok { + _spec.SetField(teamoidcconfig.FieldEnabled, field.TypeBool, value) + } + if value, ok := _u.mutation.DisplayName(); ok { + _spec.SetField(teamoidcconfig.FieldDisplayName, field.TypeString, value) + } + if value, ok := _u.mutation.Issuer(); ok { + _spec.SetField(teamoidcconfig.FieldIssuer, field.TypeString, value) + } + if value, ok := _u.mutation.ClientID(); ok { + _spec.SetField(teamoidcconfig.FieldClientID, field.TypeString, value) + } + if value, ok := _u.mutation.ClientSecretCiphertext(); ok { + _spec.SetField(teamoidcconfig.FieldClientSecretCiphertext, field.TypeString, value) + } + if _u.mutation.ClientSecretCiphertextCleared() { + _spec.ClearField(teamoidcconfig.FieldClientSecretCiphertext, field.TypeString) + } + if value, ok := _u.mutation.Scopes(); ok { + _spec.SetField(teamoidcconfig.FieldScopes, field.TypeString, value) + } + if value, ok := _u.mutation.EmailDomain(); ok { + _spec.SetField(teamoidcconfig.FieldEmailDomain, field.TypeString, value) + } + if _u.mutation.EmailDomainCleared() { + _spec.ClearField(teamoidcconfig.FieldEmailDomain, field.TypeString) + } + if value, ok := _u.mutation.AutoCreateMember(); ok { + _spec.SetField(teamoidcconfig.FieldAutoCreateMember, field.TypeBool, value) + } + if value, ok := _u.mutation.AllowPasswordLogin(); ok { + _spec.SetField(teamoidcconfig.FieldAllowPasswordLogin, field.TypeBool, value) + } + if value, ok := _u.mutation.CreatedAt(); ok { + _spec.SetField(teamoidcconfig.FieldCreatedAt, field.TypeTime, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(teamoidcconfig.FieldUpdatedAt, field.TypeTime, value) + } + if _u.mutation.TeamCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: teamoidcconfig.TeamTable, + Columns: []string{teamoidcconfig.TeamColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(team.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.TeamIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: teamoidcconfig.TeamTable, + Columns: []string{teamoidcconfig.TeamColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(team.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _spec.AddModifiers(_u.modifiers...) + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{teamoidcconfig.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// TeamOIDCConfigUpdateOne is the builder for updating a single TeamOIDCConfig entity. +type TeamOIDCConfigUpdateOne struct { + config + fields []string + hooks []Hook + mutation *TeamOIDCConfigMutation + modifiers []func(*sql.UpdateBuilder) +} + +// SetTeamID sets the "team_id" field. +func (_u *TeamOIDCConfigUpdateOne) SetTeamID(v uuid.UUID) *TeamOIDCConfigUpdateOne { + _u.mutation.SetTeamID(v) + return _u +} + +// SetNillableTeamID sets the "team_id" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableTeamID(v *uuid.UUID) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetTeamID(*v) + } + return _u +} + +// SetEnabled sets the "enabled" field. +func (_u *TeamOIDCConfigUpdateOne) SetEnabled(v bool) *TeamOIDCConfigUpdateOne { + _u.mutation.SetEnabled(v) + return _u +} + +// SetNillableEnabled sets the "enabled" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableEnabled(v *bool) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetEnabled(*v) + } + return _u +} + +// SetDisplayName sets the "display_name" field. +func (_u *TeamOIDCConfigUpdateOne) SetDisplayName(v string) *TeamOIDCConfigUpdateOne { + _u.mutation.SetDisplayName(v) + return _u +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableDisplayName(v *string) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetDisplayName(*v) + } + return _u +} + +// SetIssuer sets the "issuer" field. +func (_u *TeamOIDCConfigUpdateOne) SetIssuer(v string) *TeamOIDCConfigUpdateOne { + _u.mutation.SetIssuer(v) + return _u +} + +// SetNillableIssuer sets the "issuer" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableIssuer(v *string) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetIssuer(*v) + } + return _u +} + +// SetClientID sets the "client_id" field. +func (_u *TeamOIDCConfigUpdateOne) SetClientID(v string) *TeamOIDCConfigUpdateOne { + _u.mutation.SetClientID(v) + return _u +} + +// SetNillableClientID sets the "client_id" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableClientID(v *string) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetClientID(*v) + } + return _u +} + +// SetClientSecretCiphertext sets the "client_secret_ciphertext" field. +func (_u *TeamOIDCConfigUpdateOne) SetClientSecretCiphertext(v string) *TeamOIDCConfigUpdateOne { + _u.mutation.SetClientSecretCiphertext(v) + return _u +} + +// SetNillableClientSecretCiphertext sets the "client_secret_ciphertext" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableClientSecretCiphertext(v *string) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetClientSecretCiphertext(*v) + } + return _u +} + +// ClearClientSecretCiphertext clears the value of the "client_secret_ciphertext" field. +func (_u *TeamOIDCConfigUpdateOne) ClearClientSecretCiphertext() *TeamOIDCConfigUpdateOne { + _u.mutation.ClearClientSecretCiphertext() + return _u +} + +// SetScopes sets the "scopes" field. +func (_u *TeamOIDCConfigUpdateOne) SetScopes(v string) *TeamOIDCConfigUpdateOne { + _u.mutation.SetScopes(v) + return _u +} + +// SetNillableScopes sets the "scopes" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableScopes(v *string) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetScopes(*v) + } + return _u +} + +// SetEmailDomain sets the "email_domain" field. +func (_u *TeamOIDCConfigUpdateOne) SetEmailDomain(v string) *TeamOIDCConfigUpdateOne { + _u.mutation.SetEmailDomain(v) + return _u +} + +// SetNillableEmailDomain sets the "email_domain" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableEmailDomain(v *string) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetEmailDomain(*v) + } + return _u +} + +// ClearEmailDomain clears the value of the "email_domain" field. +func (_u *TeamOIDCConfigUpdateOne) ClearEmailDomain() *TeamOIDCConfigUpdateOne { + _u.mutation.ClearEmailDomain() + return _u +} + +// SetAutoCreateMember sets the "auto_create_member" field. +func (_u *TeamOIDCConfigUpdateOne) SetAutoCreateMember(v bool) *TeamOIDCConfigUpdateOne { + _u.mutation.SetAutoCreateMember(v) + return _u +} + +// SetNillableAutoCreateMember sets the "auto_create_member" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableAutoCreateMember(v *bool) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetAutoCreateMember(*v) + } + return _u +} + +// SetAllowPasswordLogin sets the "allow_password_login" field. +func (_u *TeamOIDCConfigUpdateOne) SetAllowPasswordLogin(v bool) *TeamOIDCConfigUpdateOne { + _u.mutation.SetAllowPasswordLogin(v) + return _u +} + +// SetNillableAllowPasswordLogin sets the "allow_password_login" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableAllowPasswordLogin(v *bool) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetAllowPasswordLogin(*v) + } + return _u +} + +// SetCreatedAt sets the "created_at" field. +func (_u *TeamOIDCConfigUpdateOne) SetCreatedAt(v time.Time) *TeamOIDCConfigUpdateOne { + _u.mutation.SetCreatedAt(v) + return _u +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_u *TeamOIDCConfigUpdateOne) SetNillableCreatedAt(v *time.Time) *TeamOIDCConfigUpdateOne { + if v != nil { + _u.SetCreatedAt(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *TeamOIDCConfigUpdateOne) SetUpdatedAt(v time.Time) *TeamOIDCConfigUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// SetTeam sets the "team" edge to the Team entity. +func (_u *TeamOIDCConfigUpdateOne) SetTeam(v *Team) *TeamOIDCConfigUpdateOne { + return _u.SetTeamID(v.ID) +} + +// Mutation returns the TeamOIDCConfigMutation object of the builder. +func (_u *TeamOIDCConfigUpdateOne) Mutation() *TeamOIDCConfigMutation { + return _u.mutation +} + +// ClearTeam clears the "team" edge to the Team entity. +func (_u *TeamOIDCConfigUpdateOne) ClearTeam() *TeamOIDCConfigUpdateOne { + _u.mutation.ClearTeam() + return _u +} + +// Where appends a list predicates to the TeamOIDCConfigUpdate builder. +func (_u *TeamOIDCConfigUpdateOne) Where(ps ...predicate.TeamOIDCConfig) *TeamOIDCConfigUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *TeamOIDCConfigUpdateOne) Select(field string, fields ...string) *TeamOIDCConfigUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated TeamOIDCConfig entity. +func (_u *TeamOIDCConfigUpdateOne) Save(ctx context.Context) (*TeamOIDCConfig, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *TeamOIDCConfigUpdateOne) SaveX(ctx context.Context) *TeamOIDCConfig { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *TeamOIDCConfigUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *TeamOIDCConfigUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *TeamOIDCConfigUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := teamoidcconfig.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *TeamOIDCConfigUpdateOne) check() error { + if v, ok := _u.mutation.Issuer(); ok { + if err := teamoidcconfig.IssuerValidator(v); err != nil { + return &ValidationError{Name: "issuer", err: fmt.Errorf(`db: validator failed for field "TeamOIDCConfig.issuer": %w`, err)} + } + } + if v, ok := _u.mutation.ClientID(); ok { + if err := teamoidcconfig.ClientIDValidator(v); err != nil { + return &ValidationError{Name: "client_id", err: fmt.Errorf(`db: validator failed for field "TeamOIDCConfig.client_id": %w`, err)} + } + } + if _u.mutation.TeamCleared() && len(_u.mutation.TeamIDs()) > 0 { + return errors.New(`db: clearing a required unique edge "TeamOIDCConfig.team"`) + } + return nil +} + +// Modify adds a statement modifier for attaching custom logic to the UPDATE statement. +func (_u *TeamOIDCConfigUpdateOne) Modify(modifiers ...func(u *sql.UpdateBuilder)) *TeamOIDCConfigUpdateOne { + _u.modifiers = append(_u.modifiers, modifiers...) + return _u +} + +func (_u *TeamOIDCConfigUpdateOne) sqlSave(ctx context.Context) (_node *TeamOIDCConfig, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(teamoidcconfig.Table, teamoidcconfig.Columns, sqlgraph.NewFieldSpec(teamoidcconfig.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`db: missing "TeamOIDCConfig.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, teamoidcconfig.FieldID) + for _, f := range fields { + if !teamoidcconfig.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("db: invalid field %q for query", f)} + } + if f != teamoidcconfig.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Enabled(); ok { + _spec.SetField(teamoidcconfig.FieldEnabled, field.TypeBool, value) + } + if value, ok := _u.mutation.DisplayName(); ok { + _spec.SetField(teamoidcconfig.FieldDisplayName, field.TypeString, value) + } + if value, ok := _u.mutation.Issuer(); ok { + _spec.SetField(teamoidcconfig.FieldIssuer, field.TypeString, value) + } + if value, ok := _u.mutation.ClientID(); ok { + _spec.SetField(teamoidcconfig.FieldClientID, field.TypeString, value) + } + if value, ok := _u.mutation.ClientSecretCiphertext(); ok { + _spec.SetField(teamoidcconfig.FieldClientSecretCiphertext, field.TypeString, value) + } + if _u.mutation.ClientSecretCiphertextCleared() { + _spec.ClearField(teamoidcconfig.FieldClientSecretCiphertext, field.TypeString) + } + if value, ok := _u.mutation.Scopes(); ok { + _spec.SetField(teamoidcconfig.FieldScopes, field.TypeString, value) + } + if value, ok := _u.mutation.EmailDomain(); ok { + _spec.SetField(teamoidcconfig.FieldEmailDomain, field.TypeString, value) + } + if _u.mutation.EmailDomainCleared() { + _spec.ClearField(teamoidcconfig.FieldEmailDomain, field.TypeString) + } + if value, ok := _u.mutation.AutoCreateMember(); ok { + _spec.SetField(teamoidcconfig.FieldAutoCreateMember, field.TypeBool, value) + } + if value, ok := _u.mutation.AllowPasswordLogin(); ok { + _spec.SetField(teamoidcconfig.FieldAllowPasswordLogin, field.TypeBool, value) + } + if value, ok := _u.mutation.CreatedAt(); ok { + _spec.SetField(teamoidcconfig.FieldCreatedAt, field.TypeTime, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(teamoidcconfig.FieldUpdatedAt, field.TypeTime, value) + } + if _u.mutation.TeamCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: teamoidcconfig.TeamTable, + Columns: []string{teamoidcconfig.TeamColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(team.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.TeamIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: teamoidcconfig.TeamTable, + Columns: []string{teamoidcconfig.TeamColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(team.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _spec.AddModifiers(_u.modifiers...) + _node = &TeamOIDCConfig{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{teamoidcconfig.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/backend/db/tx.go b/backend/db/tx.go index ac423775..fc355144 100644 --- a/backend/db/tx.go +++ b/backend/db/tx.go @@ -88,6 +88,8 @@ type Tx struct { TeamMember *TeamMemberClient // TeamModel is the client for interacting with the TeamModel builders. TeamModel *TeamModelClient + // TeamOIDCConfig is the client for interacting with the TeamOIDCConfig builders. + TeamOIDCConfig *TeamOIDCConfigClient // User is the client for interacting with the User builders. User *UserClient // UserIdentity is the client for interacting with the UserIdentity builders. @@ -262,6 +264,7 @@ func (tx *Tx) init() { tx.TeamImage = NewTeamImageClient(tx.config) tx.TeamMember = NewTeamMemberClient(tx.config) tx.TeamModel = NewTeamModelClient(tx.config) + tx.TeamOIDCConfig = NewTeamOIDCConfigClient(tx.config) tx.User = NewUserClient(tx.config) tx.UserIdentity = NewUserIdentityClient(tx.config) tx.VirtualMachine = NewVirtualMachineClient(tx.config) diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index 2c5412ef..41ac4453 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -1857,6 +1857,146 @@ } } }, + "/api/v1/teams/oidc": { + "get": { + "security": [ + { + "MonkeyCodeAITeamAuth": [] + } + ], + "description": "获取当前团队企业登录 OIDC 配置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "【Team 管理员】企业登录" + ], + "summary": "获取团队 OIDC 配置", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.Resp" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/domain.TeamOIDCConfigResp" + } + } + } + ] + } + } + } + }, + "put": { + "security": [ + { + "MonkeyCodeAITeamAuth": [] + } + ], + "description": "新增或更新当前团队企业登录 OIDC 配置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "【Team 管理员】企业登录" + ], + "summary": "保存团队 OIDC 配置", + "parameters": [ + { + "description": "请求参数", + "name": "req", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/domain.SaveTeamOIDCConfigReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.Resp" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/domain.TeamOIDCConfigResp" + } + } + } + ] + } + } + } + } + }, + "/api/v1/teams/oidc/test": { + "post": { + "security": [ + { + "MonkeyCodeAITeamAuth": [] + } + ], + "description": "拉取 OIDC discovery 文档验证配置可用性", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "【Team 管理员】企业登录" + ], + "summary": "测试团队 OIDC 配置", + "parameters": [ + { + "description": "请求参数", + "name": "req", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/domain.SaveTeamOIDCConfigReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.Resp" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/domain.TeamOIDCTestResp" + } + } + } + ] + } + } + } + } + }, "/api/v1/teams/projects": { "get": { "security": [ @@ -6388,6 +6528,142 @@ } } }, + "/api/v1/users/oidc/callback": { + "get": { + "description": "处理身份源回调并创建 MonkeyCode 登录会话", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "【用户】企业团队成员认证" + ], + "summary": "处理团队 OIDC 回调", + "parameters": [ + { + "type": "string", + "description": "授权码", + "name": "code", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "状态", + "name": "state", + "in": "query", + "required": true + } + ], + "responses": {} + } + }, + "/api/v1/users/oidc/default-team": { + "get": { + "description": "用于私有化登录页展示第一个已启用团队的企业登录入口", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "【用户】企业团队成员认证" + ], + "summary": "获取默认团队公开 OIDC 登录配置", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.Resp" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/domain.TeamOIDCPublicConfigResp" + } + } + } + ] + } + } + } + } + }, + "/api/v1/users/oidc/login": { + "get": { + "description": "根据 team_id 跳转到团队 OIDC 身份源", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "【用户】企业团队成员认证" + ], + "summary": "发起团队 OIDC 登录", + "parameters": [ + { + "type": "string", + "description": "团队 ID", + "name": "team_id", + "in": "query", + "required": true + } + ], + "responses": {} + } + }, + "/api/v1/users/oidc/teams/{team_id}": { + "get": { + "description": "用于团队专属登录页展示企业登录入口", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "【用户】企业团队成员认证" + ], + "summary": "获取团队公开 OIDC 登录配置", + "parameters": [ + { + "type": "string", + "description": "团队 ID", + "name": "team_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.Resp" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/domain.TeamOIDCPublicConfigResp" + } + } + } + ] + } + } + } + } + }, "/api/v1/users/password-login": { "post": { "description": "密码登录", @@ -8765,7 +9041,8 @@ "github", "gitlab", "gitea", - "gitee" + "gitee", + "oidc" ], "x-enum-comments": { "UserPlatformBaizhi": "百智云平台" @@ -8775,7 +9052,8 @@ "UserPlatformGithub", "UserPlatformGitLab", "UserPlatformGitea", - "UserPlatformGitee" + "UserPlatformGitee", + "UserPlatformOIDC" ] }, "consts.UserRole": { @@ -10811,6 +11089,43 @@ } } }, + "domain.SaveTeamOIDCConfigReq": { + "type": "object", + "required": [ + "client_id", + "display_name", + "issuer" + ], + "properties": { + "allow_password_login": { + "type": "boolean" + }, + "auto_create_member": { + "type": "boolean" + }, + "client_id": { + "type": "string" + }, + "client_secret": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "email_domain": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "issuer": { + "type": "string" + }, + "scopes": { + "type": "string" + } + } + }, "domain.SendBindEmailVerificationReq": { "type": "object", "required": [ @@ -11649,6 +11964,89 @@ } } }, + "domain.TeamOIDCConfig": { + "type": "object", + "properties": { + "allow_password_login": { + "type": "boolean" + }, + "auto_create_member": { + "type": "boolean" + }, + "client_id": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "email_domain": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "has_client_secret": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "issuer": { + "type": "string" + }, + "login_url": { + "type": "string" + }, + "redirect_uri": { + "type": "string" + }, + "scopes": { + "type": "string" + }, + "team_id": { + "type": "string" + } + } + }, + "domain.TeamOIDCConfigResp": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/domain.TeamOIDCConfig" + } + } + }, + "domain.TeamOIDCPublicConfigResp": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "login_url": { + "type": "string" + }, + "team_id": { + "type": "string" + } + } + }, + "domain.TeamOIDCTestResp": { + "type": "object", + "properties": { + "issuer": { + "type": "string" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, "domain.TeamProjectItem": { "type": "object", "properties": { diff --git a/backend/domain/team_oidc.go b/backend/domain/team_oidc.go new file mode 100644 index 00000000..8f5fec9e --- /dev/null +++ b/backend/domain/team_oidc.go @@ -0,0 +1,101 @@ +package domain + +import ( + "context" + + "github.com/google/uuid" + + "github.com/chaitin/MonkeyCode/backend/db" +) + +type TeamOIDCUsecase interface { + GetConfig(ctx context.Context, teamUser *TeamUser) (*TeamOIDCConfigResp, error) + SaveConfig(ctx context.Context, teamUser *TeamUser, req *SaveTeamOIDCConfigReq) (*TeamOIDCConfigResp, error) + TestConfig(ctx context.Context, teamUser *TeamUser, req *SaveTeamOIDCConfigReq) (*TeamOIDCTestResp, error) +} + +type TeamOIDCLoginUsecase interface { + StartLogin(ctx context.Context, teamID uuid.UUID) (string, error) + HandleCallback(ctx context.Context, req *TeamOIDCCallbackReq) (*User, error) + PublicConfig(ctx context.Context, teamID uuid.UUID) (*TeamOIDCPublicConfigResp, error) + DefaultPublicConfig(ctx context.Context) (*TeamOIDCPublicConfigResp, error) + PasswordLoginAllowed(ctx context.Context, teamID uuid.UUID) (bool, error) +} + +type TeamOIDCRepo interface { + GetConfig(ctx context.Context, teamID uuid.UUID) (*db.TeamOIDCConfig, error) + GetDefaultEnabledConfig(ctx context.Context) (*db.TeamOIDCConfig, error) + UpsertConfig(ctx context.Context, teamID uuid.UUID, req *SaveTeamOIDCConfigReq) (*db.TeamOIDCConfig, error) + FindUserByOIDCIdentity(ctx context.Context, identityID string) (*db.User, error) + FindTeamMemberByEmail(ctx context.Context, teamID uuid.UUID, email string) (*db.TeamMember, error) + BindOIDCIdentity(ctx context.Context, userID uuid.UUID, external *OIDCExternalUser) error + AutoCreateMember(ctx context.Context, teamID uuid.UUID, external *OIDCExternalUser) (*db.User, error) +} + +type TeamOIDCConfig struct { + ID uuid.UUID `json:"id"` + TeamID uuid.UUID `json:"team_id"` + Enabled bool `json:"enabled"` + DisplayName string `json:"display_name"` + Issuer string `json:"issuer"` + ClientID string `json:"client_id"` + HasClientSecret bool `json:"has_client_secret"` + Scopes string `json:"scopes"` + EmailDomain string `json:"email_domain"` + AutoCreateMember bool `json:"auto_create_member"` + AllowPasswordLogin bool `json:"allow_password_login"` + RedirectURI string `json:"redirect_uri"` + LoginURL string `json:"login_url"` +} + +type SaveTeamOIDCConfigReq struct { + Enabled bool `json:"enabled"` + DisplayName string `json:"display_name" validate:"required"` + Issuer string `json:"issuer" validate:"required"` + ClientID string `json:"client_id" validate:"required"` + ClientSecret string `json:"client_secret"` + Scopes string `json:"scopes"` + EmailDomain string `json:"email_domain"` + AutoCreateMember bool `json:"auto_create_member"` + AllowPasswordLogin bool `json:"allow_password_login"` +} + +type TeamOIDCConfigResp struct { + Config *TeamOIDCConfig `json:"config"` +} + +type TeamOIDCTestResp struct { + Success bool `json:"success"` + Issuer string `json:"issuer"` + Message string `json:"message"` +} + +type TeamOIDCPublicConfigReq struct { + TeamID uuid.UUID `param:"team_id" validate:"required" json:"-" swaggerignore:"true"` +} + +type TeamOIDCPublicConfigResp struct { + TeamID uuid.UUID `json:"team_id"` + Enabled bool `json:"enabled"` + DisplayName string `json:"display_name"` + LoginURL string `json:"login_url"` +} + +type TeamOIDCLoginReq struct { + TeamID uuid.UUID `query:"team_id" validate:"required"` +} + +type TeamOIDCCallbackReq struct { + Code string `query:"code" validate:"required"` + State string `query:"state" validate:"required"` +} + +type OIDCExternalUser struct { + Issuer string + Subject string + Email string + EmailVerified bool + Name string + Username string + AvatarURL string +} diff --git a/backend/ent/schema/teamoidcconfig.go b/backend/ent/schema/teamoidcconfig.go new file mode 100644 index 00000000..5996832e --- /dev/null +++ b/backend/ent/schema/teamoidcconfig.go @@ -0,0 +1,54 @@ +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// TeamOIDCConfig holds the schema definition for team OIDC login configuration. +type TeamOIDCConfig struct { + ent.Schema +} + +func (TeamOIDCConfig) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Table("team_oidc_configs"), + } +} + +func (TeamOIDCConfig) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Unique(), + field.UUID("team_id", uuid.UUID{}), + field.Bool("enabled").Default(false), + field.String("display_name").Default("企业登录"), + field.String("issuer").NotEmpty(), + field.String("client_id").NotEmpty(), + field.String("client_secret_ciphertext").Optional(), + field.String("scopes").Default("openid email profile"), + field.String("email_domain").Optional(), + field.Bool("auto_create_member").Default(false), + field.Bool("allow_password_login").Default(true), + field.Time("created_at").Default(time.Now), + field.Time("updated_at").Default(time.Now).UpdateDefault(time.Now), + } +} + +func (TeamOIDCConfig) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("team", Team.Type).Field("team_id").Unique().Required(), + } +} + +func (TeamOIDCConfig) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("team_id").Unique(), + } +} diff --git a/backend/errcode/errcode.go b/backend/errcode/errcode.go index d69a8c67..565ba730 100644 --- a/backend/errcode/errcode.go +++ b/backend/errcode/errcode.go @@ -97,6 +97,15 @@ var ( ErrEmailRequired = web.NewErr(http.StatusOK, 10614, "err-email-required") ErrEmailNotBound = web.NewErr(http.StatusOK, 10615, "err-email-not-bound") ErrEnterpriseResetPasswordDenied = web.NewErr(http.StatusOK, 10616, "err-enterprise-reset-password-denied") + ErrOIDCDisabled = web.NewErr(http.StatusOK, 10617, "err-oidc-disabled") + ErrOIDCConfigInvalid = web.NewErr(http.StatusOK, 10618, "err-oidc-config-invalid") + ErrOIDCStateInvalid = web.NewErr(http.StatusOK, 10619, "err-oidc-state-invalid") + ErrOIDCTokenInvalid = web.NewErr(http.StatusOK, 10620, "err-oidc-token-invalid") + ErrOIDCEmailRequired = web.NewErr(http.StatusOK, 10621, "err-oidc-email-required") + ErrOIDCEmailNotVerified = web.NewErr(http.StatusOK, 10622, "err-oidc-email-not-verified") + ErrOIDCEmailDomainDenied = web.NewErr(http.StatusOK, 10623, "err-oidc-email-domain-denied") + ErrOIDCTeamMemberRequired = web.NewErr(http.StatusOK, 10624, "err-oidc-team-member-required") + ErrPasswordLoginDisabled = web.NewErr(http.StatusOK, 10625, "err-password-login-disabled") // captcha 模块 ErrCreateCaptchaFailed = web.NewErr(http.StatusOK, 10700, "err-create-captcha-failed") diff --git a/backend/errcode/locale.en.toml b/backend/errcode/locale.en.toml index ee4f2fca..9f4c9332 100644 --- a/backend/errcode/locale.en.toml +++ b/backend/errcode/locale.en.toml @@ -199,4 +199,31 @@ other = "Model not found" other = "Image not found" [err-model-access-denied] -other = "Model access denied" \ No newline at end of file +other = "Model access denied" + +[err-oidc-disabled] +other = "Enterprise login is disabled" + +[err-oidc-config-invalid] +other = "Enterprise login configuration is invalid" + +[err-oidc-state-invalid] +other = "Enterprise login state has expired" + +[err-oidc-token-invalid] +other = "Enterprise identity verification failed" + +[err-oidc-email-required] +other = "Enterprise identity did not return an email" + +[err-oidc-email-not-verified] +other = "Enterprise email is not verified" + +[err-oidc-email-domain-denied] +other = "Enterprise email domain is not allowed" + +[err-oidc-team-member-required] +other = "Account is not a member of this team" + +[err-password-login-disabled] +other = "Password login is disabled for this team" diff --git a/backend/errcode/locale.zh.toml b/backend/errcode/locale.zh.toml index 5b330452..b0e05e93 100644 --- a/backend/errcode/locale.zh.toml +++ b/backend/errcode/locale.zh.toml @@ -207,3 +207,30 @@ other = "镜像配置未找到" [err-model-access-denied] other = "无权使用该模型" + +[err-oidc-disabled] +other = "企业登录未启用" + +[err-oidc-config-invalid] +other = "企业登录配置无效" + +[err-oidc-state-invalid] +other = "企业登录状态已失效,请重新登录" + +[err-oidc-token-invalid] +other = "企业登录身份校验失败" + +[err-oidc-email-required] +other = "企业身份未返回邮箱" + +[err-oidc-email-not-verified] +other = "企业邮箱未验证" + +[err-oidc-email-domain-denied] +other = "企业邮箱域名不允许登录" + +[err-oidc-team-member-required] +other = "账号未加入该团队" + +[err-password-login-disabled] +other = "该团队已关闭账号密码登录" diff --git a/backend/go.mod b/backend/go.mod index bddda496..5eebb271 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -64,11 +64,13 @@ require ( github.com/bradleyfalzon/ghinstallation/v2 v2.17.0 // indirect github.com/buger/jsonparser v1.1.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/coreos/go-oidc/v3 v3.18.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/go-faster/city v1.0.1 // indirect github.com/go-faster/errors v0.7.1 // indirect + github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/inflect v0.19.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index 2a055005..eec832e1 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -88,6 +88,8 @@ github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d41+A= +github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -120,6 +122,8 @@ github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= diff --git a/backend/migration/000014_team_oidc_config.down.sql b/backend/migration/000014_team_oidc_config.down.sql new file mode 100644 index 00000000..2bfd9b80 --- /dev/null +++ b/backend/migration/000014_team_oidc_config.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS team_oidc_configs; diff --git a/backend/migration/000014_team_oidc_config.up.sql b/backend/migration/000014_team_oidc_config.up.sql new file mode 100644 index 00000000..20c480aa --- /dev/null +++ b/backend/migration/000014_team_oidc_config.up.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS team_oidc_configs ( + id uuid PRIMARY KEY DEFAULT uuid_generate_v1() NOT NULL, + team_id uuid NOT NULL, + enabled boolean DEFAULT false NOT NULL, + display_name text DEFAULT '企业登录' NOT NULL, + issuer text NOT NULL, + client_id text NOT NULL, + client_secret_ciphertext text DEFAULT '', + scopes text DEFAULT 'openid email profile' NOT NULL, + email_domain text DEFAULT '', + auto_create_member boolean DEFAULT false NOT NULL, + allow_password_login boolean DEFAULT true NOT NULL, + created_at timestamptz DEFAULT now() NOT NULL, + updated_at timestamptz DEFAULT now(), + CONSTRAINT team_oidc_configs_team_id_key UNIQUE (team_id), + CONSTRAINT team_oidc_configs_team_id_fkey FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE +); diff --git a/backend/migration/000015_alter_user_identities_identity_id_text.down.sql b/backend/migration/000015_alter_user_identities_identity_id_text.down.sql new file mode 100644 index 00000000..b187f88d --- /dev/null +++ b/backend/migration/000015_alter_user_identities_identity_id_text.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE user_identities + ALTER COLUMN identity_id TYPE character varying(64); diff --git a/backend/migration/000015_alter_user_identities_identity_id_text.up.sql b/backend/migration/000015_alter_user_identities_identity_id_text.up.sql new file mode 100644 index 00000000..7765c96f --- /dev/null +++ b/backend/migration/000015_alter_user_identities_identity_id_text.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE user_identities + ALTER COLUMN identity_id TYPE text; diff --git a/backend/migration/migration_test.go b/backend/migration/migration_test.go new file mode 100644 index 00000000..5b365788 --- /dev/null +++ b/backend/migration/migration_test.go @@ -0,0 +1,37 @@ +package migration + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestMigrationsUseDeletedAtForSoftDelete(t *testing.T) { + files, err := filepath.Glob("*.sql") + if err != nil { + t.Fatalf("glob migrations: %v", err) + } + for _, file := range files { + data, err := os.ReadFile(file) + if err != nil { + t.Fatalf("read %s: %v", file, err) + } + if strings.Contains(string(data), "delete_time") { + t.Fatalf("%s references delete_time; soft delete column is deleted_at", file) + } + } +} + +func TestMigrationsWidenUserIdentityIDForOIDC(t *testing.T) { + data, err := os.ReadFile("000015_alter_user_identities_identity_id_text.up.sql") + if err != nil { + t.Fatalf("read identity_id migration: %v", err) + } + sql := strings.ToLower(string(data)) + if !strings.Contains(sql, "alter table user_identities") || + !strings.Contains(sql, "alter column identity_id") || + !strings.Contains(sql, "type text") { + t.Fatalf("identity_id migration must alter user_identities.identity_id to text") + } +} diff --git a/backend/pkg/oidc/client.go b/backend/pkg/oidc/client.go new file mode 100644 index 00000000..20b6e5ab --- /dev/null +++ b/backend/pkg/oidc/client.go @@ -0,0 +1,187 @@ +package oidc + +import ( + "context" + "errors" + "net/http" + "strings" + + gooidc "github.com/coreos/go-oidc/v3/oidc" + "golang.org/x/oauth2" + + "github.com/chaitin/MonkeyCode/backend/domain" +) + +type Client struct { + httpClient *http.Client +} + +type Discovery struct { + Issuer string + AuthorizationEndpoint string + TokenEndpoint string + UserinfoEndpoint string + provider *gooidc.Provider +} + +type Config struct { + Issuer string + ClientID string + ClientSecret string + RedirectURL string + Scopes []string +} + +func NewClient(httpClient *http.Client) *Client { + if httpClient == nil { + httpClient = http.DefaultClient + } + return &Client{httpClient: httpClient} +} + +func NormalizeIssuer(issuer string) string { + return strings.TrimRight(strings.TrimSpace(issuer), "/") +} + +func CleanIssuer(issuer string) string { + return strings.TrimSpace(issuer) +} + +func SplitScopes(scopes string) []string { + fields := strings.Fields(scopes) + if len(fields) == 0 { + return []string{"openid", "email", "profile"} + } + return fields +} + +func (c *Client) Discover(ctx context.Context, issuer string) (*Discovery, error) { + issuer = CleanIssuer(issuer) + if issuer == "" { + return nil, errors.New("issuer is required") + } + ctx = context.WithValue(ctx, oauth2.HTTPClient, c.httpClient) + provider, err := gooidc.NewProvider(ctx, issuer) + if err != nil { + return nil, err + } + endpoint := provider.Endpoint() + var claims struct { + Issuer string `json:"issuer"` + UserinfoEndpoint string `json:"userinfo_endpoint"` + } + if err := provider.Claims(&claims); err != nil { + return nil, err + } + if CleanIssuer(claims.Issuer) != issuer { + return nil, errors.New("issuer mismatch") + } + if endpoint.AuthURL == "" || endpoint.TokenURL == "" { + return nil, errors.New("discovery missing required endpoint") + } + return &Discovery{ + Issuer: issuer, + AuthorizationEndpoint: endpoint.AuthURL, + TokenEndpoint: endpoint.TokenURL, + UserinfoEndpoint: claims.UserinfoEndpoint, + provider: provider, + }, nil +} + +func OAuthConfig(doc *Discovery, cfg Config) *oauth2.Config { + return &oauth2.Config{ + ClientID: cfg.ClientID, + ClientSecret: cfg.ClientSecret, + RedirectURL: cfg.RedirectURL, + Scopes: cfg.Scopes, + Endpoint: oauth2.Endpoint{ + AuthURL: doc.AuthorizationEndpoint, + TokenURL: doc.TokenEndpoint, + }, + } +} + +func IdentityID(issuer, subject string) string { + return NormalizeIssuer(issuer) + "#" + subject +} + +func ValidateExternalUser(u *domain.OIDCExternalUser) error { + if u == nil || u.Subject == "" { + return errors.New("subject is required") + } + if strings.TrimSpace(u.Email) == "" { + return errors.New("email is required") + } + if !u.EmailVerified { + return errors.New("email is not verified") + } + return nil +} + +func (c *Client) VerifyIDToken(ctx context.Context, doc *Discovery, cfg Config, rawIDToken string, nonce string) (*domain.OIDCExternalUser, error) { + if doc == nil || doc.provider == nil { + return nil, errors.New("provider is required") + } + verifier := doc.provider.Verifier(&gooidc.Config{ClientID: cfg.ClientID}) + idToken, err := verifier.Verify(ctx, rawIDToken) + if err != nil { + return nil, err + } + var claims struct { + Nonce string `json:"nonce"` + Email string `json:"email"` + EmailVerified bool `json:"email_verified"` + Name string `json:"name"` + PreferredUsername string `json:"preferred_username"` + Picture string `json:"picture"` + } + if err := idToken.Claims(&claims); err != nil { + return nil, err + } + if nonce != "" && claims.Nonce != nonce { + return nil, errors.New("nonce mismatch") + } + return &domain.OIDCExternalUser{ + Issuer: NormalizeIssuer(idToken.Issuer), + Subject: idToken.Subject, + Email: strings.TrimSpace(strings.ToLower(claims.Email)), + EmailVerified: claims.EmailVerified, + Name: claims.Name, + Username: claims.PreferredUsername, + AvatarURL: claims.Picture, + }, nil +} + +func (c *Client) UserInfo(ctx context.Context, doc *Discovery, tokenSource oauth2.TokenSource, external *domain.OIDCExternalUser) (*domain.OIDCExternalUser, error) { + if doc == nil || doc.provider == nil || tokenSource == nil { + return external, nil + } + info, err := doc.provider.UserInfo(ctx, tokenSource) + if err != nil { + return external, err + } + var claims struct { + Email string `json:"email"` + EmailVerified bool `json:"email_verified"` + Name string `json:"name"` + PreferredUsername string `json:"preferred_username"` + Picture string `json:"picture"` + } + if err := info.Claims(&claims); err != nil { + return external, err + } + if external.Email == "" { + external.Email = strings.TrimSpace(strings.ToLower(claims.Email)) + external.EmailVerified = claims.EmailVerified + } + if external.Name == "" { + external.Name = claims.Name + } + if external.Username == "" { + external.Username = claims.PreferredUsername + } + if external.AvatarURL == "" { + external.AvatarURL = claims.Picture + } + return external, nil +} diff --git a/backend/pkg/oidc/client_test.go b/backend/pkg/oidc/client_test.go new file mode 100644 index 00000000..da7d4644 --- /dev/null +++ b/backend/pkg/oidc/client_test.go @@ -0,0 +1,85 @@ +package oidc + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/chaitin/MonkeyCode/backend/domain" +) + +func TestNormalizeIssuer(t *testing.T) { + got := NormalizeIssuer("https://id.example.com/") + if got != "https://id.example.com" { + t.Fatalf("NormalizeIssuer = %q", got) + } +} + +func TestDiscoverFetchesConfiguration(t *testing.T) { + var issuer string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/.well-known/openid-configuration" { + t.Fatalf("path = %s", r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{ + "issuer":"` + issuer + `", + "authorization_endpoint":"` + issuer + `/authorize", + "token_endpoint":"` + issuer + `/token", + "jwks_uri":"` + issuer + `/jwks", + "userinfo_endpoint":"` + issuer + `/userinfo" + }`)) + })) + defer srv.Close() + issuer = srv.URL + + client := NewClient(http.DefaultClient) + doc, err := client.Discover(context.Background(), srv.URL) + if err != nil { + t.Fatal(err) + } + if doc.AuthorizationEndpoint != srv.URL+"/authorize" { + t.Fatalf("authorization endpoint = %q", doc.AuthorizationEndpoint) + } +} + +func TestDiscoverPreservesTrailingSlashIssuer(t *testing.T) { + var issuer string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/.well-known/openid-configuration" { + t.Fatalf("path = %s", r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{ + "issuer":"` + issuer + `", + "authorization_endpoint":"` + issuer + `authorize", + "token_endpoint":"` + issuer + `token", + "jwks_uri":"` + issuer + `jwks", + "userinfo_endpoint":"` + issuer + `userinfo" + }`)) + })) + defer srv.Close() + issuer = srv.URL + "/" + + client := NewClient(http.DefaultClient) + doc, err := client.Discover(context.Background(), issuer) + if err != nil { + t.Fatal(err) + } + if doc.Issuer != issuer { + t.Fatalf("issuer = %q, want %q", doc.Issuer, issuer) + } +} + +func TestValidateExternalUserRequiresVerifiedEmail(t *testing.T) { + err := ValidateExternalUser(&domain.OIDCExternalUser{ + Issuer: "https://id.example.com", + Subject: "sub-1", + Email: "alice@example.com", + EmailVerified: false, + }) + if err == nil { + t.Fatal("expected unverified email error") + } +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e66d959f..6b47d54e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -20,6 +20,8 @@ import TeamManagerOverview from "./pages/console/manager/overview" import TeamManagerProjects from "./pages/console/manager/projects" import TeamManagerTasks from "./pages/console/manager/tasks" import TeamManagerConversations from "./pages/console/manager/conversations" +import TeamManagerOIDC from "./pages/console/manager/oidc" +import TeamOIDCLoginPage from "./pages/team-oidc-login" import ResetPasswordPage from "./pages/resetpassword" import FindPasswordPage from "./pages/findpassword" import TeamManagerManager from "./pages/console/manager/manager" @@ -52,6 +54,7 @@ function App() { } /> } /> } /> + } /> } /> } /> }> @@ -77,6 +80,7 @@ function App() { } /> } /> } /> + } /> } /> diff --git a/frontend/src/api/Api.ts b/frontend/src/api/Api.ts index 8603e7c8..608056a0 100644 --- a/frontend/src/api/Api.ts +++ b/frontend/src/api/Api.ts @@ -201,6 +201,7 @@ export enum ConstsUserPlatform { UserPlatformGitLab = "gitlab", UserPlatformGitea = "gitea", UserPlatformGitee = "gitee", + UserPlatformOIDC = "oidc", } export enum ConstsUserRole { @@ -1906,6 +1907,51 @@ export interface DomainUpdateTeamOAuthSiteReq { proxy_url?: string; } +export interface DomainSaveTeamOIDCConfigReq { + allow_password_login?: boolean; + auto_create_member?: boolean; + client_id: string; + client_secret?: string; + display_name: string; + email_domain?: string; + enabled?: boolean; + issuer: string; + scopes?: string; +} + +export interface DomainTeamOIDCConfig { + allow_password_login?: boolean; + auto_create_member?: boolean; + client_id?: string; + display_name?: string; + email_domain?: string; + enabled?: boolean; + has_client_secret?: boolean; + id?: string; + issuer?: string; + login_url?: string; + redirect_uri?: string; + scopes?: string; + team_id?: string; +} + +export interface DomainTeamOIDCConfigResp { + config?: DomainTeamOIDCConfig; +} + +export interface DomainTeamOIDCPublicConfigResp { + display_name?: string; + enabled?: boolean; + login_url?: string; + team_id?: string; +} + +export interface DomainTeamOIDCTestResp { + issuer?: string; + message?: string; + success?: boolean; +} + export interface DomainUpdateTeamUserReq { is_blocked?: boolean; } @@ -3453,6 +3499,80 @@ export class Api extends HttpClient + this.request< + GithubComGoYokoWebResp & { + data?: DomainTeamOIDCConfigResp; + }, + GithubComGoYokoWebResp + >({ + path: `/api/v1/teams/oidc`, + method: "GET", + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + + /** + * @description 新增或更新当前团队企业登录 OIDC 配置 + * + * @tags 【Team 管理员】企业登录 + * @name V1TeamsOidcUpdate + * @summary 保存团队 OIDC 配置 + * @request PUT:/api/v1/teams/oidc + * @secure + */ + v1TeamsOidcUpdate: (req: DomainSaveTeamOIDCConfigReq, params: RequestParams = {}) => + this.request< + GithubComGoYokoWebResp & { + data?: DomainTeamOIDCConfigResp; + }, + GithubComGoYokoWebResp + >({ + path: `/api/v1/teams/oidc`, + method: "PUT", + body: req, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + + /** + * @description 拉取 OIDC discovery 文档验证配置可用性 + * + * @tags 【Team 管理员】企业登录 + * @name V1TeamsOidcTestCreate + * @summary 测试团队 OIDC 配置 + * @request POST:/api/v1/teams/oidc/test + * @secure + */ + v1TeamsOidcTestCreate: (req: DomainSaveTeamOIDCConfigReq, params: RequestParams = {}) => + this.request< + GithubComGoYokoWebResp & { + data?: DomainTeamOIDCTestResp; + }, + GithubComGoYokoWebResp + >({ + path: `/api/v1/teams/oidc/test`, + method: "POST", + body: req, + secure: true, + type: ContentType.Json, + format: "json", + ...params, + }), + /** * @description 获取团队模型配置列表 * @@ -5251,6 +5371,73 @@ export class Api extends HttpClient + this.request({ + path: `/api/v1/users/oidc/callback`, + method: "GET", + query: query, + type: ContentType.Json, + ...params, + }), + + /** + * @description 根据 team_id 跳转到团队 OIDC 身份源 + * + * @tags 【用户】企业团队成员认证 + * @name V1UsersOidcLoginList + * @summary 发起团队 OIDC 登录 + * @request GET:/api/v1/users/oidc/login + */ + v1UsersOidcLoginList: ( + query: { + team_id: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/api/v1/users/oidc/login`, + method: "GET", + query: query, + type: ContentType.Json, + ...params, + }), + + /** + * @description 用于团队专属登录页展示企业登录入口 + * + * @tags 【用户】企业团队成员认证 + * @name V1UsersOidcTeamsDetail + * @summary 获取团队公开 OIDC 登录配置 + * @request GET:/api/v1/users/oidc/teams/{team_id} + */ + v1UsersOidcTeamsDetail: (teamId: string, params: RequestParams = {}) => + this.request< + GithubComGoYokoWebResp & { + data?: DomainTeamOIDCPublicConfigResp; + }, + GithubComGoYokoWebResp + >({ + path: `/api/v1/users/oidc/teams/${teamId}`, + method: "GET", + type: ContentType.Json, + format: "json", + ...params, + }), + /** * @description 清除用户会话,登出系统 * diff --git a/frontend/src/components/manager/nav-teams.tsx b/frontend/src/components/manager/nav-teams.tsx index ff8afa77..aff6d38f 100644 --- a/frontend/src/components/manager/nav-teams.tsx +++ b/frontend/src/components/manager/nav-teams.tsx @@ -8,7 +8,7 @@ import { SidebarMenuItem, } from "@/components/ui/sidebar" import { IconReport, IconUsersGroup } from "@tabler/icons-react" -import { Bot, Box, FolderGit2, LayoutDashboard, ListTodo, MessagesSquare, MonitorCloud, User } from "lucide-react" +import { Bot, Box, FolderGit2, LayoutDashboard, ListTodo, MessagesSquare, MonitorCloud, ShieldCheck, User } from "lucide-react" export default function NavTeams() { const location = useLocation() @@ -116,6 +116,17 @@ export default function NavTeams() { + + + + + 企业登录 + + + (defaultForm) + const [saving, setSaving] = React.useState(false) + const [testing, setTesting] = React.useState(false) + + const load = React.useCallback(async () => { + await apiRequest('v1TeamsOidcList', {}, [], (resp) => { + const cfg = resp.data?.config + if (cfg) setForm({ ...defaultForm, ...cfg, client_secret: '' }) + }) + }, []) + + React.useEffect(() => { + load() + }, [load]) + + const update = (key: K, value: OIDCForm[K]) => { + setForm((prev) => ({ ...prev, [key]: value })) + } + + const copy = async (value?: string) => { + if (!value) return + await navigator.clipboard.writeText(value) + toast.success('已复制') + } + + const save = async () => { + setSaving(true) + await apiRequest('v1TeamsOidcUpdate', form, [], (resp) => { + if (resp.code === 0) { + toast.success('企业登录配置已保存') + const cfg = resp.data?.config + if (cfg) setForm({ ...defaultForm, ...cfg, client_secret: '' }) + } else { + toast.error(resp.message || '保存失败') + } + }) + setSaving(false) + } + + const test = async () => { + setTesting(true) + await apiRequest('v1TeamsOidcTestCreate', form, [], (resp) => { + if (resp.code === 0) toast.success('连接测试通过') + else toast.error(resp.message || '连接测试失败') + }) + setTesting(false) + } + + return ( +
+
+

企业登录

+

配置团队成员使用企业 OIDC 身份源登录。

+
+ + + + + + OIDC 配置 + + + + +
+ + 启用企业登录 + update('enabled', value)} /> + + + 允许自动创建成员 + update('auto_create_member', value)} /> + + + 允许账号密码登录 + update('allow_password_login', value)} /> + + + 登录按钮名称 + update('display_name', event.target.value)} /> + +
+ + + Issuer + update('issuer', event.target.value)} /> + + + Client ID + update('client_id', event.target.value)} /> + + + Client Secret{form.has_client_secret ? '(已配置)' : ''} + update('client_secret', event.target.value)} + placeholder={form.has_client_secret ? '留空表示不修改' : ''} + /> + + + Scopes + update('scopes', event.target.value)} /> + + + 邮箱域名限制 + update('email_domain', event.target.value)} /> + +
+ +
+ + +
+ +
+ + +
+
+
+
+ ) +} + +function ReadonlyCopy({ label, value, onCopy }: { label: string; value?: string; onCopy: (value?: string) => void }) { + return ( + + {label} +
+ + +
+
+ ) +} diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx index 83d07df6..19b7e76b 100644 --- a/frontend/src/pages/login.tsx +++ b/frontend/src/pages/login.tsx @@ -25,6 +25,7 @@ import { Link, useNavigate } from "react-router-dom" import { captchaChallenge } from "@/utils/common" import { ArrowLeft, Eye, EyeOff } from "lucide-react" import { IS_OFFLINE_EDITION } from "@/utils/edition" +import type { DomainTeamOIDCPublicConfigResp, GithubComGoYokoWebResp } from "@/api/Api" const USER_STORAGE_KEY = 'login_user' const MANAGER_STORAGE_KEY = 'login_manager' @@ -42,9 +43,11 @@ export default function LoginPage({ const [showManagerPassword, setShowManagerPassword] = React.useState(false) const [userLoginView, setUserLoginView] = React.useState<'choices' | 'password'>('choices') const [agreedToTerms, setAgreedToTerms] = React.useState(true) + const [defaultOIDCConfig, setDefaultOIDCConfig] = React.useState(null) const navigate = useNavigate() const inviterId = typeof window !== 'undefined' ? (localStorage.getItem('ic') || '') : '' const userLoginHref = `/api/v1/users/login?redirect=&inviter_id=${inviterId}` + const defaultOIDCLoginURL = defaultOIDCConfig?.enabled ? defaultOIDCConfig.login_url : '' const ensureTermsAccepted = React.useCallback(() => { if (agreedToTerms) return true @@ -71,6 +74,27 @@ export default function LoginPage({ } }, []) + React.useEffect(() => { + if (!IS_OFFLINE_EDITION) return + + const controller = new AbortController() + fetch('/api/v1/users/oidc/default-team', { signal: controller.signal }) + .then(async (resp) => { + if (!resp.ok) return + const body = await resp.json() as GithubComGoYokoWebResp & { data?: DomainTeamOIDCPublicConfigResp } + if (body.code === 0 && body.data?.enabled && body.data.login_url) { + setDefaultOIDCConfig(body.data) + } + }) + .catch((err) => { + if ((err as Error).name !== 'AbortError') { + console.warn('load default oidc config failed', err) + } + }) + + return () => controller.abort() + }, []) + const handleUserLogin = async () => { if (!ensureTermsAccepted()) return @@ -166,6 +190,20 @@ export default function LoginPage({ )} + {IS_OFFLINE_EDITION && defaultOIDCLoginURL && ( + + )} + ) : ( +
该团队未启用企业登录。
+ )} + + + + + ) +}