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  }