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 }