forked from argoproj/argo-cd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rbacpolicy.go
136 lines (122 loc) · 4.32 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
package rbacpolicy
import (
"strings"
jwt "github.com/dgrijalva/jwt-go"
log "github.com/sirupsen/logrus"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
applister "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
jwtutil "github.com/argoproj/argo-cd/util/jwt"
projectutil "github.com/argoproj/argo-cd/util/project"
"github.com/argoproj/argo-cd/util/rbac"
)
const (
ResourceClusters = "clusters"
ResourceProjects = "projects"
ResourceApplications = "applications"
ResourceRepositories = "repositories"
ActionGet = "get"
ActionCreate = "create"
ActionUpdate = "update"
ActionDelete = "delete"
ActionSync = "sync"
)
// 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
}
// 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,
}
}
// 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.GetField(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
proj := p.getProjectFromRequest(rvals...)
if proj != nil {
if strings.HasPrefix(subject, "proj:") {
return p.enforceProjectToken(subject, mapClaims, proj, rvals...)
}
runtimePolicy = proj.ProjectPoliciesString()
}
// Check the subject. This is typically the 'admin' case.
// NOTE: the call to EnforceRuntimePolicy will also consider the default role
vals := append([]interface{}{subject}, rvals[1:]...)
if p.enf.EnforceRuntimePolicy(runtimePolicy, vals...) {
return true
}
// Finally check if any of the user's groups grant them permissions
groups := jwtutil.GetGroups(mapClaims)
for _, group := range groups {
vals := append([]interface{}{group}, rvals[1:]...)
if p.enf.EnforceRuntimePolicy(runtimePolicy, vals...) {
return true
}
}
logCtx := log.WithField("claims", claims).WithField("rval", rvals)
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:
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, claims jwt.MapClaims, proj *v1alpha1.AppProject, rvals ...interface{}) bool {
subjectSplit := strings.Split(subject, ":")
if len(subjectSplit) != 3 {
return false
}
projName, roleName := subjectSplit[1], subjectSplit[2]
if projName != proj.Name {
// this should never happen (we generated a project token for a different project)
return false
}
iat, err := jwtutil.GetIssuedAt(claims)
if err != nil {
return false
}
_, _, err = projectutil.GetJWTToken(proj, roleName, iat)
if err != nil {
// if we get here the token is still valid, but has been revoked (no longer exists in the project)
return false
}
vals := append([]interface{}{subject}, rvals[1:]...)
return p.enf.EnforceRuntimePolicy(proj.ProjectPoliciesString(), vals...)
}