-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmailAuth.ur
181 lines (159 loc) · 5.39 KB
/
mailAuth.ur
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
open Crypto
open StringTypes
type user = string
type token = {Id : int, Secret : string}
type userName = string
sequence ids
table userCredentials : {UserName : string, Hash : string, Salt : string}
PRIMARY KEY UserName,
CONSTRAINT UserName UNIQUE UserName
table userLinks : {Id : int,
WhenRequested : time,
Approved : bool,
TokenHash : string,
TokenSalt : string,
UserName : string,
Email : string}
PRIMARY KEY UserName,
CONSTRAINT Id UNIQUE Id
table userTokens : {Id : int,
TokenHash : string,
TokenSalt : string,
WhenCreated : time,
UserName : string
}
PRIMARY KEY Id, CONSTRAINT Id UNIQUE Id
val token_show = mkShow (fn (x : token) => (show x.Id) ^ "." ^ x.Secret)
fun _notifyUserOfToken (t : token) (e : addr) : transaction unit =
let
val message = (show e) ^ " : " ^ (show t) ^ "\n"
in
_ <- Process.exec ("cat >> /home/navarre/tokens.dat") (textBlob message) 0;
return ()
end
fun _addEmailLink (u : string) (e : addr) : transaction unit =
requestTime <- now;
myToken <- token 40;
newId <- nextval ids;
_notifyUserOfToken {Id = newId, Secret = myToken.Token} e;
dml (INSERT INTO
userLinks (Id, UserName, Email, WhenRequested, Approved, TokenHash, TokenSalt)
VALUES
(
{[newId]},
{[u]},
{[show e]},
{[requestTime]},
{[False]},
{[getHash myToken.Hash]},
{[getSalt myToken.Hash]}
)
)
fun _hexchars (s : string) : bool =
let
fun hexChar (c : char) : bool =
case (String.index "=+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" c) of
None => False
| Some _ => True
in
String.all hexChar s
end
fun readToken (s : string) : option token =
case (String.split (String.trim s) #".") of
None => None
| Some (a : string, b : string) => (
let
val maybeId : option int = read a
val saneSecret : bool = _hexchars b
in
case maybeId of
None => None
| Some id => (
if
saneSecret
then
Some {Secret = b, Id = id}
else
None
)
end
)
fun usernameExists (username : string) : transaction bool =
rows <- queryL (SELECT * FROM userCredentials WHERE userCredentials.UserName = {[username]});
return (List.length rows > 0)
fun addEmailLink (u : user) (e : addr) : transaction unit =
_addEmailLink (show u) e
fun writeAccount (u : string) (p : string) : transaction bool =
exists <- usernameExists u;
hashedPass <- hash p;
if
exists
then
return False
else
dml (INSERT INTO userCredentials (UserName, Hash, Salt) VALUES ({[u]}, {[getHash hashedPass]}, {[getSalt hashedPass]}));
return True
fun newAccount (u : string) (p : string) (e : addr) : transaction bool =
success <- writeAccount u p;
if
success
then
_addEmailLink u e;
return True
else
return False
fun blessEmailLink (u : string) (t : token) : transaction bool =
timeNow <- now;
rows <- queryL (SELECT * FROM userLinks WHERE userLinks.Id = {[t.Id]});
case rows of
[] => return False
| row :: _ =>
correctToken <- verify t.Secret row.UserLinks.TokenHash row.UserLinks.TokenSalt;
if
row.UserLinks.UserName = u
&&
ge (addSeconds row.UserLinks.WhenRequested (24 * 3600)) timeNow
&&
correctToken
then
dml (UPDATE userLinks SET Approved = TRUE WHERE Id = {[t.Id]});
return True
else
return False
fun _getEmails (username : string) : transaction (list (option addr)) =
rows <- queryL (SELECT * FROM userLinks WHERE userLinks.Approved = {[True]} AND userLinks.UserName = {[show username]});
return (List.mp (fn x => fromString x.UserLinks.Email) rows)
fun signIn (username : string) (password : string) : transaction (option user) =
rows <- queryL (SELECT * FROM userCredentials WHERE userCredentials.UserName = {[username]});
releventEmails <- _getEmails username;
case rows of
[] => return None
| row :: _ =>
correctPassword <- verify password row.UserCredentials.Hash row.UserCredentials.Salt;
if correctPassword then
return (Some username)(* (Some {UserName = username, Emails = releventEmails}) *)
else
return None
fun newToken (u : user) : transaction token =
tokenOut <- Crypto.token 20;
timeNow <- now;
newId <- nextval ids;
dml (INSERT INTO userTokens (TokenHash, TokenSalt, WhenCreated, UserName, Id) VALUES ({[Crypto.getHash (tokenOut.Hash)]}, {[Crypto.getSalt (tokenOut.Hash)]}, {[timeNow]}, {[u]}, {[newId]}));
return {Id = newId, Secret = tokenOut.Token}
fun loadUser (t : token) : transaction (option user) =
rows <- queryL (SELECT * FROM userTokens WHERE userTokens.Id = {[t.Id]});
case rows of
[] => return None
| row :: _ =>
timeNow <- now;
verified <- verify t.Secret row.UserTokens.TokenHash row.UserTokens.TokenSalt;
if ((addSeconds row.UserTokens.WhenCreated (2 * 3600)) > timeNow && verified) then
return (Some row.UserTokens.UserName)
else
return None
fun blessUserName (name : string) : transaction (option string) =
rows <- queryL (SELECT userCredentials.UserName FROM userCredentials WHERE userCredentials.UserName = {[name]});
case rows of
[] => return None
| item :: _ => return (Some name)
val show_username = mkShow (fn (x : userName) => x)