1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2013, VU University Amsterdam 7 All rights reserved. 8 9 Redistribution and use in source and binary forms, with or without 10 modification, are permitted provided that the following conditions 11 are met: 12 13 1. Redistributions of source code must retain the above copyright 14 notice, this list of conditions and the following disclaimer. 15 16 2. Redistributions in binary form must reproduce the above copyright 17 notice, this list of conditions and the following disclaimer in 18 the documentation and/or other materials provided with the 19 distribution. 20 21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 POSSIBILITY OF SUCH DAMAGE. 33*/ 34 35:- module(http_ax, 36 [ http_ax_attributes/2, % +Spec, -AttributeList 37 ax_form_attributes/2 % +Form, -Values 38 ]). 39:- use_module(library(error)). 40 41 42/** <module> Attribute Exchange library 43 44This library can be used to create HTTP request parameters and analyse 45form-data for _attribute exchange_. Attribute exchange (AX) is used by 46OpenID and OAuth to fetch attributes for accounts, such as the real 47username or e-mail address. 48*/ 49 50%! http_ax_attributes(+Spec, -HTTPAttributes) is det. 51% 52% True when HTTPAttributes is a list of Name=Value pairs that can 53% be used with an HTTP request to query for the attributes Spec. 54% Spec is a list of elements =|Alias(Value[, Options])|=. Options 55% include: 56% 57% - required 58% The attribute is required. This is mutually exclusive 59% with =if_available=. 60% - if_available 61% Only provide the attribute if it is available. This is 62% mutually exclusive with =required=. This is the default. 63% - url(+URL) 64% Can be used to ovcerrule or extend the ax_alias/2. 65% - count(+Count) 66% Maximum number of values to provide 67% 68% For example: 69% 70% == 71% ?- http_ax_attributes([ nickname(Nick), 72% email(Email, [required]) 73% ], Params). 74% Params = [ 'openid.ax.mode' = fetch_request, 75% 'openid.ax.type.nickname' = 'http://axschema.org/namePerson/friendly', 76% 'openid.ax.type.email' = 'http://axschema.org/contact/email', 77% 'openid.ax.required' = email, 78% 'openid.ax.if_available' = nickname 79% ]. 80% == 81 82http_ax_attributes(Spec, [ 'openid.ns.ax' = 'http://openid.net/srv/ax/1.0', 83 'openid.ax.mode' = fetch_request 84 | AllAttr 85 ]) :- 86 maplist(type_alias, Spec, AliasAttrs), 87 partition(required, Spec, Required, Optional), 88 alias_list(Required, 'openid.ax.required', RequiredAttr), 89 alias_list(Optional, 'openid.ax.if_available', IfAvailableAttr), 90 count_attr(Spec, CountAttr), 91 append([AliasAttrs, RequiredAttr, IfAvailableAttr, CountAttr], AllAttr). 92 93type_alias(Spec, Attr=URL) :- 94 functor(Spec, Alias, Arity), 95 ( Arity > 1, 96 arg(2, Spec, Options), 97 memberchk(url(URL), Options) 98 -> true 99 ; ax_alias(Alias, URL) 100 -> true 101 ; existence_error(ax_alias, Alias) 102 ), 103 atom_concat('openid.ax.type.', Alias, Attr). 104 105required(Spec) :- 106 functor(Spec, _, 2), 107 arg(2, Spec, Options), 108 memberchk(required, Options). 109 110alias_list([], _, []). 111alias_list(Specs, A, [A=V]) :- 112 maplist(alias_name, Specs, Aliases), 113 atomic_list_concat(Aliases, ',', V). 114 115alias_name(Spec, Alias) :- 116 functor(Spec, Alias, _). 117 118count_attr([], []). 119count_attr([Spec|T0], [A=Count|T]) :- 120 functor(Spec, Alias, 2), 121 arg(2, Spec, Options), 122 memberchk(count(Count), Options), 123 !, 124 atomic_list_concat('openid.ax.count.', Alias, A), 125 count_attr(T0, T). 126count_attr([_|T0], T) :- 127 count_attr(T0, T). 128 129 130%! ax_alias(?Alias, ?URL) is nondet. 131% 132% True when Alias is an alias name for the AX schema URL. This 133% predicate is defined as _multifile_. 134% 135% Note that Google federated login only supports =email=, 136% =country=, =language=, =firstname= and =lastname=. 137 138:- multifile 139 ax_alias/2. 140 141ax_alias(nickname, 'http://axschema.org/namePerson/friendly'). 142ax_alias(email, 'http://axschema.org/contact/email'). 143ax_alias(fullname, 'http://axschema.org/namePerson'). 144ax_alias(dob, 'http://axschema.org/birthDate'). 145ax_alias(gender, 'http://axschema.org/person/gender'). 146ax_alias(postcode, 'http://axschema.org/contact/postalCode/home'). 147ax_alias(country, 'http://axschema.org/contact/country/home'). 148ax_alias(language, 'http://axschema.org/pref/language'). 149ax_alias(timezone, 'http://axschema.org/pref/timezone'). 150ax_alias(prefix, 'http://axschema.org/namePerson/prefix'). 151ax_alias(firstname, 'http://axschema.org/namePerson/first'). 152ax_alias(lastname, 'http://axschema.org/namePerson/last'). 153ax_alias(suffix, 'http://axschema.org/namePerson/suffix'). 154 155 156 /******************************* 157 * RESPONSE * 158 *******************************/ 159 160%! ax_form_attributes(+Form, -Values) is det. 161% 162% True if Values is a list Alias(Value) for each exchanged 163% attribute. 164% 165% Note that we assume we get the same alias names as we used for 166% requesting the data. Not sure whether this is true. 167% 168% @arg Form is an HTTP form as returned using the form(Form) 169% option of http_parameters/3. 170 171ax_form_attributes(Form, Values) :- 172 ( memberchk('openid.ax.mode'=fetch_response, Form) 173 -> Ext = ax 174 ; memberchk(ExtNS='http://openid.net/srv/ax/1.0', Form), 175 atomic_list_concat([openid,ns,Ext], '.', ExtNS) 176 -> true 177 ), 178 ax_attributes(Form, Ext, Values). 179ax_form_attributes(_, []). 180 181ax_attributes([], _, []). 182ax_attributes([Name=Value|T0], Ext, AXs) :- 183 atomic_list_concat([openid, Ext, value, Alias|_Num], '.', Name), 184 !, 185 AX =.. [Alias,Value], 186 AXs = [AX|AXT], 187 ax_attributes(T0, Ext, AXT). 188ax_attributes([_|T0], Ext, AXs) :- 189 ax_attributes(T0, Ext, AXs)