github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/api/accessor/accessor.go (about) 1 package accessor 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/pf-qiu/concourse/v6/atc" 8 "github.com/pf-qiu/concourse/v6/atc/db" 9 ) 10 11 //go:generate counterfeiter . Access 12 13 type Access interface { 14 HasToken() bool 15 IsAuthenticated() bool 16 IsAuthorized(string) bool 17 IsAdmin() bool 18 IsSystem() bool 19 TeamNames() []string 20 TeamRoles() map[string][]string 21 Claims() Claims 22 } 23 24 type Claims struct { 25 Sub string 26 UserID string 27 UserName string 28 PreferredUsername string 29 Email string 30 Connector string 31 } 32 33 type Verification struct { 34 HasToken bool 35 IsTokenValid bool 36 RawClaims map[string]interface{} 37 } 38 39 type access struct { 40 verification Verification 41 requiredRole string 42 systemClaimKey string 43 systemClaimValues []string 44 teams []db.Team 45 teamRoles map[string][]string 46 isAdmin bool 47 } 48 49 func NewAccessor( 50 verification Verification, 51 requiredRole string, 52 systemClaimKey string, 53 systemClaimValues []string, 54 teams []db.Team, 55 ) *access { 56 a := &access{ 57 verification: verification, 58 requiredRole: requiredRole, 59 systemClaimKey: systemClaimKey, 60 systemClaimValues: systemClaimValues, 61 teams: teams, 62 } 63 a.computeTeamRoles() 64 return a 65 } 66 67 func (a *access) computeTeamRoles() { 68 a.teamRoles = map[string][]string{} 69 70 for _, team := range a.teams { 71 roles := a.rolesForTeam(team.Auth()) 72 if len(roles) > 0 { 73 a.teamRoles[team.Name()] = roles 74 } 75 if team.Admin() && contains(roles, "owner") { 76 a.isAdmin = true 77 } 78 } 79 } 80 81 func contains(arr []string, val string) bool { 82 for _, v := range arr { 83 if v == val { 84 return true 85 } 86 } 87 return false 88 } 89 90 func (a *access) rolesForTeam(auth atc.TeamAuth) []string { 91 roleSet := map[string]bool{} 92 93 groups := a.groups() 94 connectorID := a.connectorID() 95 userID := a.userID() 96 userName := a.userName() 97 98 for role, auth := range auth { 99 userAuth := auth["users"] 100 groupAuth := auth["groups"] 101 102 // backwards compatibility for allow-all-users 103 if len(userAuth) == 0 && len(groupAuth) == 0 { 104 roleSet[role] = true 105 } 106 107 for _, user := range userAuth { 108 if userID != "" { 109 if strings.EqualFold(user, fmt.Sprintf("%v:%v", connectorID, userID)) { 110 roleSet[role] = true 111 } 112 } 113 if userName != "" { 114 if strings.EqualFold(user, fmt.Sprintf("%v:%v", connectorID, userName)) { 115 roleSet[role] = true 116 } 117 } 118 } 119 120 for _, group := range groupAuth { 121 for _, claimGroup := range groups { 122 if claimGroup != "" { 123 if strings.EqualFold(group, fmt.Sprintf("%v:%v", connectorID, claimGroup)) { 124 roleSet[role] = true 125 } 126 } 127 } 128 } 129 } 130 131 var roles []string 132 for role := range roleSet { 133 roles = append(roles, role) 134 } 135 return roles 136 } 137 138 func (a *access) HasToken() bool { 139 return a.verification.HasToken 140 } 141 142 func (a *access) IsAuthenticated() bool { 143 return a.verification.IsTokenValid 144 } 145 146 func (a *access) IsAuthorized(teamName string) bool { 147 return a.isAdmin || a.hasPermission(a.teamRoles[teamName]) 148 } 149 150 func (a *access) TeamNames() []string { 151 teamNames := []string{} 152 for _, team := range a.teams { 153 if a.isAdmin || a.hasPermission(a.teamRoles[team.Name()]) { 154 teamNames = append(teamNames, team.Name()) 155 } 156 } 157 158 return teamNames 159 } 160 161 func (a *access) hasPermission(roles []string) bool { 162 for _, role := range roles { 163 switch a.requiredRole { 164 case OwnerRole: 165 return role == OwnerRole 166 case MemberRole: 167 return role == OwnerRole || role == MemberRole 168 case OperatorRole: 169 return role == OwnerRole || role == MemberRole || role == OperatorRole 170 case ViewerRole: 171 return role == OwnerRole || role == MemberRole || role == OperatorRole || role == ViewerRole 172 default: 173 return false 174 } 175 } 176 return false 177 } 178 179 func (a *access) claims() map[string]interface{} { 180 if a.IsAuthenticated() { 181 return a.verification.RawClaims 182 } 183 return map[string]interface{}{} 184 } 185 186 func (a *access) federatedClaims() map[string]interface{} { 187 if raw, ok := a.claims()["federated_claims"]; ok { 188 if claim, ok := raw.(map[string]interface{}); ok { 189 return claim 190 } 191 } 192 return map[string]interface{}{} 193 } 194 195 func (a *access) federatedClaim(name string) string { 196 if raw, ok := a.federatedClaims()[name]; ok { 197 if claim, ok := raw.(string); ok { 198 return claim 199 } 200 } 201 return "" 202 } 203 204 func (a *access) claim(name string) string { 205 if raw, ok := a.claims()[name]; ok { 206 if claim, ok := raw.(string); ok { 207 return claim 208 } 209 } 210 return "" 211 } 212 213 func (a *access) userID() string { 214 return a.federatedClaim("user_id") 215 } 216 217 func (a *access) userName() string { 218 if a.claim("preferred_username") != "" { 219 return a.claim("preferred_username") 220 } 221 222 return a.claim("name") 223 } 224 225 func (a *access) connectorID() string { 226 return a.federatedClaim("connector_id") 227 } 228 229 func (a *access) groups() []string { 230 groups := []string{} 231 if raw, ok := a.claims()["groups"]; ok { 232 if rawGroups, ok := raw.([]interface{}); ok { 233 for _, rawGroup := range rawGroups { 234 if group, ok := rawGroup.(string); ok { 235 groups = append(groups, group) 236 } 237 } 238 } 239 } 240 return groups 241 } 242 243 func (a *access) IsAdmin() bool { 244 return a.isAdmin 245 } 246 247 func (a *access) IsSystem() bool { 248 if claim := a.claim(a.systemClaimKey); claim != "" { 249 for _, value := range a.systemClaimValues { 250 if value == claim { 251 return true 252 } 253 } 254 } 255 return false 256 } 257 258 func (a *access) TeamRoles() map[string][]string { 259 return a.teamRoles 260 } 261 262 func (a *access) Claims() Claims { 263 return Claims{ 264 Sub: a.claim("sub"), 265 Email: a.claim("email"), 266 UserID: a.userID(), 267 UserName: a.claim("name"), 268 PreferredUsername: a.claim("preferred_username"), 269 Connector: a.connectorID(), 270 } 271 }