]> gitweb.factorcode.org Git - factor.git/blob - basis/furnace/auth/auth.factor
e2930f461b4a2710ea82e0a7094456d8789e2ccc
[factor.git] / basis / furnace / auth / auth.factor
1 ! Copyright (c) 2008, 2010 Slava Pestov
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors assocs namespaces kernel sequences sets
4 destructors combinators fry logging io.encodings.utf8
5 io.encodings.string io.binary io.sockets.secure random checksums
6 checksums.sha urls
7 html.forms
8 http.server
9 http.server.filters
10 http.server.dispatchers
11 furnace.actions
12 furnace.utilities
13 furnace.redirection
14 furnace.boilerplate
15 furnace.auth.providers
16 furnace.auth.providers.db ;
17 IN: furnace.auth
18
19 SYMBOL: logged-in-user
20
21 : logged-in? ( -- ? )
22     logged-in-user get >boolean ;
23
24 : username ( -- string/f )
25     logged-in-user get dup [ username>> ] when ;
26
27 GENERIC: init-user-profile ( responder -- )
28
29 M: object init-user-profile drop ;
30
31 M: dispatcher init-user-profile
32     default>> init-user-profile ;
33
34 M: filter-responder init-user-profile
35     responder>> init-user-profile ;
36
37 : current-profile ( -- assoc ) logged-in-user get profile>> ;
38
39 : user-changed ( -- )
40     logged-in-user get t >>changed? drop ;
41
42 : uget ( key -- value )
43     current-profile at ;
44
45 : uset ( value key -- )
46     current-profile set-at
47     user-changed ;
48
49 : uchange ( quot key -- )
50     current-profile swap change-at
51     user-changed ; inline
52
53 SYMBOL: capabilities
54
55 V{ } clone capabilities set-global
56
57 : define-capability ( word -- ) capabilities get adjoin ;
58
59 TUPLE: realm < dispatcher name users checksum secure ;
60
61 GENERIC: login-required* ( description capabilities realm -- response )
62
63 GENERIC: user-registered ( user realm -- response )
64
65 M: object user-registered 2drop URL" $realm" <redirect> ;
66
67 GENERIC: init-realm ( realm -- )
68
69 GENERIC: logged-in-username ( realm -- username )
70
71 : login-required ( description capabilities -- * )
72     realm get login-required* exit-with ;
73
74 : new-realm ( responder name class -- realm )
75     new-dispatcher
76         swap >>name
77         swap >>default
78         users-in-db >>users
79         sha-256 >>checksum
80         ssl-supported? >>secure ; inline
81
82 : users ( -- provider )
83     realm get users>> ;
84
85 TUPLE: user-saver user ;
86
87 C: <user-saver> user-saver
88
89 M: user-saver dispose
90     user>> dup changed?>> [ users update-user ] [ drop ] if ;
91
92 : save-user-after ( user -- )
93     <user-saver> &dispose drop ;
94
95 : init-user ( user -- )
96     [ [ logged-in-user namespaces:set ] [ save-user-after ] bi ] when* ;
97
98 \ init-user DEBUG add-input-logging
99
100 M: realm call-responder*
101     dup realm namespaces:set
102     logged-in? [
103         dup init-realm
104         dup logged-in-username
105         dup [ users get-user ] when
106         init-user
107     ] unless
108     call-next-method ;
109
110 : encode-password ( string salt -- bytes )
111     [ utf8 encode ] [ 4 >be ] bi* append
112     realm get checksum>> checksum-bytes ;
113
114 : >>encoded-password ( user string -- user )
115     32 random-bits [ encode-password ] keep
116     [ >>password ] [ >>salt ] bi* ; inline
117
118 : valid-login? ( password user -- ? )
119     [ salt>> encode-password ] [ password>> ] bi = ;
120
121 : check-login ( password username -- user/f )
122     users get-user dup [ [ valid-login? ] keep and ] [ 2drop f ] if ;
123
124 : if-secure-realm ( quot -- )
125     realm get secure>> [ if-secure ] [ call ] if ; inline
126
127 TUPLE: secure-realm-only < filter-responder ;
128
129 C: <secure-realm-only> secure-realm-only
130
131 M: secure-realm-only call-responder*
132     '[ _ _ call-next-method ] if-secure-realm ;
133
134 TUPLE: protected < filter-responder description capabilities ;
135
136 : <protected> ( responder -- protected )
137     protected new
138         swap >>responder ;
139
140 : have-capabilities? ( capabilities -- ? )
141     realm get secure>> secure-connection? not and [ drop f ] [
142         logged-in-user get {
143             { [ dup not ] [ 2drop f ] }
144             { [ dup deleted>> 1 = ] [ 2drop f ] }
145             [ capabilities>> subset? ]
146         } cond
147     ] if ;
148
149 M: protected call-responder*
150     dup protected namespaces:set
151     dup capabilities>> have-capabilities?
152     [ call-next-method ] [
153         [ drop ] [ [ description>> ] [ capabilities>> ] bi ] bi*
154         realm get login-required*
155     ] if ;
156
157 : <auth-boilerplate> ( responder -- responder' )
158     <boilerplate> { realm "boilerplate" } >>template ;
159
160 : password-mismatch ( -- * )
161     "passwords do not match" validation-error
162     validation-failed ;
163
164 : same-password-twice ( -- )
165     "new-password" value "verify-password" value =
166     [ password-mismatch ] unless ;
167
168 : user-exists ( -- * )
169     "username taken" validation-error
170     validation-failed ;