comparison src/ur/openidUser.ur @ 22:70ab0230649b

Fix calculation of URL to return to after sign-up
author Adam Chlipala <adam@chlipala.net>
date Sat, 15 Jan 2011 15:24:42 -0500
parents 354dae3008de
children e5df3d3554d3
comparison
equal deleted inserted replaced
21:354dae3008de 22:70ab0230649b
33 sequence sessionIds 33 sequence sessionIds
34 34
35 table session : {Id : int, Key : int, Identifier : option string, Expires : time} 35 table session : {Id : int, Key : int, Identifier : option string, Expires : time}
36 PRIMARY KEY Id 36 PRIMARY KEY Id
37 37
38 cookie signingUp : {Session : int, Key : int} 38 datatype authMode =
39 cookie login : {User : user, Session : int, Key : int} 39 SigningUp of {Session : int, Key : int}
40 | LoggedIn of {User : user, Session : int, Key : int}
41
42 cookie auth : authMode
40 43
41 val currentUrl = 44 val currentUrl =
42 b <- currentUrlHasPost; 45 b <- currentUrlHasPost;
43 if b then 46 if b then
44 return M.afterLogout 47 return M.afterLogout
45 else 48 else
46 currentUrl 49 currentUrl
47 50
48 val current = 51 val current =
49 login <- getCookie login; 52 login <- getCookie auth;
50 case login of 53 case login of
51 None => return None 54 Some (LoggedIn login) =>
52 | Some login => 55 (ident <- oneOrNoRowsE1 (SELECT (session.Identifier)
53 ident <- oneOrNoRowsE1 (SELECT (session.Identifier) 56 FROM session
54 FROM session 57 WHERE session.Id = {[login.Session]}
55 WHERE session.Id = {[login.Session]} 58 AND session.Key = {[login.Key]});
56 AND session.Key = {[login.Key]}); 59 case ident of
57 case ident of 60 None => return None
58 None => return None 61 | Some None => return None
59 | Some None => return None 62 | Some (Some ident) =>
60 | Some (Some ident) => 63 valid <- oneRowE1 (SELECT COUNT( * ) > 0
61 valid <- oneRowE1 (SELECT COUNT( * ) > 0 64 FROM identity
62 FROM identity 65 WHERE identity.User = {[login.User]}
63 WHERE identity.User = {[login.User]} 66 AND identity.Identifier = {[ident]});
64 AND identity.Identifier = {[ident]}); 67 if valid then
65 if valid then 68 return (Some login.User)
66 return (Some login.User) 69 else
67 else 70 error <xml>Session not authorized to act as user</xml>)
68 error <xml>Session not authorized to act as user</xml> 71 | _ => return None
69 72
70 fun validUser s = String.length s > 0 && String.length s < 20 73 fun validUser s = String.length s > 0 && String.length s < 20
71 && String.all Char.isAlnum s 74 && String.all Char.isAlnum s
72 75
73 fun main wrap = 76 fun main wrap =
74 let 77 let
75 fun logout () = 78 fun logout () =
76 clearCookie login; 79 clearCookie auth;
77 redirect M.afterLogout 80 redirect M.afterLogout
78 81
79 fun signupDetails after = 82 fun signupDetails after =
80 let 83 let
81 fun finishSignup uid data = 84 fun finishSignup uid data =
86 FROM user 89 FROM user
87 WHERE user.Id = {[uid]}); 90 WHERE user.Id = {[uid]});
88 if used then 91 if used then
89 return (Some "That username is taken. Please choose another.") 92 return (Some "That username is taken. Please choose another.")
90 else 93 else
91 ses <- getCookie signingUp; 94 ses <- getCookie auth;
92 case ses of 95 case ses of
93 None => return (Some "Missing session cookie") 96 None => return (Some "Missing session cookie")
94 | Some ses => 97 | Some (LoggedIn _) => return (Some "Session cookie is for already logged-in user")
98 | Some (SigningUp ses) =>
95 ident <- oneOrNoRowsE1 (SELECT (session.Identifier) 99 ident <- oneOrNoRowsE1 (SELECT (session.Identifier)
96 FROM session 100 FROM session
97 WHERE session.Id = {[ses.Session]} 101 WHERE session.Id = {[ses.Session]}
98 AND session.Key = {[ses.Key]}); 102 AND session.Key = {[ses.Key]});
99 case ident of 103 case ident of
100 None => return (Some "Invalid session data") 104 None => return (Some "Invalid session data")
101 | Some None => return (Some "Session has no associated identifier") 105 | Some None => return (Some "Session has no associated identifier")
102 | Some (Some ident) => 106 | Some (Some ident) =>
103 clearCookie signingUp; 107 setCookie auth {Value = LoggedIn ({User = uid} ++ ses),
104 setCookie login {Value = {User = uid} ++ ses, 108 Expires = None,
105 Expires = None, 109 Secure = M.secureCookies};
106 Secure = M.secureCookies};
107 110
108 cols <- M.choose user data; 111 cols <- M.choose user data;
109 dml (insert user ({Id = (SQL {[uid]})} ++ @Sql.sqexps M.folder M.inj cols)); 112 dml (insert user ({Id = (SQL {[uid]})} ++ @Sql.sqexps M.folder M.inj cols));
110 dml (INSERT INTO identity (User, Identifier) 113 dml (INSERT INTO identity (User, Identifier)
111 VALUES ({[uid]}, {[ident]})); 114 VALUES ({[uid]}, {[ident]}));
136 fun opCallback after ses res = 139 fun opCallback after ses res =
137 case res of 140 case res of
138 Openid.Canceled => error <xml>You canceled the login process.</xml> 141 Openid.Canceled => error <xml>You canceled the login process.</xml>
139 | Openid.Failure s => error <xml>Login failed: {[s]}</xml> 142 | Openid.Failure s => error <xml>Login failed: {[s]}</xml>
140 | Openid.AuthenticatedAs ident => 143 | Openid.AuthenticatedAs ident =>
141 signup <- getCookie signingUp; 144 av <- getCookie auth;
142 case signup of 145 case av of
143 Some signup => 146 Some (SigningUp signup) =>
144 if signup.Session <> ses then 147 if signup.Session <> ses then
145 error <xml>Session has changed suspiciously</xml> 148 error <xml>Session has changed suspiciously</xml>
146 else 149 else
147 invalid <- oneRowE1 (SELECT COUNT( * ) = 0 150 invalid <- oneRowE1 (SELECT COUNT( * ) = 0
148 FROM session 151 FROM session
153 else 156 else
154 dml (UPDATE session 157 dml (UPDATE session
155 SET Identifier = {[Some ident]} 158 SET Identifier = {[Some ident]}
156 WHERE Id = {[signup.Session]}); 159 WHERE Id = {[signup.Session]});
157 signupDetails after 160 signupDetails after
158 | None => 161 | Some (LoggedIn login) =>
159 login <- getCookie login; 162 if login.Session <> ses then
160 case login of 163 error <xml>Session has changed suspiciously</xml>
161 None => error <xml>Missing session cookie</xml> 164 else
162 | Some login => 165 invalid <- oneRowE1 (SELECT COUNT( * ) = 0
163 if login.Session <> ses then 166 FROM session
164 error <xml>Session has changed suspiciously</xml> 167 WHERE session.Id = {[login.Session]}
168 AND session.Key = {[login.Key]});
169 if invalid then
170 error <xml>Invalid or expired session</xml>
165 else 171 else
166 invalid <- oneRowE1 (SELECT COUNT( * ) = 0 172 dml (UPDATE session
167 FROM session 173 SET Identifier = {[Some ident]}
168 WHERE session.Id = {[login.Session]} 174 WHERE Id = {[login.Session]});
169 AND session.Key = {[login.Key]}); 175 redirect (bless after)
170 if invalid then 176 | None => error <xml>Missing session cookie</xml>
171 error <xml>Invalid or expired session</xml>
172 else
173 dml (UPDATE session
174 SET Identifier = {[Some ident]}
175 WHERE Id = {[login.Session]});
176 redirect (bless after)
177 177
178 fun newSession () = 178 fun newSession () =
179 ses <- nextval sessionIds; 179 ses <- nextval sessionIds;
180 now <- now; 180 now <- now;
181 key <- rand; 181 key <- rand;
190 LIMIT 1); 190 LIMIT 1);
191 case ident of 191 case ident of
192 None => error <xml>Username not found</xml> 192 None => error <xml>Username not found</xml>
193 | Some ident => 193 | Some ident =>
194 ses <- newSession (); 194 ses <- newSession ();
195 setCookie login {Value = r ++ ses, 195 setCookie auth {Value = LoggedIn (r ++ ses),
196 Expires = None, 196 Expires = None,
197 Secure = M.secureCookies}; 197 Secure = M.secureCookies};
198 ses <- return ses.Session; 198 ses <- return ses.Session;
199 msg <- Openid.authenticate (opCallback after ses) 199 msg <- Openid.authenticate (opCallback after ses)
200 {Association = M.association, 200 {Association = M.association,
201 Realm = M.realm, 201 Realm = M.realm,
202 Identifier = ident}; 202 Identifier = ident};
203 error <xml>Login with your identity provider failed: {[msg]}</xml> 203 error <xml>Login with your identity provider failed: {[msg]}</xml>
204 204
205 fun doSignup after r = 205 fun doSignup after r =
206 ses <- newSession (); 206 ses <- newSession ();
207 setCookie signingUp {Value = ses, 207 setCookie auth {Value = SigningUp ses,
208 Expires = None, 208 Expires = None,
209 Secure = M.secureCookies}; 209 Secure = M.secureCookies};
210 ses <- return ses.Session; 210 ses <- return ses.Session;
211 msg <- Openid.authenticate (opCallback after ses) 211 msg <- Openid.authenticate (opCallback after ses)
212 {Association = M.association, 212 {Association = M.association,
213 Realm = M.realm, 213 Realm = M.realm,
214 Identifier = r.Identifier}; 214 Identifier = r.Identifier};
215 error <xml>Login with your identity provider failed: {[msg]}</xml> 215 error <xml>Login with your identity provider failed: {[msg]}</xml>
216 216
217 fun signup () = 217 fun signup after =
218 after <- currentUrl;
219 wrap "Account Signup" <xml> 218 wrap "Account Signup" <xml>
220 <form> 219 <form>
221 OpenID Identifier: <textbox{#Identifier}/><br/> 220 OpenID Identifier: <textbox{#Identifier}/><br/>
222 <submit value="Sign Up" action={doSignup (show after)}/> 221 <submit value="Sign Up" action={doSignup after}/>
223 </form> 222 </form>
224 </xml> 223 </xml>
225 in 224 in
226 cur <- current; 225 cur <- current;
227 here <- currentUrl; 226 here <- currentUrl;
228 case cur of 227 case cur of
229 Some cur => return <xml>Logged in as {[cur]}. <a link={logout ()}>[Log out]</a></xml> 228 Some cur => return <xml>Logged in as {[cur]}. <a link={logout ()}>[Log out]</a></xml>
230 | None => return <xml> 229 | None => return <xml>
231 <form><textbox{#User}/> <submit value="Log In" action={logon (show here)}/></form> 230 <form><textbox{#User}/> <submit value="Log In" action={logon (show here)}/></form>
232 <a link={signup ()}>Sign up</a> 231 <a link={signup (show here)}>Sign up</a>
233 </xml> 232 </xml>
234 end 233 end
235 234
236 task periodic 60 = fn () => dml (DELETE FROM session 235 task periodic 60 = fn () => dml (DELETE FROM session
237 WHERE Expires < CURRENT_TIMESTAMP) 236 WHERE Expires < CURRENT_TIMESTAMP)