Jiri Simsa | d7616c9 | 2015-03-24 23:44:30 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Vanadium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 5 | package blesser |
| 6 | |
| 7 | import ( |
Asim Shankar | 7106157 | 2014-07-22 16:59:18 -0700 | [diff] [blame] | 8 | "fmt" |
Asim Shankar | 3bbd1e3 | 2015-01-09 11:52:54 -0800 | [diff] [blame] | 9 | "strings" |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 10 | "time" |
| 11 | |
Jiri Simsa | ffceefa | 2015-02-28 11:03:34 -0800 | [diff] [blame] | 12 | "v.io/x/ref/services/identity" |
Suharsh Sivakumar | c004811 | 2015-03-19 11:48:28 -0700 | [diff] [blame] | 13 | "v.io/x/ref/services/identity/internal/oauth" |
| 14 | "v.io/x/ref/services/identity/internal/revocation" |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 15 | |
Todd Wang | 54feabe | 2015-04-15 23:38:26 -0700 | [diff] [blame] | 16 | "v.io/v23/context" |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 17 | "v.io/v23/rpc" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 18 | "v.io/v23/security" |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 19 | ) |
| 20 | |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 21 | type oauthBlesser struct { |
| 22 | oauthProvider oauth.OAuthProvider |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 23 | authcodeClient struct{ ID, Secret string } |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 24 | accessTokenClients []oauth.AccessTokenClient |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 25 | duration time.Duration |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 26 | dischargerLocation string |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 27 | revocationManager revocation.RevocationManager |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 28 | } |
| 29 | |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 30 | // OAuthBlesserParams represents all the parameters provided to NewOAuthBlesserServer |
| 31 | type OAuthBlesserParams struct { |
| 32 | // The OAuth provider that must have issued the access tokens accepted by ths service. |
| 33 | OAuthProvider oauth.OAuthProvider |
Srdjan Petrovic | 89779ad | 2014-11-03 15:40:36 -0800 | [diff] [blame] | 34 | // The OAuth client IDs and names for the clients of the BlessUsingAccessToken RPCs. |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 35 | AccessTokenClients []oauth.AccessTokenClient |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 36 | // The object name of the discharger service. If this is empty then revocation caveats will not be granted. |
| 37 | DischargerLocation string |
| 38 | // The revocation manager that generates caveats and manages revocation. |
Suharsh Sivakumar | 8e898db | 2014-12-17 14:07:21 -0800 | [diff] [blame] | 39 | RevocationManager revocation.RevocationManager |
Asim Shankar | b18a44f | 2014-10-21 20:25:07 -0700 | [diff] [blame] | 40 | // The duration for which blessings will be valid. (Used iff RevocationManager is nil). |
| 41 | BlessingDuration time.Duration |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 42 | } |
| 43 | |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 44 | // NewOAuthBlesserServer provides an identity.OAuthBlesserService that uses OAuth2 |
| 45 | // access tokens to obtain the username of a client and provide blessings with that |
| 46 | // name. |
Asim Shankar | 6107179 | 2014-07-22 13:03:18 -0700 | [diff] [blame] | 47 | // |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 48 | // Blessings generated by this service carry a third-party revocation caveat if a |
| 49 | // RevocationManager is specified by the params or they carry an ExpiryCaveat that |
Asim Shankar | c195b6d | 2015-02-03 11:26:55 -0800 | [diff] [blame] | 50 | // expires after the duration specified by the params. |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 51 | func NewOAuthBlesserServer(p OAuthBlesserParams) identity.OAuthBlesserServerStub { |
| 52 | return identity.OAuthBlesserServer(&oauthBlesser{ |
| 53 | oauthProvider: p.OAuthProvider, |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 54 | duration: p.BlessingDuration, |
Suharsh Sivakumar | fb5cbb7 | 2014-08-27 13:14:22 -0700 | [diff] [blame] | 55 | dischargerLocation: p.DischargerLocation, |
| 56 | revocationManager: p.RevocationManager, |
Suharsh Sivakumar | d308c7e | 2014-10-03 12:46:50 -0700 | [diff] [blame] | 57 | accessTokenClients: p.AccessTokenClients, |
| 58 | }) |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 59 | } |
| 60 | |
Todd Wang | 4264e4b | 2015-04-16 22:43:40 -0700 | [diff] [blame] | 61 | func (b *oauthBlesser) BlessUsingAccessToken(ctx *context.T, call rpc.ServerCall, accessToken string) (security.Blessings, string, error) { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 62 | var noblessings security.Blessings |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 63 | email, clientName, err := b.oauthProvider.GetEmailAndClientName(accessToken, b.accessTokenClients) |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 64 | if err != nil { |
Ankur | 123a5c7 | 2015-01-12 16:03:43 -0800 | [diff] [blame] | 65 | return noblessings, "", err |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 66 | } |
Suharsh Sivakumar | 6069de7 | 2015-08-06 17:25:33 -0700 | [diff] [blame^] | 67 | return b.bless(ctx, call.Security(), email, clientName, nil) |
Asim Shankar | 7a72175 | 2014-08-02 14:27:23 -0700 | [diff] [blame] | 68 | } |
| 69 | |
Suharsh Sivakumar | 6069de7 | 2015-08-06 17:25:33 -0700 | [diff] [blame^] | 70 | func (b *oauthBlesser) BlessUsingAccessTokenWithCaveats(ctx *context.T, call rpc.ServerCall, accessToken string, caveats []security.Caveat) (security.Blessings, string, error) { |
| 71 | var noblessings security.Blessings |
| 72 | email, clientName, err := b.oauthProvider.GetEmailAndClientName(accessToken, b.accessTokenClients) |
| 73 | if err != nil { |
| 74 | return noblessings, "", err |
| 75 | } |
| 76 | return b.bless(ctx, call.Security(), email, clientName, caveats) |
| 77 | } |
| 78 | |
| 79 | func (b *oauthBlesser) bless(ctx *context.T, call security.Call, email, clientName string, caveats []security.Caveat) (security.Blessings, string, error) { |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 80 | var noblessings security.Blessings |
Matt Rosencrantz | 9dce9b2 | 2015-03-02 10:48:37 -0800 | [diff] [blame] | 81 | self := call.LocalPrincipal() |
Asim Shankar | b3a82ba | 2014-10-29 11:41:27 -0700 | [diff] [blame] | 82 | if self == nil { |
| 83 | return noblessings, "", fmt.Errorf("server error: no authentication happened") |
| 84 | } |
Suharsh Sivakumar | 6069de7 | 2015-08-06 17:25:33 -0700 | [diff] [blame^] | 85 | // TODO(suharshs, ataly): Should we ensure that we have at least a revocation or expiry caveat? |
| 86 | if len(caveats) == 0 { |
| 87 | var caveat security.Caveat |
| 88 | var err error |
| 89 | if b.revocationManager != nil { |
| 90 | caveat, err = b.revocationManager.NewCaveat(self.PublicKey(), b.dischargerLocation) |
| 91 | } else { |
| 92 | caveat, err = security.NewExpiryCaveat(time.Now().Add(b.duration)) |
| 93 | } |
| 94 | if err != nil { |
| 95 | return noblessings, "", err |
| 96 | } |
| 97 | caveats = append(caveats, caveat) |
Ankur | 3c33d42 | 2014-10-09 11:53:25 -0700 | [diff] [blame] | 98 | } |
Asim Shankar | 3cea582 | 2015-05-11 20:06:43 -0700 | [diff] [blame] | 99 | // Append clientName (e.g., "android", "chrome") to the email and then bless under that. |
| 100 | // Since blessings issued by this process do not have many caveats on them and typically |
| 101 | // have a large expiry duration, we include the clientName in the extension so that |
| 102 | // servers can explicitly distinguish these clients while specifying authorization policies |
| 103 | // (say, via AccessLists). |
Asim Shankar | 8e54706 | 2015-05-15 01:00:33 -0700 | [diff] [blame] | 104 | extension := strings.Join([]string{email, clientName}, security.ChainSeparator) |
Suharsh Sivakumar | 6069de7 | 2015-08-06 17:25:33 -0700 | [diff] [blame^] | 105 | blessing, err := self.Bless(call.RemoteBlessings().PublicKey(), call.LocalBlessings(), extension, caveats[0], caveats[1:]...) |
Ankur | 3c33d42 | 2014-10-09 11:53:25 -0700 | [diff] [blame] | 106 | if err != nil { |
Asim Shankar | b18a44f | 2014-10-21 20:25:07 -0700 | [diff] [blame] | 107 | return noblessings, "", err |
Ankur | 3c33d42 | 2014-10-09 11:53:25 -0700 | [diff] [blame] | 108 | } |
Asim Shankar | b07ec69 | 2015-02-27 23:40:44 -0800 | [diff] [blame] | 109 | return blessing, extension, nil |
Ankur | 3c33d42 | 2014-10-09 11:53:25 -0700 | [diff] [blame] | 110 | } |