| OLD | NEW |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package auth | 5 package auth |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "fmt" | 8 "fmt" |
| 9 "net/http" | 9 "net/http" |
| 10 "strings" | 10 "strings" |
| 11 "time" | 11 "time" |
| 12 | 12 |
| 13 "golang.org/x/net/context" | 13 "golang.org/x/net/context" |
| 14 "golang.org/x/oauth2" |
| 14 "google.golang.org/grpc/credentials" | 15 "google.golang.org/grpc/credentials" |
| 15 | 16 |
| 16 "github.com/luci/luci-go/common/auth" | 17 "github.com/luci/luci-go/common/auth" |
| 17 "github.com/luci/luci-go/server/auth/delegation" | 18 "github.com/luci/luci-go/server/auth/delegation" |
| 18 "github.com/luci/luci-go/server/auth/identity" | 19 "github.com/luci/luci-go/server/auth/identity" |
| 19 "github.com/luci/luci-go/server/auth/internal" | 20 "github.com/luci/luci-go/server/auth/internal" |
| 20 ) | 21 ) |
| 21 | 22 |
| 22 // RPCAuthorityKind defines under whose authority RPCs are made. | 23 // RPCAuthorityKind defines under whose authority RPCs are made. |
| 23 type RPCAuthorityKind int | 24 type RPCAuthorityKind int |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 95 // requests. | 96 // requests. |
| 96 // | 97 // |
| 97 // Usage: | 98 // Usage: |
| 98 // tr, err := auth.GetRPCTransport(c, auth.AsSelf, auth.WithScopes("...")) | 99 // tr, err := auth.GetRPCTransport(c, auth.AsSelf, auth.WithScopes("...")) |
| 99 // if err != nil { | 100 // if err != nil { |
| 100 // return err | 101 // return err |
| 101 // } | 102 // } |
| 102 // client := &http.Client{Transport: tr} | 103 // client := &http.Client{Transport: tr} |
| 103 // ... | 104 // ... |
| 104 func GetRPCTransport(c context.Context, kind RPCAuthorityKind, opts ...RPCOption
) (http.RoundTripper, error) { | 105 func GetRPCTransport(c context.Context, kind RPCAuthorityKind, opts ...RPCOption
) (http.RoundTripper, error) { |
| 105 » options, err := makeRpcOptions(kind, opts) | 106 » options, err := makeRPCOptions(kind, opts) |
| 106 if err != nil { | 107 if err != nil { |
| 107 return nil, err | 108 return nil, err |
| 108 } | 109 } |
| 109 config := GetConfig(c) | 110 config := GetConfig(c) |
| 110 if config == nil || config.AnonymousTransport == nil { | 111 if config == nil || config.AnonymousTransport == nil { |
| 111 return nil, ErrNotConfigured | 112 return nil, ErrNotConfigured |
| 112 } | 113 } |
| 113 baseTransport := config.AnonymousTransport(c) | 114 baseTransport := config.AnonymousTransport(c) |
| 114 if options.kind == NoAuth { | 115 if options.kind == NoAuth { |
| 115 return baseTransport, nil | 116 return baseTransport, nil |
| 116 } | 117 } |
| 117 return auth.NewModifyingTransport(baseTransport, func(req *http.Request)
error { | 118 return auth.NewModifyingTransport(baseTransport, func(req *http.Request)
error { |
| 118 headers, err := options.getRPCHeaders(c, req.URL.String(), optio
ns) | 119 headers, err := options.getRPCHeaders(c, req.URL.String(), optio
ns) |
| 119 if err != nil { | 120 if err != nil { |
| 120 return err | 121 return err |
| 121 } | 122 } |
| 122 for k, v := range headers { | 123 for k, v := range headers { |
| 123 req.Header.Set(k, v) | 124 req.Header.Set(k, v) |
| 124 } | 125 } |
| 125 return nil | 126 return nil |
| 126 }), nil | 127 }), nil |
| 127 } | 128 } |
| 128 | 129 |
| 129 // GetPerRPCCredentials returns gRPC's PerRPCCredentials implementation. | 130 // GetPerRPCCredentials returns gRPC's PerRPCCredentials implementation. |
| 130 // | 131 // |
| 131 // It can be used to authenticate outbound gPRC RPC's. | 132 // It can be used to authenticate outbound gPRC RPC's. |
| 132 func GetPerRPCCredentials(kind RPCAuthorityKind, opts ...RPCOption) (credentials
.PerRPCCredentials, error) { | 133 func GetPerRPCCredentials(kind RPCAuthorityKind, opts ...RPCOption) (credentials
.PerRPCCredentials, error) { |
| 133 » options, err := makeRpcOptions(kind, opts) | 134 » options, err := makeRPCOptions(kind, opts) |
| 134 if err != nil { | 135 if err != nil { |
| 135 return nil, err | 136 return nil, err |
| 136 } | 137 } |
| 137 return perRPCCreds{options}, nil | 138 return perRPCCreds{options}, nil |
| 138 } | 139 } |
| 139 | 140 |
| 140 type perRPCCreds struct { | 141 type perRPCCreds struct { |
| 141 options *rpcOptions | 142 options *rpcOptions |
| 142 } | 143 } |
| 143 | 144 |
| 144 func (creds perRPCCreds) GetRequestMetadata(c context.Context, uri ...string) (m
ap[string]string, error) { | 145 func (creds perRPCCreds) GetRequestMetadata(c context.Context, uri ...string) (m
ap[string]string, error) { |
| 145 if len(uri) == 0 { | 146 if len(uri) == 0 { |
| 146 panic("perRPCCreds: no URI given") | 147 panic("perRPCCreds: no URI given") |
| 147 } | 148 } |
| 148 return creds.options.getRPCHeaders(c, uri[0], creds.options) | 149 return creds.options.getRPCHeaders(c, uri[0], creds.options) |
| 149 } | 150 } |
| 150 | 151 |
| 151 func (creds perRPCCreds) RequireTransportSecurity() bool { | 152 func (creds perRPCCreds) RequireTransportSecurity() bool { |
| 152 return true | 153 return true |
| 153 } | 154 } |
| 154 | 155 |
| 156 // GetTokenSourceAsSelf returns an oauth2.TokenSource bound to the supplied |
| 157 // Context that returns tokens for AsSelf authentication. |
| 158 // |
| 159 // If no scopes are provided, auth.OAuthScopeEmail will be used. |
| 160 // |
| 161 // While GetPerRPCCredentials is preferred, this can be used by packages that |
| 162 // cannot or do not properly handle this gRPC option. |
| 163 func GetTokenSourceAsSelf(c context.Context, scopes ...string) oauth2.TokenSourc
e { |
| 164 if len(scopes) == 0 { |
| 165 scopes = []string{auth.OAuthScopeEmail} |
| 166 } |
| 167 return &tokenSource{c, scopes} |
| 168 } |
| 169 |
| 170 type tokenSource struct { |
| 171 context.Context |
| 172 scopes []string |
| 173 } |
| 174 |
| 175 func (ts *tokenSource) Token() (*oauth2.Token, error) { |
| 176 cfg := GetConfig(ts) |
| 177 if cfg == nil || cfg.AccessTokenProvider == nil { |
| 178 return nil, ErrNotConfigured |
| 179 } |
| 180 |
| 181 tok, err := cfg.AccessTokenProvider(ts, ts.scopes) |
| 182 if err != nil { |
| 183 return nil, err |
| 184 } |
| 185 return tok.OAuth2Token(), nil |
| 186 } |
| 187 |
| 155 //////////////////////////////////////////////////////////////////////////////// | 188 //////////////////////////////////////////////////////////////////////////////// |
| 156 // Internal stuff. | 189 // Internal stuff. |
| 157 | 190 |
| 158 func init() { | 191 func init() { |
| 159 // This is needed to allow packages imported by 'server/auth' to make | 192 // This is needed to allow packages imported by 'server/auth' to make |
| 160 // authenticated calls. They can't use GetRPCTransport directly, since t
hey | 193 // authenticated calls. They can't use GetRPCTransport directly, since t
hey |
| 161 // can't import 'server/auth' (it creates an import cycle). | 194 // can't import 'server/auth' (it creates an import cycle). |
| 162 internal.RegisterClientFactory(func(c context.Context, scopes []string)
(*http.Client, error) { | 195 internal.RegisterClientFactory(func(c context.Context, scopes []string)
(*http.Client, error) { |
| 163 var t http.RoundTripper | 196 var t http.RoundTripper |
| 164 var err error | 197 var err error |
| (...skipping 24 matching lines...) Expand all Loading... |
| 189 type headersGetter func(c context.Context, uri string, opts *rpcOptions) (map[st
ring]string, error) | 222 type headersGetter func(c context.Context, uri string, opts *rpcOptions) (map[st
ring]string, error) |
| 190 | 223 |
| 191 type rpcOptions struct { | 224 type rpcOptions struct { |
| 192 kind RPCAuthorityKind | 225 kind RPCAuthorityKind |
| 193 scopes []string | 226 scopes []string |
| 194 delegationToken string | 227 delegationToken string |
| 195 getRPCHeaders headersGetter | 228 getRPCHeaders headersGetter |
| 196 rpcMocks *rpcMocks | 229 rpcMocks *rpcMocks |
| 197 } | 230 } |
| 198 | 231 |
| 199 // makeRpcOptions applies all options and validates them. | 232 // makeRPCOptions applies all options and validates them. |
| 200 func makeRpcOptions(kind RPCAuthorityKind, opts []RPCOption) (*rpcOptions, error
) { | 233 func makeRPCOptions(kind RPCAuthorityKind, opts []RPCOption) (*rpcOptions, error
) { |
| 201 options := &rpcOptions{kind: kind} | 234 options := &rpcOptions{kind: kind} |
| 202 for _, o := range opts { | 235 for _, o := range opts { |
| 203 o.apply(options) | 236 o.apply(options) |
| 204 } | 237 } |
| 205 // Validate options. | 238 // Validate options. |
| 206 switch { | 239 switch { |
| 207 case options.kind == AsSelf && len(options.scopes) == 0: | 240 case options.kind == AsSelf && len(options.scopes) == 0: |
| 208 options.scopes = defaultOAuthScopes | 241 options.scopes = defaultOAuthScopes |
| 209 case options.kind != AsSelf && len(options.scopes) != 0: | 242 case options.kind != AsSelf && len(options.scopes) != 0: |
| 210 return nil, fmt.Errorf("auth: WithScopes can only be used with A
sSelf authorization kind") | 243 return nil, fmt.Errorf("auth: WithScopes can only be used with A
sSelf authorization kind") |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 299 // Use our own OAuth token too, since the delegation token is bound to u
s. | 332 // Use our own OAuth token too, since the delegation token is bound to u
s. |
| 300 oauthTok, err := cfg.AccessTokenProvider(c, []string{auth.OAuthScopeEmai
l}) | 333 oauthTok, err := cfg.AccessTokenProvider(c, []string{auth.OAuthScopeEmai
l}) |
| 301 if err != nil { | 334 if err != nil { |
| 302 return nil, err | 335 return nil, err |
| 303 } | 336 } |
| 304 return map[string]string{ | 337 return map[string]string{ |
| 305 "Authorization": oauthTok.TokenType + " " + oauthTok.A
ccessToken, | 338 "Authorization": oauthTok.TokenType + " " + oauthTok.A
ccessToken, |
| 306 delegation.HTTPHeaderName: delegationToken, | 339 delegation.HTTPHeaderName: delegationToken, |
| 307 }, nil | 340 }, nil |
| 308 } | 341 } |
| OLD | NEW |