-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathusers.dylan
150 lines (132 loc) · 4.46 KB
/
users.dylan
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
module: users
author: Hannes Mehnert <[email protected]>
define thread variable *authenticated-user* = #f;
// The default "realm" value passed in the WWW-Authenticate header.
//
define variable *default-authentication-realm* :: <string> = "web-framework";
// Because clients (browsers) continue to send the Authentication header
// once an authentication has been accepted (at least until the browser
// is restarted, it seems) we need to keep track of the fact that a user
// has logged out by storing the auth values here.
//
// Also, note that if the server restarts and browsers resend the auth,
// the user is suddenly logged in again. Yikes.
//
define variable *ignore-authorizations* = list();
define variable *ignore-logins* = list();
define open class <user> (<object>)
slot user-name :: <string>,
required-init-keyword: name:;
slot user-password :: <string>,
required-init-keyword: password:;
slot user-email :: <string>,
required-init-keyword: email:;
slot administrator? :: <boolean> = #f,
init-keyword: administrator?:;
slot user-activation-key :: <string>,
init-keyword: activation-key:;
slot user-activated? :: <boolean> = #f,
init-keyword: activated?:;
end class <user>;
define method initialize (user :: <user>, #key)
next-method();
if (find-user(user.user-name))
signal(make(<web-error>,
error: format-to-string("A user named '%s' already exists.",
user.user-name)));
else
if (~slot-initialized?(user, user-activation-key))
user.user-activation-key := generate-activation-key(user);
end;
save(user);
end if;
end method initialize;
define method generate-activation-key
(user :: <user>)
=> (key :: <string>)
// temporary. should be more secure.
base64-encode(concatenate(user.user-name, user.user-email))
end;
define method key (user :: <user>)
=> (res :: <string>)
user.user-name;
end;
define method storage-type (type == <user>) => (res)
<string-table>
end;
define method as (class == <string>, user :: <user>)
=> (result :: <string>)
user.user-name;
end;
define function authenticated-user ()
=> (user :: false-or(<user>))
authenticate();
*authenticated-user*
end;
define function find-user
(name :: <string>)
=> (user :: false-or(<user>))
element(storage(<user>), name, default: #f);
end;
define method login (#key realm :: false-or(<string>))
let redirect-url = get-query-value("redirect");
let user = check-authorization();
if (~user)
require-authorization(realm: realm);
elseif (member?(user, *ignore-authorizations*, test: \=) &
member?(user, *ignore-logins*, test: \=))
*ignore-authorizations* := remove!(*ignore-authorizations*, user);
require-authorization(realm: realm);
elseif (~member?(user, *ignore-authorizations*, test: \=) &
member?(user, *ignore-logins*, test: \=))
*ignore-logins* := remove!(*ignore-logins*, user);
redirect-url & redirect-to(redirect-url);
else
redirect-url & redirect-to(redirect-url);
end if;
end;
define function logout ()
let user = check-authorization();
if (user)
*authenticated-user* := #f;
*ignore-authorizations* :=
add!(*ignore-authorizations*, user);
*ignore-logins* :=
add!(*ignore-logins*, user);
end if;
let redirect-url = get-query-value("redirect");
redirect-url & redirect-to(redirect-url);
end;
define function check-authorization ()
=> (user :: false-or(<user>))
let authorization = get-header(current-request(), "Authorization", parsed: #t);
if (authorization)
let user = find-user(head(authorization));
if (user
& user.user-activated?
& user.user-password = tail(authorization))
user
end
end
end function check-authorization;
define function authenticate ()
=> (user :: false-or(<user>))
let user = check-authorization();
if (user)
*authenticated-user*
:= if (~member?(user, *ignore-authorizations*, test: \=)
& ~member?(user, *ignore-logins*, test: \=))
user
end;
end
end function authenticate;
define function require-authorization (#key realm :: false-or(<string>))
let realm = realm | *default-authentication-realm*;
let response = current-response();
set-header(response, "WWW-Authenticate", concatenate("Basic realm=\"", realm, "\""));
unauthorized-error(headers: response-headers(response));
end;
define method \= (user1 :: <user>, user2 :: <user>)
=> (equal? :: <boolean>)
user1.user-name = user2.user-name
end;