forked from argoproj/argo-cd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrbacpolicy.go
205 lines (184 loc) · 6.03 KB
/
rbacpolicy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package rbacpolicy
import (
"strings"
"github.com/golang-jwt/jwt/v4"
log "github.com/sirupsen/logrus"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
applister "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
jwtutil "github.com/argoproj/argo-cd/v2/util/jwt"
"github.com/argoproj/argo-cd/v2/util/rbac"
)
const (
// please add new items to Resources
ResourceClusters = "clusters"
ResourceProjects = "projects"
ResourceApplications = "applications"
ResourceApplicationSets = "applicationsets"
ResourceRepositories = "repositories"
ResourceCertificates = "certificates"
ResourceAccounts = "accounts"
ResourceGPGKeys = "gpgkeys"
ResourceLogs = "logs"
ResourceExec = "exec"
ResourceExtensions = "extensions"
// please add new items to Actions
ActionGet = "get"
ActionCreate = "create"
ActionUpdate = "update"
ActionDelete = "delete"
ActionSync = "sync"
ActionOverride = "override"
ActionAction = "action"
ActionInvoke = "invoke"
)
var (
defaultScopes = []string{"groups"}
Resources = []string{
ResourceClusters,
ResourceProjects,
ResourceApplications,
ResourceApplicationSets,
ResourceRepositories,
ResourceCertificates,
ResourceLogs,
ResourceExec,
}
Actions = []string{
ActionGet,
ActionCreate,
ActionUpdate,
ActionDelete,
ActionSync,
ActionOverride,
}
)
// RBACPolicyEnforcer provides an RBAC Claims Enforcer which additionally consults AppProject
// roles, jwt tokens, and groups. It is backed by a AppProject informer/lister cache and does not
// make any API calls during enforcement.
type RBACPolicyEnforcer struct {
enf *rbac.Enforcer
projLister applister.AppProjectNamespaceLister
scopes []string
}
// NewRBACPolicyEnforcer returns a new RBAC Enforcer for the Argo CD API Server
func NewRBACPolicyEnforcer(enf *rbac.Enforcer, projLister applister.AppProjectNamespaceLister) *RBACPolicyEnforcer {
return &RBACPolicyEnforcer{
enf: enf,
projLister: projLister,
scopes: nil,
}
}
func (p *RBACPolicyEnforcer) SetScopes(scopes []string) {
p.scopes = scopes
}
func (p *RBACPolicyEnforcer) GetScopes() []string {
scopes := p.scopes
if scopes == nil {
scopes = defaultScopes
}
return scopes
}
func IsProjectSubject(subject string) bool {
_, _, ok := GetProjectRoleFromSubject(subject)
return ok
}
func GetProjectRoleFromSubject(subject string) (string, string, bool) {
parts := strings.Split(subject, ":")
if len(parts) == 3 && parts[0] == "proj" {
return parts[1], parts[2], true
}
return "", "", false
}
// EnforceClaims is an RBAC claims enforcer specific to the Argo CD API server
func (p *RBACPolicyEnforcer) EnforceClaims(claims jwt.Claims, rvals ...interface{}) bool {
mapClaims, err := jwtutil.MapClaims(claims)
if err != nil {
return false
}
subject := jwtutil.StringField(mapClaims, "sub")
// Check if the request is for an application resource. We have special enforcement which takes
// into consideration the project's token and group bindings
var runtimePolicy string
var projName string
proj := p.getProjectFromRequest(rvals...)
if proj != nil {
if IsProjectSubject(subject) {
return p.enforceProjectToken(subject, proj, rvals...)
}
runtimePolicy = proj.ProjectPoliciesString()
projName = proj.Name
}
// NOTE: This calls prevent multiple creation of the wrapped enforcer
enforcer := p.enf.CreateEnforcerWithRuntimePolicy(projName, runtimePolicy)
// Check the subject. This is typically the 'admin' case.
// NOTE: the call to EnforceWithCustomEnforcer will also consider the default role
vals := append([]interface{}{subject}, rvals[1:]...)
if p.enf.EnforceWithCustomEnforcer(enforcer, vals...) {
return true
}
scopes := p.scopes
if scopes == nil {
scopes = defaultScopes
}
// Finally check if any of the user's groups grant them permissions
groups := jwtutil.GetScopeValues(mapClaims, scopes)
// Get groups to reduce the amount to checking groups
groupingPolicies := enforcer.GetGroupingPolicy()
for gidx := range groups {
for gpidx := range groupingPolicies {
// Prefilter user groups by groups defined in the model
if groupingPolicies[gpidx][0] == groups[gidx] {
vals := append([]interface{}{groups[gidx]}, rvals[1:]...)
if p.enf.EnforceWithCustomEnforcer(enforcer, vals...) {
return true
}
break
}
}
}
logCtx := log.WithFields(log.Fields{"claims": claims, "rval": rvals, "subject": subject, "groups": groups, "project": projName, "scopes": scopes})
logCtx.Debug("enforce failed")
return false
}
// getProjectFromRequest parses the project name from the RBAC request and returns the associated
// project (if it exists)
func (p *RBACPolicyEnforcer) getProjectFromRequest(rvals ...interface{}) *v1alpha1.AppProject {
if len(rvals) != 4 {
return nil
}
getProjectByName := func(projName string) *v1alpha1.AppProject {
proj, err := p.projLister.Get(projName)
if err != nil {
return nil
}
return proj
}
if res, ok := rvals[1].(string); ok {
if obj, ok := rvals[3].(string); ok {
switch res {
case ResourceApplications, ResourceRepositories, ResourceClusters, ResourceLogs, ResourceExec:
if objSplit := strings.Split(obj, "/"); len(objSplit) >= 2 {
return getProjectByName(objSplit[0])
}
case ResourceProjects:
// we also automatically give project tokens and groups 'get' access to the project
return getProjectByName(obj)
}
}
}
return nil
}
// enforceProjectToken will check to see the valid token has not yet been revoked in the project
func (p *RBACPolicyEnforcer) enforceProjectToken(subject string, proj *v1alpha1.AppProject, rvals ...interface{}) bool {
subjectSplit := strings.Split(subject, ":")
if len(subjectSplit) != 3 {
return false
}
projName, _ := subjectSplit[1], subjectSplit[2]
if projName != proj.Name {
// this should never happen (we generated a project token for a different project)
return false
}
vals := append([]interface{}{subject}, rvals[1:]...)
return p.enf.EnforceRuntimePolicy(proj.Name, proj.ProjectPoliciesString(), vals...)
}