From e47e7ec30c1b980a13118df8b0919476ca34680a Mon Sep 17 00:00:00 2001 From: Slava Pestov <slava@slava-pestovs-macbook-pro.local> Date: Mon, 16 Jun 2008 05:16:51 -0500 Subject: [PATCH] Login authentication is now stored outside of the session, allowing multiple independent login realms per site --- extra/furnace/alloy/alloy.factor | 5 +- extra/furnace/auth/auth-tests.factor | 6 ++ extra/furnace/auth/auth.factor | 9 +-- extra/furnace/auth/basic/basic.factor | 10 ++-- .../features/registration/registration.factor | 2 +- extra/furnace/auth/login/login.factor | 56 +++++++++++++++---- .../furnace/auth/login/permits/permits.factor | 30 ++++++++++ .../auth/providers/assoc/assoc-tests.factor | 6 +- extra/furnace/furnace.factor | 13 +++++ extra/furnace/sessions/sessions.factor | 24 ++------ extra/http/http.factor | 12 +++- extra/http/server/static/static.factor | 2 +- extra/webapps/wiki/wiki.factor | 7 ++- 13 files changed, 131 insertions(+), 51 deletions(-) create mode 100644 extra/furnace/auth/auth-tests.factor create mode 100644 extra/furnace/auth/login/permits/permits.factor diff --git a/extra/furnace/alloy/alloy.factor b/extra/furnace/alloy/alloy.factor index 14ffbaba9d..28c34e6715 100644 --- a/extra/furnace/alloy/alloy.factor +++ b/extra/furnace/alloy/alloy.factor @@ -7,7 +7,8 @@ furnace.flash furnace.sessions furnace.referrer furnace.db -furnace.auth.providers ; +furnace.auth.providers +furnace.auth.login.permits ; IN: furnace.alloy : <alloy> ( responder db params -- responder' ) @@ -19,7 +20,7 @@ IN: furnace.alloy <check-form-submissions> ] call ; -: state-classes { session flash-scope aside } ; inline +: state-classes { session flash-scope aside permit } ; inline : init-furnace-tables ( -- ) state-classes ensure-tables diff --git a/extra/furnace/auth/auth-tests.factor b/extra/furnace/auth/auth-tests.factor new file mode 100644 index 0000000000..220a8cd04c --- /dev/null +++ b/extra/furnace/auth/auth-tests.factor @@ -0,0 +1,6 @@ +USING: furnace.auth tools.test ; +IN: furnace.auth.tests + +\ logged-in-username must-infer +\ <protected> must-infer +\ new-realm must-infer diff --git a/extra/furnace/auth/auth.factor b/extra/furnace/auth/auth.factor index 9bb7ea105e..d9f517aaf4 100755 --- a/extra/furnace/auth/auth.factor +++ b/extra/furnace/auth/auth.factor @@ -82,15 +82,12 @@ M: user-saver dispose : save-user-after ( user -- ) <user-saver> &dispose drop ; -: init-user ( realm -- ) - logged-in-username [ - users get-user - [ logged-in-user set ] [ save-user-after ] bi - ] when* ; +: init-user ( user -- ) + [ [ logged-in-user set ] [ save-user-after ] bi ] when* ; M: realm call-responder* ( path responder -- response ) dup realm set - dup init-user + dup logged-in-username dup [ users get-user ] when init-user call-next-method ; : encode-password ( string salt -- bytes ) diff --git a/extra/furnace/auth/basic/basic.factor b/extra/furnace/auth/basic/basic.factor index ae9cbb82c1..e478f70dcc 100755 --- a/extra/furnace/auth/basic/basic.factor +++ b/extra/furnace/auth/basic/basic.factor @@ -1,17 +1,18 @@ ! Copyright (c) 2007 Chris Double. ! See http://factorcode.org/license.txt for BSD license. -USING: accessors kernel splitting base64 namespaces +USING: accessors kernel splitting base64 namespaces strings http http.server.responses furnace.auth ; IN: furnace.auth.basic TUPLE: basic-auth-realm < realm ; -C: <basic-auth-realm> basic-auth-realm +: <basic-auth-realm> ( responder name -- realm ) + basic-auth-realm new-realm ; : parse-basic-auth ( header -- username/f password/f ) dup [ " " split1 swap "Basic" = [ - base64> ":" split1 + base64> >string ":" split1 ] [ drop f f ] if ] [ drop f f ] if ; @@ -23,5 +24,6 @@ M: basic-auth-realm login-required* ( realm -- response ) name>> <401> ; M: basic-auth-realm logged-in-username ( realm -- uid ) + drop request get "authorization" header parse-basic-auth - dup [ over realm get check-login swap and ] [ 2drop f ] if ; + dup [ over check-login swap and ] [ 2drop f ] if ; diff --git a/extra/furnace/auth/features/registration/registration.factor b/extra/furnace/auth/features/registration/registration.factor index 3deead4869..2bc7688b10 100644 --- a/extra/furnace/auth/features/registration/registration.factor +++ b/extra/furnace/auth/features/registration/registration.factor @@ -7,7 +7,7 @@ IN: furnace.auth.features.registration : <register-action> ( -- action ) <page-action> - { realm "register" } >>template + { realm "features/registration/register" } >>template [ { diff --git a/extra/furnace/auth/login/login.factor b/extra/furnace/auth/login/login.factor index 6a59c01c63..e2b208de3a 100755 --- a/extra/furnace/auth/login/login.factor +++ b/extra/furnace/auth/login/login.factor @@ -1,28 +1,57 @@ ! Copyright (c) 2008 Slava Pestov ! See http://factorcode.org/license.txt for BSD license. -USING: kernel accessors namespaces validators urls -html.forms -http.server.dispatchers +USING: kernel accessors namespaces sequences math.parser +calendar validators urls html.forms +http http.server http.server.dispatchers +furnace furnace.auth furnace.flash furnace.asides furnace.actions furnace.sessions -furnace.utilities ; +furnace.utilities +furnace.auth.login.permits ; IN: furnace.auth.login -TUPLE: login-realm < realm ; +SYMBOL: permit-id + +: permit-id-key ( realm -- string ) + [ >hex 2 CHAR: 0 pad-left ] { } map-as concat + "__p_" prepend ; + +: client-permit-id ( realm -- id/f ) + permit-id-key client-state dup [ string>number ] when ; + +TUPLE: login-realm < realm timeout domain ; + +M: login-realm call-responder* + [ name>> client-permit-id permit-id set ] + [ call-next-method ] + bi ; M: login-realm logged-in-username - drop session get uid>> ; + drop permit-id get dup [ get-permit-uid ] when ; -: set-uid ( username -- ) - session get [ (>>uid) ] [ (session-changed) ] bi ; +M: login-realm modify-form ( responder -- ) + drop permit-id get realm get name>> permit-id-key hidden-form-field ; + +: <permit-cookie> ( -- cookie ) + permit-id get realm get name>> permit-id-key <cookie> + "$login-realm" resolve-base-path >>path + realm get timeout>> from-now >>expires + realm get domain>> >>domain ; + +: put-permit-cookie ( response -- response' ) + <permit-cookie> put-cookie ; : successful-login ( user -- response ) - username>> set-uid URL" $realm" end-aside ; + [ username>> make-permit permit-id set ] [ init-user ] bi + URL" $realm" end-aside + put-permit-cookie ; -: logout ( -- ) f set-uid URL" $realm" end-aside ; +: logout ( -- ) + permit-id get [ delete-permit ] when* + URL" $realm" end-aside ; SYMBOL: description SYMBOL: capabilities @@ -56,7 +85,9 @@ SYMBOL: capabilities : <logout-action> ( -- action ) <action> - [ logout ] >>submit ; + [ logout ] >>submit + <protected> + "logout" >>description ; M: login-realm login-required* drop @@ -68,4 +99,5 @@ M: login-realm login-required* : <login-realm> ( responder name -- auth ) login-realm new-realm <login-action> <auth-boilerplate> "login" add-responder - <logout-action> "logout" add-responder ; + <logout-action> "logout" add-responder + 20 minutes >>timeout ; diff --git a/extra/furnace/auth/login/permits/permits.factor b/extra/furnace/auth/login/permits/permits.factor new file mode 100644 index 0000000000..49cf98e0e3 --- /dev/null +++ b/extra/furnace/auth/login/permits/permits.factor @@ -0,0 +1,30 @@ +USING: accessors namespaces combinators.lib kernel +db.tuples db.types +furnace.auth furnace.sessions furnace.cache ; +IN: furnace.auth.login.permits + +TUPLE: permit < server-state session uid ; + +permit "PERMITS" { + { "session" "SESSION" BIG-INTEGER +not-null+ } + { "uid" "UID" { VARCHAR 255 } +not-null+ } +} define-persistent + +: touch-permit ( permit -- ) + realm get touch-state ; + +: get-permit-uid ( id -- uid ) + permit get-state { + [ ] + [ session>> session get id>> = ] + [ [ touch-permit ] [ uid>> ] bi ] + } 1&& ; + +: make-permit ( uid -- id ) + permit new + swap >>uid + session get id>> >>session + [ touch-permit ] [ insert-tuple ] [ id>> ] tri ; + +: delete-permit ( id -- ) + permit new-server-state delete-tuples ; diff --git a/extra/furnace/auth/providers/assoc/assoc-tests.factor b/extra/furnace/auth/providers/assoc/assoc-tests.factor index 8f9eeaa7a5..8fe1dd4dd4 100755 --- a/extra/furnace/auth/providers/assoc/assoc-tests.factor +++ b/extra/furnace/auth/providers/assoc/assoc-tests.factor @@ -1,11 +1,11 @@ IN: furnace.auth.providers.assoc.tests -USING: furnace.actions furnace.auth.providers +USING: furnace.actions furnace.auth furnace.auth.providers furnace.auth.providers.assoc furnace.auth.login tools.test namespaces accessors kernel ; -<action> <login> +<action> "Test" <login-realm> <users-in-memory> >>users -login set +realm set [ t ] [ "slava" <user> diff --git a/extra/furnace/furnace.factor b/extra/furnace/furnace.factor index 6b47bc681b..521f8a3bc1 100644 --- a/extra/furnace/furnace.factor +++ b/extra/furnace/furnace.factor @@ -96,6 +96,19 @@ M: object modify-form drop ; request get url>> [ [ protocol>> ] [ host>> ] [ port>> ] tri 3array ] bi@ = ; +: cookie-client-state ( key request -- value/f ) + swap get-cookie dup [ value>> ] when ; + +: post-client-state ( key request -- value/f ) + request-params at ; + +: client-state ( key -- value/f ) + request get dup method>> { + { "GET" [ cookie-client-state ] } + { "HEAD" [ cookie-client-state ] } + { "POST" [ post-client-state ] } + } case ; + SYMBOL: exit-continuation : exit-with ( value -- ) diff --git a/extra/furnace/sessions/sessions.factor b/extra/furnace/sessions/sessions.factor index fe8053fc9c..bb0a844269 100755 --- a/extra/furnace/sessions/sessions.factor +++ b/extra/furnace/sessions/sessions.factor @@ -98,20 +98,6 @@ M: session-saver dispose : session-id-key "__s" ; -: cookie-session-id ( request -- id/f ) - session-id-key get-cookie - dup [ value>> string>number ] when ; - -: post-session-id ( request -- id/f ) - session-id-key swap request-params at string>number ; - -: request-session-id ( -- id/f ) - request get dup method>> { - { "GET" [ cookie-session-id ] } - { "HEAD" [ cookie-session-id ] } - { "POST" [ post-session-id ] } - } case ; - : verify-session ( session -- session ) sessions get verify?>> [ dup [ @@ -123,16 +109,18 @@ M: session-saver dispose ] when ; : request-session ( -- session/f ) - request-session-id get-session verify-session ; + session-id-key + client-state dup [ string>number ] when + get-session verify-session ; -: <session-cookie> ( id -- cookie ) - session-id-key <cookie> +: <session-cookie> ( -- cookie ) + session get id>> session-id-key <cookie> "$sessions" resolve-base-path >>path sessions get timeout>> from-now >>expires sessions get domain>> >>domain ; : put-session-cookie ( response -- response' ) - session get id>> number>string <session-cookie> put-cookie ; + <session-cookie> put-cookie ; M: sessions modify-form ( responder -- ) drop session get id>> session-id-key hidden-form-field ; diff --git a/extra/http/http.factor b/extra/http/http.factor index d2a0b0f922..025e2c8441 100755 --- a/extra/http/http.factor +++ b/extra/http/http.factor @@ -99,23 +99,29 @@ TUPLE: cookie name value path domain expires max-age http-only ; drop ] { } make ; +: check-cookie-string ( string -- string' ) + dup "=;'\"" intersect empty? + [ "Bad cookie name or value" throw ] unless ; + : (unparse-cookie) ( key value -- ) { { f [ drop ] } - { t [ , ] } + { t [ check-cookie-string , ] } [ { { [ dup timestamp? ] [ timestamp>cookie-string ] } { [ dup duration? ] [ dt>seconds number>string ] } + { [ dup real? ] [ number>string ] } [ ] } cond - "=" swap 3append , + check-cookie-string "=" swap check-cookie-string 3append , ] } case ; : unparse-cookie ( cookie -- strings ) [ - dup name>> >lower over value>> (unparse-cookie) + dup name>> check-cookie-string >lower + over value>> (unparse-cookie) "path" over path>> (unparse-cookie) "domain" over domain>> (unparse-cookie) "expires" over expires>> (unparse-cookie) diff --git a/extra/http/server/static/static.factor b/extra/http/server/static/static.factor index 9d76c82e4a..83fcf6f4a9 100755 --- a/extra/http/server/static/static.factor +++ b/extra/http/server/static/static.factor @@ -82,7 +82,7 @@ TUPLE: file-responder root hook special allow-listings ; "index.html" append-path dup exists? [ drop f ] unless ; : serve-directory ( filename -- response ) - request get path>> "/" tail? [ + request get url>> path>> "/" tail? [ dup find-index [ serve-file ] [ list-directory ] ?if ] [ diff --git a/extra/webapps/wiki/wiki.factor b/extra/webapps/wiki/wiki.factor index 34bad6db18..13c445b0a8 100644 --- a/extra/webapps/wiki/wiki.factor +++ b/extra/webapps/wiki/wiki.factor @@ -284,6 +284,11 @@ M: revision feed-entry-url id>> revision-url ; <boilerplate> { wiki "page-common" } >>template ; +: init-sidebar ( -- ) + "Sidebar" latest-revision [ + "sidebar" [ from-object ] nest-form + ] when* ; + : <wiki> ( -- dispatcher ) wiki new-dispatcher <main-article-action> <article-boilerplate> "" add-responder @@ -301,5 +306,5 @@ M: revision feed-entry-url id>> revision-url ; <list-changes-feed-action> "changes.atom" add-responder <delete-action> "delete" add-responder <boilerplate> - [ "sidebar" [ "Sidebar" latest-revision from-object ] nest-form ] >>init + [ init-sidebar ] >>init { wiki "wiki-common" } >>template ;