comparison src/ur/openidUser.ur @ 17:df2eb629f21a

Successfully created an account
author Adam Chlipala <adam@chlipala.net>
date Thu, 06 Jan 2011 14:42:37 -0500
parents 9851bc87b0d7
children dd8eb53da51b
comparison
equal deleted inserted replaced
16:9851bc87b0d7 17:df2eb629f21a
1 functor Make(M: sig 1 functor Make(M: sig
2 con cols :: {Type} 2 con cols :: {Type}
3 constraint [Id] ~ cols 3 constraint [Id] ~ cols
4 val folder : folder cols
5 val inj : $(map sql_injectable cols)
6
7 type creationState
8 type creationData
9 val creationState : transaction creationState
10 val render : creationState -> xtable
11 val tabulate : creationState -> signal creationData
12 val choose : sql_table ([Id = string] ++ cols) [Pkey = [Id]] -> creationData -> transaction $cols
4 13
5 val sessionLifetime : int 14 val sessionLifetime : int
6 val afterLogout : url 15 val afterLogout : url
7 val secureCookies : bool 16 val secureCookies : bool
8 val association : Openid.association_mode 17 val association : Openid.association_mode
9 val realm : option string 18 val realm : option string
19 val formClass : css_class
10 end) = struct 20 end) = struct
11 21
12 type user = string 22 type user = string
13 val show_user = _ 23 val show_user = _
14 val inj_user = _ 24 val inj_user = _
24 table session : {Id : int, Key : int, Identifier : option string, Expires : time} 34 table session : {Id : int, Key : int, Identifier : option string, Expires : time}
25 PRIMARY KEY Id 35 PRIMARY KEY Id
26 36
27 cookie signingUp : {Session : int, Key : int} 37 cookie signingUp : {Session : int, Key : int}
28 cookie login : {User : user, Session : int, Key : int} 38 cookie login : {User : user, Session : int, Key : int}
39
40 val currentUrl =
41 b <- currentUrlHasPost;
42 if b then
43 return M.afterLogout
44 else
45 currentUrl
29 46
30 val current = 47 val current =
31 login <- getCookie login; 48 login <- getCookie login;
32 case login of 49 case login of
33 None => return None 50 None => return None
47 if valid then 64 if valid then
48 return (Some login.User) 65 return (Some login.User)
49 else 66 else
50 error <xml>Session not authorized to act as user</xml> 67 error <xml>Session not authorized to act as user</xml>
51 68
69 fun validUser s = String.length s > 0 && String.length s < 20
70 && String.all Char.isAlnum s
71
52 fun main wrap = 72 fun main wrap =
53 let 73 let
54 fun logout () = 74 fun logout () =
55 clearCookie login; 75 clearCookie login;
56 redirect M.afterLogout 76 redirect M.afterLogout
77
78 fun signupDetails after =
79 let
80 fun finishSignup uid data =
81 if not (validUser uid) then
82 return (Some "That username is not valid. It must be between 1 and 19 characters long, containing only letters and numbers.")
83 else
84 used <- oneRowE1 (SELECT COUNT( * ) > 0
85 FROM user
86 WHERE user.Id = {[uid]});
87 if used then
88 return (Some "That username is taken. Please choose another.")
89 else
90 ses <- getCookie signingUp;
91 case ses of
92 None => return (Some "Missing session cookie")
93 | Some ses =>
94 ident <- oneOrNoRowsE1 (SELECT (session.Identifier)
95 FROM session
96 WHERE session.Id = {[ses.Session]}
97 AND session.Key = {[ses.Key]});
98 case ident of
99 None => return (Some "Invalid session data")
100 | Some None => return (Some "Session has no associated identifier")
101 | Some (Some ident) =>
102 clearCookie signingUp;
103 setCookie login {Value = {User = uid} ++ ses,
104 Expires = None,
105 Secure = M.secureCookies};
106
107 cols <- M.choose user data;
108 dml (insert user ({Id = (SQL {[uid]})} ++ @Sql.sqexps M.folder M.inj cols));
109 dml (INSERT INTO identity (User, Identifier)
110 VALUES ({[uid]}, {[ident]}));
111 redirect (bless after)
112 in
113 uid <- source "";
114 cs <- M.creationState;
115
116 wrap "Your User Details" <xml>
117 <table class={M.formClass}>
118 <tr> <th class={M.formClass}>Username:</th> <td><ctextbox source={uid}/></td> </tr>
119 {M.render cs}
120 <tr> <td><button value="Create Account" onclick={uid <- get uid;
121 data <- Basis.current (M.tabulate cs);
122 res <- rpc (finishSignup uid data);
123 case res of
124 None => redirect (bless after)
125 | Some msg => alert msg}/></td> </tr>
126 </table>
127 </xml>
128 end
57 129
58 fun opCallback after ses res = 130 fun opCallback after ses res =
59 case res of 131 case res of
60 Openid.Canceled => error <xml>You canceled the login process.</xml> 132 Openid.Canceled => error <xml>You canceled the login process.</xml>
61 | Openid.Failure s => error <xml>Login failed: {[s]}</xml> 133 | Openid.Failure s => error <xml>Login failed: {[s]}</xml>
71 WHERE session.Id = {[signup.Session]} 143 WHERE session.Id = {[signup.Session]}
72 AND session.Key = {[signup.Key]}); 144 AND session.Key = {[signup.Key]});
73 if invalid then 145 if invalid then
74 error <xml>Invalid or expired session</xml> 146 error <xml>Invalid or expired session</xml>
75 else 147 else
76 return <xml>I now believe that you are {[ident]}.</xml> 148 dml (UPDATE session
149 SET Identifier = {[Some ident]}
150 WHERE Id = {[signup.Session]});
151 signupDetails after
77 | None => 152 | None =>
78 login <- getCookie login; 153 login <- getCookie login;
79 case login of 154 case login of
80 None => error <xml>Missing session cookie</xml> 155 None => error <xml>Missing session cookie</xml>
81 | Some login => 156 | Some login =>
89 if invalid then 164 if invalid then
90 error <xml>Invalid or expired session</xml> 165 error <xml>Invalid or expired session</xml>
91 else 166 else
92 dml (UPDATE session 167 dml (UPDATE session
93 SET Identifier = {[Some ident]} 168 SET Identifier = {[Some ident]}
94 WHERE Key = {[login.Key]}); 169 WHERE Id = {[login.Session]});
95 redirect (bless after) 170 redirect (bless after)
96 171
97 fun newSession () = 172 fun newSession () =
98 ses <- nextval sessionIds; 173 ses <- nextval sessionIds;
99 now <- now; 174 now <- now;
100 key <- rand; 175 key <- rand;
101 dml (INSERT INTO session (Id, Key, Identifier, Expires) 176 dml (INSERT INTO session (Id, Key, Identifier, Expires)
102 VALUES ({[ses]}, {[key]}, NULL, {[addSeconds now M.sessionLifetime]})); 177 VALUES ({[ses]}, {[key]}, NULL, {[addSeconds now M.sessionLifetime]}));
103 return {Session = ses, Key = key} 178 return {Session = ses, Key = key}
104 179
105 fun logon r = 180 fun logon after r =
106 ident <- oneOrNoRowsE1 (SELECT (identity.Identifier) 181 ident <- oneOrNoRowsE1 (SELECT (identity.Identifier)
107 FROM identity 182 FROM identity
108 WHERE identity.User = {[r.User]} 183 WHERE identity.User = {[r.User]}
109 LIMIT 1); 184 LIMIT 1);
110 case ident of 185 case ident of
112 | Some ident => 187 | Some ident =>
113 ses <- newSession (); 188 ses <- newSession ();
114 setCookie login {Value = r ++ ses, 189 setCookie login {Value = r ++ ses,
115 Expires = None, 190 Expires = None,
116 Secure = M.secureCookies}; 191 Secure = M.secureCookies};
117 after <- currentUrl;
118 after <- return (show after);
119 ses <- return ses.Session; 192 ses <- return ses.Session;
120 msg <- Openid.authenticate (opCallback after ses) 193 msg <- Openid.authenticate (opCallback after ses)
121 {Association = M.association, 194 {Association = M.association,
122 Realm = M.realm, 195 Realm = M.realm,
123 Identifier = ident}; 196 Identifier = ident};
143 <submit value="Sign Up" action={doSignup (show after)}/> 216 <submit value="Sign Up" action={doSignup (show after)}/>
144 </form> 217 </form>
145 </xml> 218 </xml>
146 in 219 in
147 cur <- current; 220 cur <- current;
221 here <- currentUrl;
148 case cur of 222 case cur of
149 Some cur => return <xml>Logged in as {[cur]}. <a link={logout ()}>[Log out]</a></xml> 223 Some cur => return <xml>Logged in as {[cur]}. <a link={logout ()}>[Log out]</a></xml>
150 | None => return <xml> 224 | None => return <xml>
151 <form><textbox{#User}/> <submit value="Log In" action={logon}/></form> 225 <form><textbox{#User}/> <submit value="Log In" action={logon (show here)}/></form>
152 <a link={signup ()}>Sign up</a> 226 <a link={signup ()}>Sign up</a>
153 </xml> 227 </xml>
154 end 228 end
155 229
156 task periodic 60 = fn () => dml (DELETE FROM session 230 task periodic 60 = fn () => dml (DELETE FROM session