1/* Part of SWISH 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2014-2018, 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_config, 37 [ swish_reply_config/2, % +Request, +Options 38 swish_config/2, % ?Type, ?Config 39 swish_config_hash/2 % -HASH, +Options 40 ]). 41:- use_module(library(http/http_dispatch)). 42:- use_module(library(http/http_path)). 43:- use_module(library(http/http_json)). 44:- use_module(library(option)). 45:- use_module(library(apply)). 46 47:- multifile 48 config/2, % ?Key, ?Value 49 config/3, % ?Key, ?Value, +Options 50 web_plugin/1, % ?Dict 51 source_alias/2, % ?Alias, ?Options 52 authenticate/2, % +Request, -User 53 login_item/2, % -Server, -HTML_DOM 54 login/2, % +Server, +Request 55 user_info/3. % +Request, -Server, -Info 56 57/** <module> Make HTTP locations known to JSON code 58*/ 59 60 /******************************* 61 * CONFIG * 62 *******************************/ 63 64%% swish_reply_config(+Request, +Options) is semidet. 65% 66% Emit a configuration object to the client if the client requests 67% for '.../swish_config.json', regardless of the path prefix. 68 69swish_reply_config(Request, Options) :- 70 option(path(Path), Request), 71 file_base_name(Path, 'swish_config.json'), 72 json_config(JSON, Options), 73 reply_json(JSON). 74 75%% swish_config_hash(-Hash, +Options) is det. 76% 77% True if Hash is the SHA1 of the SWISH config. 78 79swish_config_hash(Hash, Options) :- 80 json_config(Config, Options), 81 variant_sha1(Config, Hash). 82 83json_config(json{ http: json{ locations:JSON 84 }, 85 swish: SWISHConfig, 86 plugins : Plugins 87 }, Options) :- 88 http_locations(JSON), 89 swish_config_dict(SWISHConfig, Options), 90 web_plugins(Plugins, Options). 91 92http_locations(JSON) :- 93 findall(ID-Path, 94 ( http_current_handler(Path, _:_, Options), 95 memberchk(id(ID), Options) 96 ), Pairs), 97 keysort(Pairs, Sorted), 98 remove_duplicate_ids(Sorted, Cleaned), 99 dict_pairs(JSON, json, Cleaned). 100 101remove_duplicate_ids([], []). 102remove_duplicate_ids([Id-Path1,Id-Path2|T], [Id-Path1|Cleaned]) :- !, 103 same_ids(T, Id, T1, Paths0), 104 sort([Path1,Path2|Paths0], Unique), 105 ( Unique = [_] 106 -> true 107 ; print_message(warning, http(duplicate_handlers(Id, Unique))) 108 ), 109 remove_duplicate_ids(T1, Cleaned). 110remove_duplicate_ids([H|T0], [H|T]) :- 111 remove_duplicate_ids(T0, T). 112 113same_ids([], _, [], []). 114same_ids([Id-Path|T0], Id, T, [Path|TP]) :- !, 115 same_ids(T0, Id, T, TP). 116same_ids(T, _, T, []). 117 118 119%% swish_config_dict(-Config:dict, +Options) is det. 120% 121% Obtain name-value pairs from swish_config:config/2 122 123swish_config_dict(Config, Options) :- 124 findall(Key-Value, swish_config(Key, Value, Options), Pairs), 125 keysort(Pairs, Sorted), 126 warn_duplicate_config(Sorted, Unique), 127 dict_pairs(Config, json, Unique). 128 129:- dynamic warned_duplicate/1. 130:- volatile warned_duplicate/1. 131 132warn_duplicate_config([], []). 133warn_duplicate_config([K-V1,K-V2|T0], [K-V1|T]) :- !, 134 collect_same(K, T0, VL, T1), 135 ( warned_duplicate(K) 136 -> true 137 ; sort([V1,V2|VL], [_]) 138 -> true 139 ; print_message(warning, swish(duplicate_config(K, [V1,V2|VL]))), 140 assertz(warned_duplicate(K)) 141 ), 142 warn_duplicate_config(T1, T). 143warn_duplicate_config([KV|T0], [KV|T]) :- !, 144 warn_duplicate_config(T0, T). 145 146collect_same(K, [K-V|T0], [V|VT], T) :- !, 147 collect_same(K, T0, VT, T). 148collect_same(_, List, [], List). 149 150%! web_plugins(-Plugins, +Options) is det. 151% 152% Obtain a list of JSON dicts for additional web plugins. 153 154web_plugins(Plugins, _Options) :- 155 findall(Plugin, web_plugin_ex(Plugin), Plugins). 156 157web_plugin_ex(Plugin) :- 158 web_plugin(Plugin0), 159 dict_pairs(Plugin0, Tag, Pairs0), 160 maplist(expand_paths, Pairs0, Pairs), 161 dict_pairs(Plugin, Tag, Pairs). 162 163:- multifile http:location/3. 164:- dynamic http:location/3. 165 166expand_paths(Name-Spec, Name-Path) :- 167 compound(Spec), 168 compound_name_arity(Spec, Alias, 1), 169 http:location(Alias, _, _), 170 !, 171 http_absolute_location(Spec, Path, []). 172expand_paths(Pair, Pair). 173 174 175%% config(-Key, -Value) is nondet. 176%% swish_config(-Key, -Value) is nondet. 177% 178% Define a name/value pair that will end up in the SWISH config 179% object (see =web/js/config.js=) 180 181swish_config(Key, Value) :- 182 swish_config(Key, Value, []). 183 184swish_config(Key, Value, Options) :- 185 config(Key, Value, Options). 186swish_config(Key, Value, _) :- 187 config(Key, Value). 188 189config(residuals_var, '_residuals'). 190:- if(exists_source(library(wfs))). 191config(wfs_residual_program_var, '_wfs_residual_program'). 192:- endif. 193 194 195 /******************************* 196 * LOGIN * 197 *******************************/ 198 199%! login_item(-Server, -Item) is nondet. 200% 201% This hook is called to find all possible login options. It 202% should bind Item to an HTML description for html//1 that must be 203% clicked to login with this option. The item may have the 204% following HTML attributes: 205% 206% - 'data-server'(+Server) 207% This must be present and provides the first argument for the 208% login/2 hook. 209% 210% - 'data-frame'(+Style) 211% The login is realised in a popup to avoid reloading the 212% current swish page. If Style is `popup`, a browser popup window 213% is used. This is necessary for identity providers that refuse to 214% open inside a frame. The default is `iframe`, which handles 215% the login inside an =iframe= element in a modal popup. 216% 217% The Item is often an image. The image must have a class 218% =login-with=. Below is an example to login with Google: 219% 220% ``` 221% swish_config:login_item(Item) :- 222% http_absolute_location(icons('social_google_box.png'), Img, []), 223% Item = img([ src(Img), 224% class('login-with'), 225% 'data-server'(google), 226% title('Login with Google') 227% ]). 228% ``` 229% 230% @arg Item may be of the form `Tag-Item`. In this case the items 231% are ordered by Tag. The default tag is `0`. 232 233%! login(+Server, +Request) is det. 234% 235% If a login item with `'data-server'(+Server)` is clicked, the 236% HTTP handler with id `login` is called. This handler figures the 237% selected login _server_ and calls this hook. 238 239%! user_info(+Request, -Server, -UserInfo:dict) is semidet. 240% 241% Each login facility must provide this hook. The hook should 242% succeed if-and-only-if the user is logged in using this facility 243% and the hook must bind UserInfo with a dict that contains the 244% following fields: 245% 246% - user: User 247% User name (id) if the logged in user. 248% - name: Name 249% Common name of the logged in user. 250% - email: Email 251% Email address of the logged in user. 252% - picture: URL 253% If present, URL is used to indicate the currently logged in 254% user. 255% - auth_method: Method 256% Authentication method used. Currently one of `basic`, `digest` 257% or `oauth2`. 258% - logout_url: URL 259% URL that must be used to logout. Needed if `auth_method` is 260% not one of the HTTP authentication methods (`basic` or 261% `digest`). 262% 263% If this hook fails the user is not logged in. 264 265 266 /******************************* 267 * OTHER HOOKS * 268 *******************************/ 269 270%% source_alias(?Alias, ?Options) is nondet. 271% 272% Multifile hook that defines properties of file_search_path/2 273% aliases wrt. storage handling. Defined options are: 274% 275% - access(Access) 276% One of `read` or `both`. 277% - search(Pattern) 278% The _New Tab_ search form searches in files that satisfy the 279% given pattern in the matching directories. Pattern is handed 280% to expand_file_name/2. 281 282 283 /******************************* 284 * MESSAGES * 285 *******************************/ 286 287:- multifile 288 prolog:message//1. 289 290prologmessage(http(duplicate_handlers(Id, Paths))) --> 291 [ 'Duplicate HTTP handler IDs: "~w"'-[Id] ], 292 paths(Paths). 293prologmessage(swish(duplicate_config(K, [V0|List]))) --> 294 [ 'Duplicate SWISH config values for "~w": ~p. Using ~q'- 295 [K, [V0|List], V0] 296 ]. 297 298paths([]) --> []. 299paths([H|T]) --> [ '\t~q'-[H], nl ], paths(T)