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
10 http.server.dispatchers
15 furnace.auth.providers
16 furnace.auth.providers.db ;
17 FROM: assocs => change-at ;
18 FROM: namespaces => set ;
21 SYMBOL: logged-in-user
24 logged-in-user get >boolean ;
26 : username ( -- string/f )
27 logged-in-user get dup [ username>> ] when ;
29 GENERIC: init-user-profile ( responder -- )
31 M: object init-user-profile drop ;
33 M: dispatcher init-user-profile
34 default>> init-user-profile ;
36 M: filter-responder init-user-profile
37 responder>> init-user-profile ;
39 : current-profile ( -- assoc ) logged-in-user get profile>> ;
42 logged-in-user get t >>changed? drop ;
44 : uget ( key -- value )
47 : uset ( value key -- )
48 current-profile set-at
51 : uchange ( quot key -- )
52 current-profile swap change-at
57 V{ } clone capabilities set-global
59 : define-capability ( word -- ) capabilities get adjoin ;
61 TUPLE: realm < dispatcher name users checksum secure ;
63 GENERIC: login-required* ( description capabilities realm -- response )
65 GENERIC: user-registered ( user realm -- response )
67 M: object user-registered 2drop URL" $realm" <redirect> ;
69 GENERIC: init-realm ( realm -- )
71 GENERIC: logged-in-username ( realm -- username )
73 : login-required ( description capabilities -- * )
74 realm get login-required* exit-with ;
76 : new-realm ( responder name class -- realm )
82 ssl-supported? >>secure ; inline
84 : users ( -- provider )
87 TUPLE: user-saver user ;
89 C: <user-saver> user-saver
92 user>> dup changed?>> [ users update-user ] [ drop ] if ;
94 : save-user-after ( user -- )
95 <user-saver> &dispose drop ;
97 : init-user ( user -- )
98 [ [ logged-in-user set ] [ save-user-after ] bi ] when* ;
100 \ init-user DEBUG add-input-logging
102 M: realm call-responder* ( path responder -- response )
106 dup logged-in-username
107 dup [ users get-user ] when
112 : encode-password ( string salt -- bytes )
113 [ utf8 encode ] [ 4 >be ] bi* append
114 realm get checksum>> checksum-bytes ;
116 : >>encoded-password ( user string -- user )
117 32 random-bits [ encode-password ] keep
118 [ >>password ] [ >>salt ] bi* ; inline
120 : valid-login? ( password user -- ? )
121 [ salt>> encode-password ] [ password>> ] bi = ;
123 : check-login ( password username -- user/f )
124 users get-user dup [ [ valid-login? ] keep and ] [ 2drop f ] if ;
126 : if-secure-realm ( quot -- )
127 realm get secure>> [ if-secure ] [ call ] if ; inline
129 TUPLE: secure-realm-only < filter-responder ;
131 C: <secure-realm-only> secure-realm-only
133 M: secure-realm-only call-responder*
134 '[ _ _ call-next-method ] if-secure-realm ;
136 TUPLE: protected < filter-responder description capabilities ;
138 : <protected> ( responder -- protected )
142 : have-capabilities? ( capabilities -- ? )
143 realm get secure>> secure-connection? not and [ drop f ] [
145 { [ dup not ] [ 2drop f ] }
146 { [ dup deleted>> 1 = ] [ 2drop f ] }
147 [ capabilities>> subset? ]
151 M: protected call-responder* ( path responder -- response )
153 dup capabilities>> have-capabilities?
154 [ call-next-method ] [
155 [ drop ] [ [ description>> ] [ capabilities>> ] bi ] bi*
156 realm get login-required*
159 : <auth-boilerplate> ( responder -- responder' )
160 <boilerplate> { realm "boilerplate" } >>template ;
162 : password-mismatch ( -- * )
163 "passwords do not match" validation-error
166 : same-password-twice ( -- )
167 "new-password" value "verify-password" value =
168 [ password-mismatch ] unless ;
170 : user-exists ( -- * )
171 "username taken" validation-error