Mercurial > openid
comparison src/ur/openidUser.ur @ 16:9851bc87b0d7
Beginning of OpenidUser
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Thu, 06 Jan 2011 12:48:13 -0500 |
parents | |
children | df2eb629f21a |
comparison
equal
deleted
inserted
replaced
15:35bc4da563dd | 16:9851bc87b0d7 |
---|---|
1 functor Make(M: sig | |
2 con cols :: {Type} | |
3 constraint [Id] ~ cols | |
4 | |
5 val sessionLifetime : int | |
6 val afterLogout : url | |
7 val secureCookies : bool | |
8 val association : Openid.association_mode | |
9 val realm : option string | |
10 end) = struct | |
11 | |
12 type user = string | |
13 val show_user = _ | |
14 val inj_user = _ | |
15 | |
16 table user : ([Id = user] ++ M.cols) | |
17 PRIMARY KEY Id | |
18 | |
19 table identity : {User : user, Identifier : string} | |
20 PRIMARY KEY (User, Identifier) | |
21 | |
22 sequence sessionIds | |
23 | |
24 table session : {Id : int, Key : int, Identifier : option string, Expires : time} | |
25 PRIMARY KEY Id | |
26 | |
27 cookie signingUp : {Session : int, Key : int} | |
28 cookie login : {User : user, Session : int, Key : int} | |
29 | |
30 val current = | |
31 login <- getCookie login; | |
32 case login of | |
33 None => return None | |
34 | Some login => | |
35 ident <- oneOrNoRowsE1 (SELECT (session.Identifier) | |
36 FROM session | |
37 WHERE session.Id = {[login.Session]} | |
38 AND session.Key = {[login.Key]}); | |
39 case ident of | |
40 None => error <xml>Invalid or expired session</xml> | |
41 | Some None => return None | |
42 | Some (Some ident) => | |
43 valid <- oneRowE1 (SELECT COUNT( * ) > 0 | |
44 FROM identity | |
45 WHERE identity.User = {[login.User]} | |
46 AND identity.Identifier = {[ident]}); | |
47 if valid then | |
48 return (Some login.User) | |
49 else | |
50 error <xml>Session not authorized to act as user</xml> | |
51 | |
52 fun main wrap = | |
53 let | |
54 fun logout () = | |
55 clearCookie login; | |
56 redirect M.afterLogout | |
57 | |
58 fun opCallback after ses res = | |
59 case res of | |
60 Openid.Canceled => error <xml>You canceled the login process.</xml> | |
61 | Openid.Failure s => error <xml>Login failed: {[s]}</xml> | |
62 | Openid.AuthenticatedAs ident => | |
63 signup <- getCookie signingUp; | |
64 case signup of | |
65 Some signup => | |
66 if signup.Session <> ses then | |
67 error <xml>Session has changed suspiciously</xml> | |
68 else | |
69 invalid <- oneRowE1 (SELECT COUNT( * ) = 0 | |
70 FROM session | |
71 WHERE session.Id = {[signup.Session]} | |
72 AND session.Key = {[signup.Key]}); | |
73 if invalid then | |
74 error <xml>Invalid or expired session</xml> | |
75 else | |
76 return <xml>I now believe that you are {[ident]}.</xml> | |
77 | None => | |
78 login <- getCookie login; | |
79 case login of | |
80 None => error <xml>Missing session cookie</xml> | |
81 | Some login => | |
82 if login.Session <> ses then | |
83 error <xml>Session has changed suspiciously</xml> | |
84 else | |
85 invalid <- oneRowE1 (SELECT COUNT( * ) = 0 | |
86 FROM session | |
87 WHERE session.Id = {[login.Session]} | |
88 AND session.Key = {[login.Key]}); | |
89 if invalid then | |
90 error <xml>Invalid or expired session</xml> | |
91 else | |
92 dml (UPDATE session | |
93 SET Identifier = {[Some ident]} | |
94 WHERE Key = {[login.Key]}); | |
95 redirect (bless after) | |
96 | |
97 fun newSession () = | |
98 ses <- nextval sessionIds; | |
99 now <- now; | |
100 key <- rand; | |
101 dml (INSERT INTO session (Id, Key, Identifier, Expires) | |
102 VALUES ({[ses]}, {[key]}, NULL, {[addSeconds now M.sessionLifetime]})); | |
103 return {Session = ses, Key = key} | |
104 | |
105 fun logon r = | |
106 ident <- oneOrNoRowsE1 (SELECT (identity.Identifier) | |
107 FROM identity | |
108 WHERE identity.User = {[r.User]} | |
109 LIMIT 1); | |
110 case ident of | |
111 None => error <xml>Username not found</xml> | |
112 | Some ident => | |
113 ses <- newSession (); | |
114 setCookie login {Value = r ++ ses, | |
115 Expires = None, | |
116 Secure = M.secureCookies}; | |
117 after <- currentUrl; | |
118 after <- return (show after); | |
119 ses <- return ses.Session; | |
120 msg <- Openid.authenticate (opCallback after ses) | |
121 {Association = M.association, | |
122 Realm = M.realm, | |
123 Identifier = ident}; | |
124 error <xml>Login with your identity provider failed: {[msg]}</xml> | |
125 | |
126 fun doSignup after r = | |
127 ses <- newSession (); | |
128 setCookie signingUp {Value = ses, | |
129 Expires = None, | |
130 Secure = M.secureCookies}; | |
131 ses <- return ses.Session; | |
132 msg <- Openid.authenticate (opCallback after ses) | |
133 {Association = M.association, | |
134 Realm = M.realm, | |
135 Identifier = r.Identifier}; | |
136 error <xml>Login with your identity provider failed: {[msg]}</xml> | |
137 | |
138 fun signup () = | |
139 after <- currentUrl; | |
140 wrap "Account Signup" <xml> | |
141 <form> | |
142 OpenID Identifier: <textbox{#Identifier}/><br/> | |
143 <submit value="Sign Up" action={doSignup (show after)}/> | |
144 </form> | |
145 </xml> | |
146 in | |
147 cur <- current; | |
148 case cur of | |
149 Some cur => return <xml>Logged in as {[cur]}. <a link={logout ()}>[Log out]</a></xml> | |
150 | None => return <xml> | |
151 <form><textbox{#User}/> <submit value="Log In" action={logon}/></form> | |
152 <a link={signup ()}>Sign up</a> | |
153 </xml> | |
154 end | |
155 | |
156 task periodic 60 = fn () => dml (DELETE FROM session | |
157 WHERE Expires >= CURRENT_TIMESTAMP) | |
158 | |
159 end |