OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
2 // Use of this source code is governed under the Apache License, Version 2.0 | |
3 // that can be found in the LICENSE file. | |
4 | |
5 package delegation | |
6 | |
7 import ( | |
8 "encoding/gob" | |
9 "fmt" | |
10 "time" | |
11 | |
12 "golang.org/x/net/context" | |
13 | |
14 "github.com/luci/luci-go/common/clock" | |
15 "github.com/luci/luci-go/server/auth/identity" | |
16 "github.com/luci/luci-go/server/auth/internal" | |
17 ) | |
18 | |
19 // TokenRequest describes parameters of a new delegation token. | |
20 type TokenRequest struct { | |
21 // AuthServiceURL is root URL (e.g. https://<host>) of the service to us e for | |
iannucci
2016/08/11 22:52:35
I think I almost asked this elsewhere, but deleted
Vadim Sh.
2016/08/11 23:25:37
Usually using https://<host> is less strings manip
| |
22 // minting the delegation token. | |
23 // | |
24 // The token will be signed by the service's private key. Only services that | |
25 // trust this auth service would be able to accept the new token. | |
26 // | |
27 // Required. | |
28 AuthServiceURL string | |
29 | |
30 // Audience is to whom caller's identity is delegated. | |
iannucci
2016/08/11 22:52:35
IIUC, the Audience set (all identities in this sli
Vadim Sh.
2016/08/11 23:25:37
Correct.
| |
31 // | |
32 // Only clients that can prove they are intended audience (e.g. by prese nting | |
33 // valid access token) would be able to use the delegation token. | |
34 // | |
35 // Optional. If both Audience and AudienceGroups are empty, the token is | |
36 // usable by any bearer. | |
iannucci
2016/08/11 22:52:35
that's a bit awkward; if you somehow strip both of
Vadim Sh.
2016/08/11 23:25:37
Done.
| |
37 Audience []identity.Identity | |
38 | |
39 // AudienceGroups can be used to specify a group (or a bunch of groups) as | |
40 // a target audience of the token. | |
41 // | |
42 // It works in addition to Audience. | |
43 // | |
44 // Optional. If both Audience and AudienceGroups are empty, the token is | |
45 // usable by any bearer. | |
46 AudienceGroups []string | |
47 | |
48 // Services is a list of 'service:...' identities with services that acc ept | |
49 // the token. | |
50 // | |
51 // This can be used to limit the scope of the token. | |
52 // | |
53 // Optional. If empty, the token is accepted by any LUCI service. | |
54 Services []identity.Identity | |
iannucci
2016/08/11 22:52:35
Yeah I'm not super happy with go's zero-value for
Vadim Sh.
2016/08/11 23:25:37
Done.
| |
55 | |
56 // Impersonate defines on whose behalf the token is being created. | |
57 // | |
58 // By default the token delegates an identity of whoever requested it. T his | |
59 // identity is extracted from credentials that accompany token minting r equest | |
60 // (e.g. OAuth access token). | |
iannucci
2016/08/11 22:52:35
could we ever run into a situation where this extr
Vadim Sh.
2016/08/11 23:25:37
No, auth_service enforces that user is not using d
| |
61 // | |
62 // By using 'Impersonate' a caller can make a token that delegates someo ne | |
63 // else's identity, effectively producing an impersonation token. | |
64 // | |
65 // Only limited set of callers can do this. The set of who can be impers onated | |
66 // is also limited. The rules are enforced by the auth service. | |
67 Impersonate identity.Identity | |
68 | |
69 // ValidityDuration defines for how long the token would be valid. | |
70 // | |
71 // Maximum theoretical TTL is limited by the lifetime of the signing key of | |
72 // the auth service (~= 24 hours). Minimum acceptable value is 30 sec. | |
73 // | |
74 // Required. | |
75 ValidityDuration time.Duration | |
76 | |
77 // Intent is a reason why the token is created. | |
78 // | |
79 // Used only for logging purposes on the auth service, will be indexed. Should | |
80 // be a short identifier-like string. | |
iannucci
2016/08/11 22:52:35
Will it be publicly visible, or could you put priv
Vadim Sh.
2016/08/11 23:25:37
It is not encoded in the token body. It ends up in
| |
81 // | |
82 // Optional. | |
83 Intent string | |
84 } | |
85 | |
86 // Token is actual delegation token with its expiration time and ID. | |
87 type Token struct { | |
88 Token string // base64-encoded URL-safe blob with the token | |
89 Expiry time.Time // UTC time when it expires (also encoded in Token) | |
90 SubtokenID string // identifier of the token (also encoded in Token) | |
91 } | |
92 | |
93 // CreateToken makes a request to the auth service to generate the token. | |
94 // | |
95 // If uses current service's credentials to authenticate the request. | |
96 // | |
97 // If req.Impersonate is not used, the identity encoded in the authentication | |
98 // credentials will be delegated by the token, otherwise the service will check | |
99 // that caller is allowed to do the impersonation and will return a token that | |
100 // delegates the identity specified by req.Impersonate. | |
101 func CreateToken(c context.Context, req TokenRequest) (*Token, error) { | |
102 // See https://github.com/luci/luci-py/blob/master/appengine/auth_servic e/delegation.py. | |
103 var params struct { | |
104 Audience []string `json:"audience,omitempty"` | |
105 Services []string `json:"services,omitempty"` | |
106 ValidityDuration int `json:"validity_duration"` | |
107 Impersonate string `json:"impersonate,omitempty"` | |
108 Intent string `json:"intent,omitempty"` | |
109 } | |
110 | |
111 // Audience. | |
112 params.Audience = make([]string, 0, len(req.Audience)+len(req.AudienceGr oups)) | |
113 for _, aud := range req.Audience { | |
114 if err := aud.Validate(); err != nil { | |
115 return nil, err | |
116 } | |
117 params.Audience = append(params.Audience, string(aud)) | |
118 } | |
119 for _, group := range req.AudienceGroups { | |
120 params.Audience = append(params.Audience, "group:"+group) | |
121 } | |
122 if len(params.Audience) == 0 { | |
123 params.Audience = []string{"*"} // means "Any bearer" | |
124 } | |
125 | |
126 // Services. | |
127 params.Services = make([]string, 0, len(req.Services)) | |
128 for _, srv := range req.Services { | |
129 if err := srv.Validate(); err != nil { | |
130 return nil, err | |
131 } | |
132 params.Services = append(params.Services, string(srv)) | |
133 } | |
134 if len(params.Services) == 0 { | |
135 params.Services = []string{"*"} // means "Any service" | |
136 } | |
137 | |
138 // Validity duration. | |
139 params.ValidityDuration = int(req.ValidityDuration / time.Second) | |
140 if params.ValidityDuration < 30 { | |
141 return nil, fmt.Errorf("ValidityDuration must be >= 30 sec, got %s", req.ValidityDuration) | |
142 } | |
143 if params.ValidityDuration > 24*3600 { | |
144 return nil, fmt.Errorf("ValidityDuration must be <= 24h, got %s" , req.ValidityDuration) | |
145 } | |
146 | |
147 // The rest of the fields. | |
148 if req.Impersonate != "" { | |
149 if err := req.Impersonate.Validate(); err != nil { | |
150 return nil, err | |
151 } | |
152 params.Impersonate = string(req.Impersonate) | |
153 } | |
154 params.Intent = req.Intent | |
155 | |
156 var response struct { | |
157 DelegationToken string `json:"delegation_token,omitempty"` | |
158 ValidityDuration int `json:"validity_duration,omitempty"` | |
159 SubtokenID string `json:"subtoken_id,omitempty"` | |
160 Text string `json:"text,omitempty"` // for error res ponses | |
161 } | |
162 | |
163 httpReq := internal.Request{ | |
164 Method: "POST", | |
165 URL: req.AuthServiceURL + "/auth_service/api/v1/delegation/to ken/create", | |
166 Scopes: []string{"https://www.googleapis.com/auth/userinfo.email "}, | |
167 Body: ¶ms, | |
168 Out: &response, | |
169 } | |
170 if err := httpReq.Do(c); err != nil { | |
171 if response.Text != "" { | |
172 err = fmt.Errorf("%s - %s", err, response.Text) | |
173 } | |
174 return nil, err | |
175 } | |
176 | |
177 return &Token{ | |
178 Token: response.DelegationToken, | |
179 Expiry: clock.Now(c).Add(time.Duration(response.ValidityDura tion) * time.Second).UTC(), | |
180 SubtokenID: response.SubtokenID, | |
181 }, nil | |
182 } | |
183 | |
184 func init() { | |
185 // For token cache. | |
186 gob.Register(Token{}) | |
187 } | |
OLD | NEW |