forked from vmware-archive/atc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
space_verifier.go
141 lines (115 loc) · 3.13 KB
/
space_verifier.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
package uaa
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"golang.org/x/oauth2"
"code.cloudfoundry.org/lager"
"code.cloudfoundry.org/urljoiner"
"github.com/dgrijalva/jwt-go"
)
type SpaceVerifier struct {
spaceGUIDs []string
cfAPIURL string
}
func NewSpaceVerifier(
spaceGUIDs []string,
cfAPIURL string,
) SpaceVerifier {
return SpaceVerifier{
spaceGUIDs: spaceGUIDs,
cfAPIURL: cfAPIURL,
}
}
type UAAToken struct {
UserID string `json:"user_id"`
}
type CFSpaceDevelopersResponse struct {
NextUrl string `json:"next_url"`
UserInfos []CFUserInfo `json:"resources"`
}
type CFUserInfo struct {
Metadata struct {
GUID string `json:"guid"`
} `json:"metadata"`
}
func (verifier SpaceVerifier) Verify(logger lager.Logger, httpClient *http.Client) (bool, error) {
oauth2Transport, ok := httpClient.Transport.(*oauth2.Transport)
if !ok {
return false, errors.New("httpClient transport must be of type oauth2.Transport")
}
token, err := oauth2Transport.Source.Token()
if err != nil {
return false, err
}
tokenParts := strings.Split(token.AccessToken, ".")
if len(tokenParts) < 2 {
return false, errors.New("access token contains an invalid number of segments")
}
decodedClaims, err := jwt.DecodeSegment(tokenParts[1])
if err != nil {
return false, err
}
var uaaToken UAAToken
err = json.Unmarshal(decodedClaims, &uaaToken)
if err != nil {
return false, err
}
if uaaToken.UserID == "" {
return false, fmt.Errorf("not able to retrieve 'user_id' property from UAA access token")
}
for _, verifierSpaceGUID := range verifier.spaceGUIDs {
spaceURL := urljoiner.Join(verifier.cfAPIURL, "v2", "spaces", verifierSpaceGUID, "developers?results-per-page=100")
hasAccess, nextUrl, err := verifier.isSpaceDeveloper(logger, httpClient, spaceURL, uaaToken.UserID)
if err != nil {
return false, err
}
if hasAccess {
return true, nil
}
for nextUrl != "" {
spaceURL = urljoiner.Join(verifier.cfAPIURL, nextUrl)
hasAccess, nextUrl, err = verifier.isSpaceDeveloper(logger, httpClient, spaceURL, uaaToken.UserID)
if err != nil {
return false, err
}
if hasAccess {
return true, nil
}
}
}
logger.Info("not-in-spaces", lager.Data{
"want": verifier.spaceGUIDs,
})
return false, nil
}
func (verifier SpaceVerifier) isSpaceDeveloper(
logger lager.Logger,
httpClient *http.Client,
cfApiURL string,
userGUID string,
) (bool, string, error) {
logger.Info("cf-request", lager.Data{"url": cfApiURL})
response, err := httpClient.Get(cfApiURL)
if err != nil {
return false, "", err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
logger.Info("unexpected response from CF API URL", lager.Data{
"statusCode": response.StatusCode,
"status": response.Status,
})
return false, "", nil
}
var cfSpaceDevelopersResponse CFSpaceDevelopersResponse
err = json.NewDecoder(response.Body).Decode(&cfSpaceDevelopersResponse)
for _, userInfo := range cfSpaceDevelopersResponse.UserInfos {
if userInfo.Metadata.GUID == userGUID {
return true, cfSpaceDevelopersResponse.NextUrl, nil
}
}
return false, cfSpaceDevelopersResponse.NextUrl, nil
}