12 "github.com/google/uuid"
13 "github.com/octo/retry"
14 legacy_context "golang.org/x/net/context"
16 "google.golang.org/appengine/datastore"
17 "google.golang.org/appengine/log"
31 func NewUser(ctx context.Context, email string) (*User, error) {
33 err := datastore.RunInTransaction(ctx, func(ctx legacy_context.Context) error {
34 key := datastore.NewKey(ctx, "User", email, 0, nil)
37 err := datastore.Get(ctx, key, &u)
38 if err != nil && err != datastore.ErrNoSuchEntity {
46 id = uuid.New().String()
47 _, err = datastore.Put(ctx, key, &dbUser{
57 key: datastore.NewKey(ctx, "User", email, 0, nil),
63 func UserByID(ctx context.Context, id string) (*User, error) {
64 q := datastore.NewQuery("User").Filter("ID=", id).KeysOnly()
65 keys, err := q.GetAll(ctx, nil)
67 return nil, fmt.Errorf("datastore.Query.GetAll(): %v", err)
70 return nil, fmt.Errorf("len(keys) = %d, want 1", len(keys))
76 Email: keys[0].StringID(),
80 func (u *User) Token(ctx context.Context, svc string) (*oauth2.Token, error) {
81 key := datastore.NewKey(ctx, "Token", svc, 0, u.key)
84 if err := datastore.Get(ctx, key, &tok); err != nil {
91 func (u *User) SetToken(ctx context.Context, svc string, tok *oauth2.Token) error {
92 key := datastore.NewKey(ctx, "Token", svc, 0, u.key)
93 _, err := datastore.Put(ctx, key, tok)
97 func (u *User) DeleteToken(ctx context.Context, svc string) error {
98 key := datastore.NewKey(ctx, "Token", svc, 0, u.key)
99 return datastore.Delete(ctx, key)
102 func (u *User) OAuthClient(ctx context.Context, svc string, cfg *oauth2.Config) (*http.Client, error) {
103 key := datastore.NewKey(ctx, "Token", svc, 0, u.key)
106 if err := datastore.Get(ctx, key, &tok); err != nil {
107 return nil, fmt.Errorf("datastore.Get(%v) = %v", key, err)
110 src := cfg.TokenSource(ctx, &tok)
111 c := oauth2.NewClient(ctx, &persistingTokenSource{
117 c.Transport = retry.Transport{
118 RoundTripper: c.Transport,
124 func (u *User) String() string {
128 func (u *User) Sign(payload string) string {
129 mac := hmac.New(sha1.New, []byte(u.ID))
130 mac.Write([]byte(payload))
132 return hex.EncodeToString(mac.Sum(nil))
135 type persistingTokenSource struct {
138 src oauth2.TokenSource
144 func (s *persistingTokenSource) Token() (*oauth2.Token, error) {
148 tok, err := s.src.Token()
153 if s.t.AccessToken != tok.AccessToken ||
154 s.t.TokenType != tok.TokenType ||
155 s.t.RefreshToken != tok.RefreshToken ||
156 !s.t.Expiry.Equal(tok.Expiry) {
157 if _, err := datastore.Put(s.ctx, s.key, tok); err != nil {
158 log.Errorf(s.ctx, "persisting OAuth token in datastore failed: %v", err)