1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
package redis
import (
"fmt"
"log"
"github.com/gomodule/redigo/redis"
"golang.org/x/oauth2"
"git.ofmax.li/iserv/internal/auth"
"git.ofmax.li/iserv/internal/goog"
)
var (
createAuthLua = redis.NewScript(2, `
if redis.call("SISMEMBER","invites",KEYS[1]) == 1 then
return redis.call("HMSET",KEYS[2],unpack(ARGV))
end
return nil
`)
checkActiveAuthLua = redis.NewScript(1, `
local email = redis.call("HMGET",KEYS[1],"email")
if email == nil then
return false
end
return redis.call("SISMEMBER",email)
`)
authProfileKey = func(id string) string { return fmt.Sprintf("auth:%s") }
)
// AuthRepo supporting auth
type AuthRepo struct {
db *redis.Pool
}
// NewRedisAuthRepo redis.Pool
// creates auth repo around redis pool
func NewRedisAuthRepo(conn *redis.Pool) *AuthRepo {
return &AuthRepo{
conn,
}
}
// IsAuthorized is member of invites
func (db *AuthRepo) IsAuthorized(gp *goog.GoogleProfile) (bool, error) {
conn := db.db.Get()
defer conn.Close()
inviteKey := "goog:" + gp.Email
reply, err := redis.Bool(conn.Do("SISMEMBER", "invites", inviteKey))
if err != nil {
return reply, err
}
return reply, err
}
// LookUpAuthProfileID get internal profileid
func (db *AuthRepo) LookUpAuthProfileID(gp *goog.GoogleProfile) (string, error) {
conn := db.db.Get()
var id string
defer conn.Close()
reply, err := redis.Values(conn.Do("HMGET", authProfileKey(gp.ProfileID), "id"))
if err != nil {
// some other error
return "", err
}
if _, err := redis.Scan(reply, &id); err != nil {
return "", err
}
return id, nil
}
// SaveAuthProfile save profile, validate against invites
func (db *AuthRepo) SaveAuthProfile(email string, ap *auth.Profile) error {
// TODO this will over-write refresh tokens
conn := db.db.Get()
defer conn.Close()
prof := redis.Args{}.Add(email).Add(ap.ID).AddFlat(ap)
_, err := createAuthLua.Do(conn, prof...)
conn.Flush()
return err
}
// CheckProfileID return true if the profile exists and authorized
func (db *AuthRepo) CheckProfileID(id string) (bool, error) {
conn := db.db.Get()
defer conn.Close()
res, err := redis.Int(checkActiveAuthLua.Do(conn, authProfileKey(id)))
if err == redis.ErrNil {
return false, nil
} else if err != nil {
return false, err
}
// there could be profile created but later revoked authorization
return res == 1, nil
}
func (db *AuthRepo) getAuthProfile(id string) (*auth.Profile, error) {
conn := db.db.Get()
defer conn.Close()
authProfile := auth.Profile{}
res, err := redis.Values(conn.Do("HGETALL", authProfileKey(id)))
if err != nil {
log.Fatal(err)
}
log.Printf("getAuthProfile: %+v", res)
redis.ScanStruct(res, &authProfile)
return &authProfile, err
}
// GetProfileToken token for profile
func (db *AuthRepo) GetProfileToken(id string) (*oauth2.Token, error) {
profile, err := db.getAuthProfile(id)
if err != nil {
return nil, err
}
tkn, err := profile.Token()
if err != nil {
return nil, err
}
refreshedToken, err := oauth2.ReuseTokenSource(tkn, profile).Token()
if err != nil {
return nil, err
}
return refreshedToken, nil
}
|