/
backend.go
129 lines (113 loc) · 3.63 KB
/
backend.go
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
126
127
128
129
/**
* This package provide types of authentication methods.
**/
package backend
import (
"log"
"database/sql"
_ "github.com/lib/pq"
"crypto/sha256"
"github.com/aspic/go-auth/common"
"fmt"
)
var config *Config
var db *sql.DB
type Auther interface {
Auth(username string, password string, realm string) (bool, string)
ValidateByUser(user *common.User) *common.AuthInfo
}
type Simple struct {}
type Postgres struct {}
type Config struct {
Key string // JWT Secret
Auth string // Authentication method
Username string // Username (for auth or db)
Password string // Password (for auth or db)
Host string // database host
Database string // database
Expire int // Hours until token expire
}
// Reads username and password from the configuration file
func (s *Simple) Auth(username string, password string, realm string) (bool, string) {
return username == config.Username && password == config.Password, config.Key
}
// Auths with postgresql database as back end
func (p *Postgres) Auth(username string, password string, realm string) (bool, string) {
var hash string
var salt string
var key string
stmt, err := db.Prepare(
`SELECT u.pw_hash, u.salt, realm.key FROM identity AS u, realm, inrealm
WHERE inrealm.id = u.id AND inrealm.realm = realm.id
AND realm.name = $1 AND u.username = $2`)
if err != nil {
log.Print("Failed to execute query: ", err)
return false, ""
}
stmt.QueryRow(realm, username).Scan(&hash, &salt, &key)
log.Print("Got key: ", key)
if hash != "" && salt != "" {
return validPassword(password, salt, hash), key
}
log.Printf("Unable to authenticate user: %s for realm %s", username, realm)
return false, ""
}
func (s *Simple) ValidateByUser(user *common.User) *common.AuthInfo {
if user.Username == config.Username {
return &common.AuthInfo{Key: config.Key, User: user}
}
return nil
}
func (s *Postgres) ValidateByUser(user *common.User) *common.AuthInfo {
stmt, err := db.Prepare(
`SELECT r.name, r.key FROM identity AS i, realm AS r, inrealm
WHERE inrealm.id = i.id AND r.id = inrealm.realm AND i.username = $1`)
if err != nil {
log.Fatal(err)
}
rows, err := stmt.Query(user.Username)
realms := make([]*common.Realm, 0)
var key string
for rows.Next() {
var realmName string
var realmKey string
if err := rows.Scan(&realmName, &realmKey); err != nil {
log.Fatal(err)
}
if realmName == user.Realm {
key = realmKey
}
realms = append(realms, &common.Realm{Name: realmName})
}
// Got valid realm and matching key
if len(realms) > 0 && key != "" {
return &common.AuthInfo{Key: key, User: user, Realms: realms}
}
return nil
}
func validPassword(pw string, salt string, pwHash string) bool {
pwBytes := []byte(salt + pw)
hasher := sha256.New()
hasher.Write(pwBytes)
sum := fmt.Sprintf("%x", hasher.Sum(nil))
return sum == pwHash
}
func New (conf *Config) Auther {
var err error
config = conf
if config.Auth == "simpleAuth" {
return &Simple{}
} else if config.Auth == "pgAuth" {
// Load database
props := fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=require", config.Username, config.Password, config.Host, config.Database)
db, err = sql.Open("postgres", props)
if err != nil {
log.Fatal(err)
return nil
}
return &Postgres{}
} else {
log.Fatal("No backend set, fix configuration")
}
return nil
}