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