1/* Part of SWISH 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@cs.vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (C): 2017, VU University Amsterdam 7 CWI Amsterdam 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(swish_login, 37 [ login_button//1, % +Options 38 login_continue_button//0, 39 reply_logged_in/1, % +Options 40 reply_logged_in_page/1, % +Options 41 reply_logged_out/1, % +Options 42 reply_logged_out_page/1, % +Options 43 current_user_info/2 % +Request, -UserInfo 44 ]). 45:- use_module(library(http/http_dispatch)). 46:- use_module(library(http/http_parameters)). 47:- use_module(library(http/http_json)). 48:- use_module(library(http/html_write)). 49:- use_module(library(http/js_write)). 50:- use_module(library(option)). 51:- use_module(library(apply)). 52:- use_module(library(broadcast)). 53 54:- use_module('../config', []). 55 56:- multifile 57 swish_config:login/2, 58 swish_config:login_item/2, 59 swish_config:li_login_button//1, % +Options 60 swish_config:reply_logged_in/1, % +Options 61 swish_config:reply_logged_out/1, % +Options 62 swish_config:user_info/3, % +Request, -Server, -Info 63 swish_config:user_profile/2. % +Request, -Info
76:- http_handler(swish(login), swish_login, [id(login)]). 77:- http_handler(swish(user_info), user_info, [id(user_info)]). 78 79 80 /******************************* 81 * UI ELEMENTS * 82 *******************************/
page.pl
to include the login buttons.
88swish_config (Options) -->
89 html(li(\login_button(Options))).
96login_button(_Options) --> 97 { findall(Item, login_item(Item), Items0), 98 Items0 \== [], 99 sort(Items0, Items), 100 http_link_to_id(login, [], Login) 101 }, 102 !, 103 html(a([ href(Login), id(login), class(login) ], 104 [ span(class(login), 105 \login_items(Items)), 106 span([ class(logout) 107 ], 108 [ span(class(value), 'Logout') 109 ]) 110 ])). 111login_button(_Options) --> % config-available/auth_http_always.pl 112 html(a([ id(login), class([login, 'no-logout']) ], 113 [ span([ class(logout) 114 ], 115 [ span(class(value), []) 116 ]) 117 ])). 118 119login_item(item(Tag, Server, Item)) :- 120 swish_config:login_item(Server, Item0), 121 ( Item0 = Tag-Item 122 -> true 123 ; Item = Item0, 124 Tag = 0 125 ).
132login_items([item(_Tag, Server, Item)]) --> 133 !, 134 { findall(Attr, login_attr(Item, Attr), Attrs) 135 }, 136 html(span(['data-server'(Server)|Attrs], 137 [ span(class([glyphicon, 'glyphicon-log-in']), []), 138 span(class(value), 'Login') 139 ])). 140login_items(Items) --> 141 { maplist(arg(3), Items, HTML) }, 142 html([ span(class(value), HTML) 143 ]). 144 145login_attr(Item, 'data-frame'(Frame)) :- 146 sub_term('data-frame'(Frame), Item).
At least one of user(User)
or name(Name)
must be present.
The predicate reply_logged_in/1 calls the hook reply_logged_in/1. This hook is provided for interacting with a user profile manager.
174reply_logged_in(Options) :- 175 swish_config:reply_logged_in(Options), 176 !. 177reply_logged_in(Options) :- 178 reply_logged_in_page(Options). 179 180reply_logged_in_page(Options) :- 181 reply_html_page( 182 title('Logged in'), 183 [ h4('Welcome'), 184 p([ 'You have been identified ', 185 \identity_provider(Options), 186 ' as ', 187 \user(Options) 188 ]), 189 \login_continue_button 190 ]). 191 192identity_provider(Options) --> 193 { option(identity_provider(Provider), Options) }, 194 !, 195 html(['by ', Provider]). 196identity_provider(_) --> []. 197 198user(Options) --> 199 { option(user(User), Options) }, 200 !, 201 html(User), 202 ( { option(name(Name), Options) } 203 -> html([' (', Name, ')' ]) 204 ; [] 205 ). 206user(Options) --> 207 { option(name(Name), Options) }, 208 !, 209 html(Name). 210user(_) --> 211 html(unknown).
219login_continue_button -->
220 html(style(\[ 'div.login-continue { text-align: center; margin-top: 2em; }'
221 ])),
222
223 js_script({|javascript||
224function inIframe() {
225 try {
226 return window.self !== window.top;
227 } catch (e) {
228 return true;
229 }
230}
231
232function append( elString, parent ) {
233 var div = document.createElement( "div" );
234 div.innerHTML = elString;
235 document.querySelector( parent || "body" ).appendChild( div.firstChild );
236}
237
238if ( !inIframe() ) {
239 append('<div class="login-continue">\n'+
240 ' <button onclick="window.close()">\n'+
241 ' Continue\n'+
242 ' </button>\n'+
243 '</div>');
244}
245 |}).
253reply_logged_out(Options) :- 254 swish_config:reply_logged_out(Options), 255 !. 256reply_logged_out(Options) :- 257 reply_logged_out_page(Options). 258 259reply_logged_out_page(Options) :- 260 option(reply(Format), Options, json), 261 ( Format == json 262 -> reply_json_dict(true) 263 ; true 264 ). 265 266 267 /******************************* 268 * HTTP HANDLERS * 269 *******************************/
data-server
attribute.
277swish_login(Request) :-
278 http_parameters(Request,
279 [ server(Server, [default(default)])
280 ]),
281 swish_config:login(Server, Request).
update()
from login.js.
289user_info(Request) :-
290 http_parameters(Request,
291 [ reason(Reason, [optional(true)])
292 ]),
293 ( current_user_info(Request, Info)
294 -> reply_json_dict(Info)
295 ; ( Reason == logout_by_http
296 -> broadcast(swish(logout(http)))
297 ; true
298 ),
299 reply_json_dict(null)
300 ).
307current_user_info(Request, Info) :- 308 swish_config:user_info(Request, _Server, UserInfo), 309 ( swish_config:user_profile(Request, Profile) 310 -> copy_fields([identity_provider, auth_method, logout_url], 311 UserInfo, Profile, Info) 312 ; Info = UserInfo 313 ). 314 315copy_fields([], _From, Dict, Dict). 316copy_fields([H|T], From, Dict0, Dict) :- 317 ( V = From.get(H) 318 -> copy_fields(T, From, Dict0.put(H,V), Dict) 319 ; copy_fields(T, From, Dict0, Dict) 320 )
SWISH login support
This module provides the generic code to deal with optional login using multiple protocols. Optional means that SWISH may be used both anonymously and after login.
This module cooperates with web/js/login.js. Login providers are defined using configuration hooks. The various login options are accompagnied by configuration files in
config-available
. */