mirror of https://github.com/harness/drone.git
feat: [CODE-2232]: Branch Rules: UserGroup support: Create and List (#2640)
* feat: [CODE-2327]: add usergroup change to rebase api * Merge branch 'main' into akp/CODE-2327 * feat: [CODE-2327]: wrap error * feat: [CODE-2327]: move resolver inside bypass.go and update tests * feat: [CODE-2327]: newline * feat: [CODE-2327]: update wire * Merge remote-tracking branch 'origin' into akp/CODE-2327 * feat: [CODE-2327]: update tests * feat: [CODE-2327]: fix build * feat: [CODE-2327]: merge main * feat: [CODE-2327]: add and update unit tests * feat: [CODE-2327]: fix * feat: [CODE-2312]: introduce parser and enclosing method * Apply suggestion from code review * feat: [CODE-2327]: annotate error * Merge branch 'main' into akp/CODE-2327 * feat: [CODE-2327]: usergroup resolver via controller * feat: [CODE-2312]: update interface in protection * feat: [CODE-2312]: export and update deduplication use * feat: [CODE-2327]: Branch Rules: Allow Group bypass.go * feat: [CODE-2232]: Branch Rules: UserGroup support: Create and ListCODE-2402
parent
16cb3d5bc1
commit
a2cea52155
|
@ -55,10 +55,11 @@ func (c *Controller) ListChecks(
|
||||||
}
|
}
|
||||||
|
|
||||||
reqChecks, err := protectionRules.RequiredChecks(ctx, protection.RequiredChecksInput{
|
reqChecks, err := protectionRules.RequiredChecks(ctx, protection.RequiredChecksInput{
|
||||||
Actor: &session.Principal,
|
ResolveUserGroupID: c.userGroupService.ListUserIDsByGroupIDs,
|
||||||
IsRepoOwner: isRepoOwner,
|
Actor: &session.Principal,
|
||||||
Repo: repo,
|
IsRepoOwner: isRepoOwner,
|
||||||
PullReq: pr,
|
Repo: repo,
|
||||||
|
PullReq: pr,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.PullReqChecks{}, fmt.Errorf("failed to get identifiers of required checks: %w", err)
|
return types.PullReqChecks{}, fmt.Errorf("failed to get identifiers of required checks: %w", err)
|
||||||
|
|
|
@ -204,16 +204,17 @@ func (c *Controller) Merge(
|
||||||
}
|
}
|
||||||
|
|
||||||
ruleOut, violations, err := protectionRules.MergeVerify(ctx, protection.MergeVerifyInput{
|
ruleOut, violations, err := protectionRules.MergeVerify(ctx, protection.MergeVerifyInput{
|
||||||
Actor: &session.Principal,
|
ResolveUserGroupID: c.userGroupService.ListUserIDsByGroupIDs,
|
||||||
AllowBypass: in.BypassRules,
|
Actor: &session.Principal,
|
||||||
IsRepoOwner: isRepoOwner,
|
AllowBypass: in.BypassRules,
|
||||||
TargetRepo: targetRepo,
|
IsRepoOwner: isRepoOwner,
|
||||||
SourceRepo: sourceRepo,
|
TargetRepo: targetRepo,
|
||||||
PullReq: pr,
|
SourceRepo: sourceRepo,
|
||||||
Reviewers: reviewers,
|
PullReq: pr,
|
||||||
Method: in.Method,
|
Reviewers: reviewers,
|
||||||
CheckResults: checkResults,
|
Method: in.Method,
|
||||||
CodeOwners: codeOwnerWithApproval,
|
CheckResults: checkResults,
|
||||||
|
CodeOwners: codeOwnerWithApproval,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to verify protection rules: %w", err)
|
return nil, nil, fmt.Errorf("failed to verify protection rules: %w", err)
|
||||||
|
|
|
@ -83,13 +83,14 @@ func (c *Controller) CommitFiles(ctx context.Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
|
violations, err := rules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
|
||||||
Actor: &session.Principal,
|
ResolveUserGroupID: c.userGroupService.ListUserIDsByGroupIDs,
|
||||||
AllowBypass: in.BypassRules,
|
Actor: &session.Principal,
|
||||||
IsRepoOwner: isRepoOwner,
|
AllowBypass: in.BypassRules,
|
||||||
Repo: repo,
|
IsRepoOwner: isRepoOwner,
|
||||||
RefAction: refAction,
|
Repo: repo,
|
||||||
RefType: protection.RefTypeBranch,
|
RefAction: refAction,
|
||||||
RefNames: []string{branchName},
|
RefType: protection.RefTypeBranch,
|
||||||
|
RefNames: []string{branchName},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.CommitFilesResponse{}, nil, fmt.Errorf("failed to verify protection rules: %w", err)
|
return types.CommitFilesResponse{}, nil, fmt.Errorf("failed to verify protection rules: %w", err)
|
||||||
|
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"github.com/harness/gitness/app/services/protection"
|
"github.com/harness/gitness/app/services/protection"
|
||||||
"github.com/harness/gitness/app/services/publicaccess"
|
"github.com/harness/gitness/app/services/publicaccess"
|
||||||
"github.com/harness/gitness/app/services/settings"
|
"github.com/harness/gitness/app/services/settings"
|
||||||
|
"github.com/harness/gitness/app/services/usergroup"
|
||||||
"github.com/harness/gitness/app/store"
|
"github.com/harness/gitness/app/store"
|
||||||
"github.com/harness/gitness/app/url"
|
"github.com/harness/gitness/app/url"
|
||||||
"github.com/harness/gitness/audit"
|
"github.com/harness/gitness/audit"
|
||||||
|
@ -82,6 +83,7 @@ type Controller struct {
|
||||||
settings *settings.Service
|
settings *settings.Service
|
||||||
principalInfoCache store.PrincipalInfoCache
|
principalInfoCache store.PrincipalInfoCache
|
||||||
userGroupStore store.UserGroupStore
|
userGroupStore store.UserGroupStore
|
||||||
|
userGroupService usergroup.SearchService
|
||||||
protectionManager *protection.Manager
|
protectionManager *protection.Manager
|
||||||
git git.Interface
|
git git.Interface
|
||||||
importer *importer.Repository
|
importer *importer.Repository
|
||||||
|
@ -127,6 +129,7 @@ func NewController(
|
||||||
labelSvc *label.Service,
|
labelSvc *label.Service,
|
||||||
instrumentation instrument.Service,
|
instrumentation instrument.Service,
|
||||||
userGroupStore store.UserGroupStore,
|
userGroupStore store.UserGroupStore,
|
||||||
|
userGroupService usergroup.SearchService,
|
||||||
) *Controller {
|
) *Controller {
|
||||||
return &Controller{
|
return &Controller{
|
||||||
defaultBranch: config.Git.DefaultBranch,
|
defaultBranch: config.Git.DefaultBranch,
|
||||||
|
@ -156,6 +159,7 @@ func NewController(
|
||||||
labelSvc: labelSvc,
|
labelSvc: labelSvc,
|
||||||
instrumentation: instrumentation,
|
instrumentation: instrumentation,
|
||||||
userGroupStore: userGroupStore,
|
userGroupStore: userGroupStore,
|
||||||
|
userGroupService: userGroupService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,12 +241,31 @@ func (c *Controller) fetchRules(
|
||||||
return protectionRules, isRepoOwner, nil
|
return protectionRules, isRepoOwner, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) getRuleUsers(ctx context.Context, r *types.Rule) (map[int64]*types.PrincipalInfo, error) {
|
func (c *Controller) getRuleUserAndUserGroups(
|
||||||
rule, err := c.protectionManager.FromJSON(r.Type, r.Definition, false)
|
ctx context.Context,
|
||||||
|
r *types.Rule,
|
||||||
|
) (map[int64]*types.PrincipalInfo, map[int64]*types.UserGroupInfo, error) {
|
||||||
|
rule, err := c.parseRule(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse json rule definition: %w", err)
|
return nil, nil, fmt.Errorf("failed to parse rule: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userMap, err := c.getRuleUsers(ctx, rule)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to get rule users: %w", err)
|
||||||
|
}
|
||||||
|
userGroupMap, err := c.getRuleUserGroups(ctx, rule)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to get rule user groups: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return userMap, userGroupMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) getRuleUsers(
|
||||||
|
ctx context.Context,
|
||||||
|
rule protection.Protection,
|
||||||
|
) (map[int64]*types.PrincipalInfo, error) {
|
||||||
userIDs, err := rule.UserIDs()
|
userIDs, err := rule.UserIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get user ID from rule: %w", err)
|
return nil, fmt.Errorf("failed to get user ID from rule: %w", err)
|
||||||
|
@ -256,12 +279,10 @@ func (c *Controller) getRuleUsers(ctx context.Context, r *types.Rule) (map[int64
|
||||||
return userMap, nil
|
return userMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) getRuleUserGroups(ctx context.Context, r *types.Rule) (map[int64]*types.UserGroupInfo, error) {
|
func (c *Controller) getRuleUserGroups(
|
||||||
rule, err := c.protectionManager.FromJSON(r.Type, r.Definition, false)
|
ctx context.Context,
|
||||||
if err != nil {
|
rule protection.Protection,
|
||||||
return nil, fmt.Errorf("failed to parse json rule definition: %w", err)
|
) (map[int64]*types.UserGroupInfo, error) {
|
||||||
}
|
|
||||||
|
|
||||||
groupIDs, err := rule.UserGroupIDs()
|
groupIDs, err := rule.UserGroupIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get group IDs from rule: %w", err)
|
return nil, fmt.Errorf("failed to get group IDs from rule: %w", err)
|
||||||
|
@ -283,3 +304,12 @@ func (c *Controller) getRuleUserGroups(ctx context.Context, r *types.Rule) (map[
|
||||||
}
|
}
|
||||||
return userGroupInfoMap, nil
|
return userGroupInfoMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) parseRule(r *types.Rule) (protection.Protection, error) {
|
||||||
|
rule, err := c.protectionManager.FromJSON(r.Type, r.Definition, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse json rule definition: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rule, nil
|
||||||
|
}
|
||||||
|
|
|
@ -82,13 +82,14 @@ func (c *Controller) Rebase(
|
||||||
}
|
}
|
||||||
|
|
||||||
violations, err := protectionRules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
|
violations, err := protectionRules.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
|
||||||
Actor: &session.Principal,
|
ResolveUserGroupID: c.userGroupService.ListUserIDsByGroupIDs,
|
||||||
AllowBypass: in.BypassRules,
|
Actor: &session.Principal,
|
||||||
IsRepoOwner: isRepoOwner,
|
AllowBypass: in.BypassRules,
|
||||||
Repo: repo,
|
IsRepoOwner: isRepoOwner,
|
||||||
RefAction: protection.RefActionUpdate,
|
Repo: repo,
|
||||||
RefType: protection.RefTypeBranch,
|
RefAction: protection.RefActionUpdate,
|
||||||
RefNames: []string{in.HeadBranch},
|
RefType: protection.RefTypeBranch,
|
||||||
|
RefNames: []string{in.HeadBranch},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to verify protection rules: %w", err)
|
return nil, nil, fmt.Errorf("failed to verify protection rules: %w", err)
|
||||||
|
|
|
@ -142,15 +142,13 @@ func (c *Controller) RuleCreate(ctx context.Context,
|
||||||
log.Ctx(ctx).Warn().Msgf("failed to insert instrumentation record for create branch rule operation: %s", err)
|
log.Ctx(ctx).Warn().Msgf("failed to insert instrumentation record for create branch rule operation: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Users, err = c.getRuleUsers(ctx, r)
|
userMap, userGroupMap, err := c.getRuleUserAndUserGroups(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to get rule users and user groups: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.UserGroups, err = c.getRuleUserGroups(ctx, r)
|
r.Users = userMap
|
||||||
if err != nil {
|
r.UserGroups = userGroupMap
|
||||||
return nil, fmt.Errorf("failed to get rule user groups: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,15 +39,13 @@ func (c *Controller) RuleFind(ctx context.Context,
|
||||||
return nil, fmt.Errorf("failed to find repository-level protection rule by identifier: %w", err)
|
return nil, fmt.Errorf("failed to find repository-level protection rule by identifier: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Users, err = c.getRuleUsers(ctx, r)
|
userMap, userGroupMap, err := c.getRuleUserAndUserGroups(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to get rule users and user groups: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.UserGroups, err = c.getRuleUserGroups(ctx, r)
|
r.Users = userMap
|
||||||
if err != nil {
|
r.UserGroups = userGroupMap
|
||||||
return nil, fmt.Errorf("failed to get rule user groups: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (c *Controller) RuleList(ctx context.Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range list {
|
for i := range list {
|
||||||
list[i].Users, err = c.getRuleUsers(ctx, &list[i])
|
list[i].Users, list[i].UserGroups, err = c.getRuleUserAndUserGroups(ctx, &list[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,14 +102,14 @@ func (c *Controller) RuleUpdate(ctx context.Context,
|
||||||
}
|
}
|
||||||
oldRule := r.Clone()
|
oldRule := r.Clone()
|
||||||
if in.isEmpty() {
|
if in.isEmpty() {
|
||||||
r.Users, err = c.getRuleUsers(ctx, r)
|
userMap, userGroupMap, err := c.getRuleUserAndUserGroups(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to get rule users and user groups: %w", err)
|
||||||
}
|
|
||||||
r.UserGroups, err = c.getRuleUserGroups(ctx, r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get rule user groups: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.Users = userMap
|
||||||
|
r.UserGroups = userGroupMap
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,15 +132,13 @@ func (c *Controller) RuleUpdate(ctx context.Context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Users, err = c.getRuleUsers(ctx, r)
|
userMap, userGroupMap, err := c.getRuleUserAndUserGroups(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get rule users: %w", err)
|
return nil, fmt.Errorf("failed to get rule users and user groups: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.UserGroups, err = c.getRuleUserGroups(ctx, r)
|
r.Users = userMap
|
||||||
if err != nil {
|
r.UserGroups = userGroupMap
|
||||||
return nil, fmt.Errorf("failed to get rule user groups: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.ruleStore.Update(ctx, r)
|
err = c.ruleStore.Update(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/harness/gitness/app/services/protection"
|
"github.com/harness/gitness/app/services/protection"
|
||||||
"github.com/harness/gitness/app/services/publicaccess"
|
"github.com/harness/gitness/app/services/publicaccess"
|
||||||
"github.com/harness/gitness/app/services/settings"
|
"github.com/harness/gitness/app/services/settings"
|
||||||
|
"github.com/harness/gitness/app/services/usergroup"
|
||||||
"github.com/harness/gitness/app/store"
|
"github.com/harness/gitness/app/store"
|
||||||
"github.com/harness/gitness/app/url"
|
"github.com/harness/gitness/app/url"
|
||||||
"github.com/harness/gitness/audit"
|
"github.com/harness/gitness/audit"
|
||||||
|
@ -72,13 +73,14 @@ func ProvideController(
|
||||||
labelSvc *label.Service,
|
labelSvc *label.Service,
|
||||||
instrumentation instrument.Service,
|
instrumentation instrument.Service,
|
||||||
userGroupStore store.UserGroupStore,
|
userGroupStore store.UserGroupStore,
|
||||||
|
userGroupService usergroup.SearchService,
|
||||||
) *Controller {
|
) *Controller {
|
||||||
return NewController(config, tx, urlProvider,
|
return NewController(config, tx, urlProvider,
|
||||||
authorizer,
|
authorizer,
|
||||||
repoStore, spaceStore, pipelineStore,
|
repoStore, spaceStore, pipelineStore,
|
||||||
principalStore, ruleStore, settings, principalInfoCache, protectionManager, rpcClient, importer,
|
principalStore, ruleStore, settings, principalInfoCache, protectionManager, rpcClient, importer,
|
||||||
codeOwners, reporeporter, indexer, limiter, locker, auditService, mtxManager, identifierCheck,
|
codeOwners, reporeporter, indexer, limiter, locker, auditService, mtxManager, identifierCheck,
|
||||||
repoChecks, publicAccess, labelSvc, instrumentation, userGroupStore)
|
repoChecks, publicAccess, labelSvc, instrumentation, userGroupStore, userGroupService)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideRepoCheck() Check {
|
func ProvideRepoCheck() Check {
|
||||||
|
|
|
@ -15,8 +15,10 @@
|
||||||
package protection
|
package protection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/cache"
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
|
@ -28,7 +30,21 @@ type DefBypass struct {
|
||||||
RepoOwners bool `json:"repo_owners,omitempty"`
|
RepoOwners bool `json:"repo_owners,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v DefBypass) matches(actor *types.Principal, isRepoOwner bool) bool {
|
func (v DefBypass) matches(
|
||||||
|
ctx context.Context,
|
||||||
|
actor *types.Principal,
|
||||||
|
isRepoOwner bool,
|
||||||
|
userGroupResolverFn func(context.Context, []int64) ([]int64, error),
|
||||||
|
) bool {
|
||||||
|
if userGroupResolverFn != nil {
|
||||||
|
userIDs, err := userGroupResolverFn(ctx, v.UserGroupIDs)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
v.UserIDs = append(v.UserIDs, userIDs...)
|
||||||
|
v.UserIDs = cache.Deduplicate(v.UserIDs)
|
||||||
|
}
|
||||||
return actor != nil &&
|
return actor != nil &&
|
||||||
(v.RepoOwners && isRepoOwner ||
|
(v.RepoOwners && isRepoOwner ||
|
||||||
slices.Contains(v.UserIDs, actor.ID))
|
slices.Contains(v.UserIDs, actor.ID))
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package protection
|
package protection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
|
@ -78,7 +79,7 @@ func TestBranch_matches(t *testing.T) {
|
||||||
t.Errorf("invalid: %s", err.Error())
|
t.Errorf("invalid: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if want, got := test.exp, test.bypass.matches(test.actor, test.owner); want != got {
|
if want, got := test.exp, test.bypass.matches(context.TODO(), test.actor, test.owner, nil); want != got {
|
||||||
t.Errorf("want=%t got=%t", want, got)
|
t.Errorf("want=%t got=%t", want, got)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -41,10 +41,10 @@ func (v *Branch) MergeVerify(
|
||||||
) (out MergeVerifyOutput, violations []types.RuleViolations, err error) {
|
) (out MergeVerifyOutput, violations []types.RuleViolations, err error) {
|
||||||
out, violations, err = v.PullReq.MergeVerify(ctx, in)
|
out, violations, err = v.PullReq.MergeVerify(ctx, in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return out, violations, fmt.Errorf("merge verify error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bypassable := v.Bypass.matches(in.Actor, in.IsRepoOwner)
|
bypassable := v.Bypass.matches(ctx, in.Actor, in.IsRepoOwner, in.ResolveUserGroupID)
|
||||||
bypassed := in.AllowBypass && bypassable
|
bypassed := in.AllowBypass && bypassable
|
||||||
for i := range violations {
|
for i := range violations {
|
||||||
violations[i].Bypassable = bypassable
|
violations[i].Bypassable = bypassable
|
||||||
|
@ -73,7 +73,7 @@ func (v *Branch) RequiredChecks(
|
||||||
bypassableIDs map[string]struct{}
|
bypassableIDs map[string]struct{}
|
||||||
)
|
)
|
||||||
|
|
||||||
if bypassable := v.Bypass.matches(in.Actor, in.IsRepoOwner); bypassable {
|
if bypassable := v.Bypass.matches(ctx, in.Actor, in.IsRepoOwner, in.ResolveUserGroupID); bypassable {
|
||||||
bypassableIDs = ids
|
bypassableIDs = ids
|
||||||
} else {
|
} else {
|
||||||
requiredIDs = ids
|
requiredIDs = ids
|
||||||
|
@ -94,8 +94,11 @@ func (v *Branch) RefChangeVerify(
|
||||||
}
|
}
|
||||||
|
|
||||||
violations, err = v.Lifecycle.RefChangeVerify(ctx, in)
|
violations, err = v.Lifecycle.RefChangeVerify(ctx, in)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("lifecycle error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
bypassable := v.Bypass.matches(in.Actor, in.IsRepoOwner)
|
bypassable := v.Bypass.matches(ctx, in.Actor, in.IsRepoOwner, in.ResolveUserGroupID)
|
||||||
bypassed := in.AllowBypass && bypassable
|
bypassed := in.AllowBypass && bypassable
|
||||||
for i := range violations {
|
for i := range violations {
|
||||||
violations[i].Bypassable = bypassable
|
violations[i].Bypassable = bypassable
|
||||||
|
|
|
@ -182,10 +182,11 @@ func TestBranch_MergeVerify(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
in: MergeVerifyInput{
|
in: MergeVerifyInput{
|
||||||
Actor: user,
|
Actor: user,
|
||||||
CodeOwners: &codeowners.Evaluation{},
|
ResolveUserGroupID: mockUserGroupResolver,
|
||||||
PullReq: &types.PullReq{},
|
CodeOwners: &codeowners.Evaluation{},
|
||||||
Reviewers: []*types.PullReqReviewer{},
|
PullReq: &types.PullReq{},
|
||||||
|
Reviewers: []*types.PullReqReviewer{},
|
||||||
},
|
},
|
||||||
expOut: MergeVerifyOutput{
|
expOut: MergeVerifyOutput{
|
||||||
DeleteSourceBranch: true,
|
DeleteSourceBranch: true,
|
||||||
|
@ -480,6 +481,31 @@ func TestBranch_RefChangeVerify(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "usergroup-bypass",
|
||||||
|
branch: Branch{
|
||||||
|
Bypass: DefBypass{RepoOwners: true},
|
||||||
|
Lifecycle: DefLifecycle{DeleteForbidden: true},
|
||||||
|
},
|
||||||
|
in: RefChangeVerifyInput{
|
||||||
|
Actor: &types.Principal{ID: 43},
|
||||||
|
ResolveUserGroupID: mockUserGroupResolver,
|
||||||
|
AllowBypass: true,
|
||||||
|
IsRepoOwner: false,
|
||||||
|
RefAction: RefActionDelete,
|
||||||
|
RefType: RefTypeBranch,
|
||||||
|
RefNames: []string{"abc"},
|
||||||
|
},
|
||||||
|
expVs: []types.RuleViolations{
|
||||||
|
{
|
||||||
|
Bypassable: true,
|
||||||
|
Bypassed: true,
|
||||||
|
Violations: []types.Violation{
|
||||||
|
{Code: codeLifecycleDelete},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -527,3 +553,7 @@ func TestBranch_RefChangeVerify(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mockUserGroupResolver(_ context.Context, _ []int64) ([]int64, error) {
|
||||||
|
return []int64{43}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -26,13 +26,14 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
RefChangeVerifyInput struct {
|
RefChangeVerifyInput struct {
|
||||||
Actor *types.Principal
|
ResolveUserGroupID func(ctx context.Context, userGroupIDs []int64) ([]int64, error)
|
||||||
AllowBypass bool
|
Actor *types.Principal
|
||||||
IsRepoOwner bool
|
AllowBypass bool
|
||||||
Repo *types.Repository
|
IsRepoOwner bool
|
||||||
RefAction RefAction
|
Repo *types.Repository
|
||||||
RefType RefType
|
RefAction RefAction
|
||||||
RefNames []string
|
RefType RefType
|
||||||
|
RefNames []string
|
||||||
}
|
}
|
||||||
|
|
||||||
RefType int
|
RefType int
|
||||||
|
|
|
@ -35,16 +35,17 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
MergeVerifyInput struct {
|
MergeVerifyInput struct {
|
||||||
Actor *types.Principal
|
ResolveUserGroupID func(ctx context.Context, userGroupIDs []int64) ([]int64, error)
|
||||||
AllowBypass bool
|
Actor *types.Principal
|
||||||
IsRepoOwner bool
|
AllowBypass bool
|
||||||
TargetRepo *types.Repository
|
IsRepoOwner bool
|
||||||
SourceRepo *types.Repository
|
TargetRepo *types.Repository
|
||||||
PullReq *types.PullReq
|
SourceRepo *types.Repository
|
||||||
Reviewers []*types.PullReqReviewer
|
PullReq *types.PullReq
|
||||||
Method enum.MergeMethod
|
Reviewers []*types.PullReqReviewer
|
||||||
CheckResults []types.CheckResult
|
Method enum.MergeMethod
|
||||||
CodeOwners *codeowners.Evaluation
|
CheckResults []types.CheckResult
|
||||||
|
CodeOwners *codeowners.Evaluation
|
||||||
}
|
}
|
||||||
|
|
||||||
MergeVerifyOutput struct {
|
MergeVerifyOutput struct {
|
||||||
|
@ -59,10 +60,11 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
RequiredChecksInput struct {
|
RequiredChecksInput struct {
|
||||||
Actor *types.Principal
|
ResolveUserGroupID func(ctx context.Context, userGroupIDs []int64) ([]int64, error)
|
||||||
IsRepoOwner bool
|
Actor *types.Principal
|
||||||
Repo *types.Repository
|
IsRepoOwner bool
|
||||||
PullReq *types.PullReq
|
Repo *types.Repository
|
||||||
|
PullReq *types.PullReq
|
||||||
}
|
}
|
||||||
|
|
||||||
RequiredChecksOutput struct {
|
RequiredChecksOutput struct {
|
||||||
|
|
|
@ -29,3 +29,7 @@ func (s *searchService) ListUsers(
|
||||||
) ([]string, error) {
|
) ([]string, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *searchService) ListUserIDsByGroupIDs(_ context.Context, _ []int64) ([]int64, error) {
|
||||||
|
return nil, fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
|
|
|
@ -32,4 +32,6 @@ type SearchService interface {
|
||||||
session *auth.Session,
|
session *auth.Session,
|
||||||
userGroup *types.UserGroup,
|
userGroup *types.UserGroup,
|
||||||
) ([]string, error)
|
) ([]string, error)
|
||||||
|
|
||||||
|
ListUserIDsByGroupIDs(ctx context.Context, userGroupIDs []int64) ([]int64, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ func TestDeduplicate(t *testing.T) {
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
test.input = deduplicate(test.input)
|
test.input = Deduplicate(test.input)
|
||||||
if want, got := test.expected, test.input; !reflect.DeepEqual(want, got) {
|
if want, got := test.expected, test.input; !reflect.DeepEqual(want, got) {
|
||||||
t.Errorf("failed - want=%v, got=%v", want, got)
|
t.Errorf("failed - want=%v, got=%v", want, got)
|
||||||
return
|
return
|
||||||
|
|
|
@ -140,7 +140,7 @@ func (c *ExtendedTTLCache[K, V]) Map(ctx context.Context, keys []K) (map[K]V, er
|
||||||
m := make(map[K]V)
|
m := make(map[K]V)
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
keys = deduplicate(keys)
|
keys = Deduplicate(keys)
|
||||||
|
|
||||||
// Check what's already available in the cache.
|
// Check what's already available in the cache.
|
||||||
|
|
||||||
|
@ -211,8 +211,8 @@ func (c *TTLCache[K, V]) Get(ctx context.Context, key K) (V, error) {
|
||||||
return item, nil
|
return item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deduplicate is a utility function that removes duplicates from slice.
|
// Deduplicate is a utility function that removes duplicates from slice.
|
||||||
func deduplicate[V constraints.Ordered](slice []V) []V {
|
func Deduplicate[V constraints.Ordered](slice []V) []V {
|
||||||
if len(slice) <= 1 {
|
if len(slice) <= 1 {
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,7 +245,8 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
||||||
labelService := label.ProvideLabel(transactor, spaceStore, labelStore, labelValueStore, pullReqLabelAssignmentStore)
|
labelService := label.ProvideLabel(transactor, spaceStore, labelStore, labelValueStore, pullReqLabelAssignmentStore)
|
||||||
instrumentService := instrument.ProvideService()
|
instrumentService := instrument.ProvideService()
|
||||||
userGroupStore := database.ProvideUserGroupStore(db)
|
userGroupStore := database.ProvideUserGroupStore(db)
|
||||||
repoController := repo.ProvideController(config, transactor, provider, authorizer, repoStore, spaceStore, pipelineStore, principalStore, ruleStore, settingsService, principalInfoCache, protectionManager, gitInterface, repository, codeownersService, reporter, indexer, resourceLimiter, lockerLocker, auditService, mutexManager, repoIdentifier, repoCheck, publicaccessService, labelService, instrumentService, userGroupStore)
|
searchService := usergroup.ProvideSearchService()
|
||||||
|
repoController := repo.ProvideController(config, transactor, provider, authorizer, repoStore, spaceStore, pipelineStore, principalStore, ruleStore, settingsService, principalInfoCache, protectionManager, gitInterface, repository, codeownersService, reporter, indexer, resourceLimiter, lockerLocker, auditService, mutexManager, repoIdentifier, repoCheck, publicaccessService, labelService, instrumentService, userGroupStore, searchService)
|
||||||
reposettingsController := reposettings.ProvideController(authorizer, repoStore, settingsService, auditService)
|
reposettingsController := reposettings.ProvideController(authorizer, repoStore, settingsService, auditService)
|
||||||
executionStore := database.ProvideExecutionStore(db)
|
executionStore := database.ProvideExecutionStore(db)
|
||||||
checkStore := database.ProvideCheckStore(db, principalInfoCache)
|
checkStore := database.ProvideCheckStore(db, principalInfoCache)
|
||||||
|
@ -332,7 +333,6 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pullReq := migrate.ProvidePullReqImporter(provider, gitInterface, principalStore, repoStore, pullReqStore, pullReqActivityStore, transactor)
|
pullReq := migrate.ProvidePullReqImporter(provider, gitInterface, principalStore, repoStore, pullReqStore, pullReqActivityStore, transactor)
|
||||||
searchService := usergroup.ProvideSearchService()
|
|
||||||
pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, userGroupStore, userGroupReviewersStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, reporter3, migrator, pullreqService, listService, protectionManager, streamer, codeownersService, lockerLocker, pullReq, labelService, instrumentService, searchService)
|
pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, userGroupStore, userGroupReviewersStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, reporter3, migrator, pullreqService, listService, protectionManager, streamer, codeownersService, lockerLocker, pullReq, labelService, instrumentService, searchService)
|
||||||
webhookConfig := server.ProvideWebhookConfig(config)
|
webhookConfig := server.ProvideWebhookConfig(config)
|
||||||
webhookStore := database.ProvideWebhookStore(db)
|
webhookStore := database.ProvideWebhookStore(db)
|
||||||
|
|
Loading…
Reference in New Issue