Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(157)

Side by Side Diff: server/auth/delegation/minter.go

Issue 2236163002: auth: Low-level API for minting delegation tokens. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@store-int-in-cache
Patch Set: add tests Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « server/auth/delegation/doc.go ('k') | server/auth/delegation/minter_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
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.
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 // Must be empty if UnlimitedAudience is true.
36 Audience []identity.Identity
37
38 // AudienceGroups can be used to specify a group (or a bunch of groups) as
39 // a target audience of the token.
40 //
41 // It works in addition to Audience.
42 //
43 // Must be empty if UnlimitedAudience is true.
44 AudienceGroups []string
45
46 // UnlimitedAudience, if true, indicates that the delegation token can b e
47 // used by any bearer.
48 //
49 // Tokens with unlimited audience are roughly as powerful as OAuth acces s
50 // tokens and should be used only if absolutely necessary.
51 //
52 // Prefer to limit token's audience as much as possible. See Audience an d
53 // AudienceGroups fields above.
54 //
55 // If UnlimitedAudience is true, Audience and AudienceGroups must be emp ty.
56 UnlimitedAudience bool
57
58 // TargetServices is a list of 'service:...' identities with services th at
59 // accept the token.
60 //
61 // This can be used to limit the scope of the token.
62 //
63 // Must be empty if Untargeted is true.
64 TargetServices []identity.Identity
65
66 // Untargeted, if true, indicates that the delegation token should be ac cepted
67 // by any LUCI service.
68 //
69 // Use this if you are preparing a token in advance, not yet knowing whe re it
70 // is going to be used.
71 //
72 // If Untargeted is true, TargetServices must be empty.
73 Untargeted bool
74
75 // Impersonate defines on whose behalf the token is being created.
76 //
77 // By default the token delegates an identity of whoever requested it. T his
78 // identity is extracted from credentials that accompany token minting r equest
79 // (e.g. OAuth access token).
80 //
81 // By using 'Impersonate' a caller can make a token that delegates someo ne
82 // else's identity, effectively producing an impersonation token.
83 //
84 // Only limited set of callers can do this. The set of who can be impers onated
85 // is also limited. The rules are enforced by the auth service.
86 Impersonate identity.Identity
87
88 // ValidityDuration defines for how long the token would be valid.
89 //
90 // Maximum theoretical TTL is limited by the lifetime of the signing key of
91 // the auth service (~= 24 hours). Minimum acceptable value is 30 sec.
92 //
93 // Required.
94 ValidityDuration time.Duration
95
96 // Intent is a reason why the token is created.
97 //
98 // Used only for logging purposes on the auth service, will be indexed. Should
99 // be a short identifier-like string.
100 //
101 // Optional.
102 Intent string
103 }
104
105 // Token is actual delegation token with its expiration time and ID.
106 type Token struct {
107 Token string // base64-encoded URL-safe blob with the token
108 Expiry time.Time // UTC time when it expires (also encoded in Token)
109 SubtokenID string // identifier of the token (also encoded in Token)
110 }
111
112 // CreateToken makes a request to the auth service to generate the token.
113 //
114 // If uses current service's credentials to authenticate the request.
115 //
116 // If req.Impersonate is not used, the identity encoded in the authentication
117 // credentials will be delegated by the token, otherwise the service will check
118 // that caller is allowed to do the impersonation and will return a token that
119 // delegates the identity specified by req.Impersonate.
120 func CreateToken(c context.Context, req TokenRequest) (*Token, error) {
121 // See https://github.com/luci/luci-py/blob/master/appengine/auth_servic e/delegation.py.
122 var params struct {
123 Audience []string `json:"audience,omitempty"`
124 Services []string `json:"services,omitempty"`
125 ValidityDuration int `json:"validity_duration"`
126 Impersonate string `json:"impersonate,omitempty"`
127 Intent string `json:"intent,omitempty"`
128 }
129
130 // Audience.
131 params.Audience = make([]string, 0, len(req.Audience)+len(req.AudienceGr oups))
132 for _, aud := range req.Audience {
133 if err := aud.Validate(); err != nil {
134 return nil, err
135 }
136 params.Audience = append(params.Audience, string(aud))
137 }
138 for _, group := range req.AudienceGroups {
139 params.Audience = append(params.Audience, "group:"+group)
140 }
141 switch {
142 case req.UnlimitedAudience && len(params.Audience) != 0:
143 return nil, fmt.Errorf("delegation: can't specify audience for U nlimitedAudience=true token")
144 case !req.UnlimitedAudience && len(params.Audience) == 0:
145 return nil, fmt.Errorf("delegation: either Audience/AudienceGrou ps or UnlimitedAudience=true are required")
146 }
147 if req.UnlimitedAudience {
148 params.Audience = []string{"*"}
149 }
150
151 // Services.
152 params.Services = make([]string, 0, len(req.TargetServices))
153 for _, srv := range req.TargetServices {
154 if err := srv.Validate(); err != nil {
155 return nil, err
156 }
157 params.Services = append(params.Services, string(srv))
158 }
159 switch {
160 case req.Untargeted && len(params.Services) != 0:
161 return nil, fmt.Errorf("delegation: can't specify TargetServices for Untargeted=true token")
162 case !req.Untargeted && len(params.Services) == 0:
163 return nil, fmt.Errorf("delegation: either TargetServices or Unt argeted=true are required")
164 }
165 if req.Untargeted {
166 params.Services = []string{"*"}
167 }
168
169 // Validity duration.
170 params.ValidityDuration = int(req.ValidityDuration / time.Second)
171 if params.ValidityDuration < 30 {
172 return nil, fmt.Errorf("ValidityDuration must be >= 30 sec, got %s", req.ValidityDuration)
173 }
174 if params.ValidityDuration > 24*3600 {
175 return nil, fmt.Errorf("ValidityDuration must be <= 24h, got %s" , req.ValidityDuration)
176 }
177
178 // The rest of the fields.
179 if req.Impersonate != "" {
180 if err := req.Impersonate.Validate(); err != nil {
181 return nil, err
182 }
183 params.Impersonate = string(req.Impersonate)
184 }
185 params.Intent = req.Intent
186
187 var response struct {
188 DelegationToken string `json:"delegation_token,omitempty"`
189 ValidityDuration int `json:"validity_duration,omitempty"`
190 SubtokenID string `json:"subtoken_id,omitempty"`
191 Text string `json:"text,omitempty"` // for error res ponses
192 }
193
194 httpReq := internal.Request{
195 Method: "POST",
196 URL: req.AuthServiceURL + "/auth_service/api/v1/delegation/to ken/create",
197 Scopes: []string{"https://www.googleapis.com/auth/userinfo.email "},
198 Body: &params,
199 Out: &response,
200 }
201 if err := httpReq.Do(c); err != nil {
202 if response.Text != "" {
203 err = fmt.Errorf("%s - %s", err, response.Text)
204 }
205 return nil, err
206 }
207
208 return &Token{
209 Token: response.DelegationToken,
210 Expiry: clock.Now(c).Add(time.Duration(response.ValidityDura tion) * time.Second).UTC(),
211 SubtokenID: response.SubtokenID,
212 }, nil
213 }
214
215 func init() {
216 // For token cache.
217 gob.Register(Token{})
218 }
OLDNEW
« no previous file with comments | « server/auth/delegation/doc.go ('k') | server/auth/delegation/minter_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698