/
auth.go
160 lines (133 loc) · 3.43 KB
/
auth.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package main
import (
"errors"
log "github.com/Sirupsen/logrus"
"sync"
"time"
)
/*
* The available groups are as follows
* - Read-Only: This group can view the current cracks and all outputs,
* but cannot create a job.
* - Standard User: This group can create, view, and stop jobs, but has no
* access to add, remove, or pause resource or the queue itself.
* - Administrator: These users can do any function provided by the API.
*/
const (
ReadOnly = "Read-Only"
StandardUser = "Standard User"
Administrator = "Administrator"
)
// Value in minutes
var SessionExpiration = 30 * time.Minute
/*
* Structure used to represent a user logged into the API.
*/
type User struct {
Username string
Groups []string
LogOnTime time.Time
Timeout time.Time
}
func (u *User) EffectiveRole() string {
var role string
for _, v := range u.Groups {
switch role {
case "":
role = v
case Administrator:
return role
case StandardUser:
if v == Administrator {
role = Administrator
}
case ReadOnly:
if v == StandardUser {
role = StandardUser
}
if v == Administrator {
role = Administrator
}
}
}
return role
}
func (u *User) Allowed(required string) bool {
if u.EffectiveRole() == Administrator {
return true
}
if u.EffectiveRole() == required {
return true
}
if u.EffectiveRole() == StandardUser && required == ReadOnly {
return true
}
return false
}
/*
* This interface is used to allow multiple different types of authenticator
* mechanisms to be used. Given a username and password it should return User
* structure if the login was successful and an error if not. The Username,
* Groups, Email, and LogOnTime should be populated by the Authenticator. Token will
* be taken care of by the API package itself. It will overide any value
* provided by default. Authenticators must be thread safe.
*/
type Authenticator interface {
Login(user, pass string) (User, error)
}
/*
* The token store saves the valid tokens and the time they expire. The 30
* minute timer is renewed after every successful check.
*/
type TokenStore struct {
store map[string]*User
sync.Mutex
}
func NewTokenStore() TokenStore {
return TokenStore{
store: map[string]*User{},
}
}
func (t *TokenStore) AddToken(token string, user User) {
t.Lock()
defer t.Unlock()
t.store[token] = &user
t.store[token].Timeout = time.Now().Add(30 * time.Minute)
log.WithFields(log.Fields{
"user": user.Username,
"token": token,
}).Debug("Token added to user store.")
}
func (t *TokenStore) RemoveToken(token string) {
t.Lock()
defer t.Unlock()
delete(t.store, token)
}
func (t *TokenStore) CheckToken(token string) bool {
t.Lock()
defer t.Unlock()
if user, ok := t.store[token]; ok {
// Check that this ticket hasn't timed out
if 0 > user.Timeout.Sub(time.Now()) {
// Token has expired so we should return false and remove the token
delete(t.store, token)
log.Warn("Token was attempted that has timed out and is no longer valid.")
return false
}
// Token exists and has not timed out so return true and reset time
t.store[token].Timeout = time.Now().Add(30 * time.Minute)
return true
}
// Token did not exist to return false
return false
}
func (t *TokenStore) GetUser(token string) (User, error) {
t.Lock()
defer t.Unlock()
// Check for valid token
if user, ok := t.store[token]; ok {
// return the user we just got
return *user, nil
}
return User{}, errors.New("Invalid Token")
}