-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsmee_feds.ex
441 lines (350 loc) · 13.4 KB
/
smee_feds.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
defmodule SmeeFeds do
@moduledoc """
`SmeeFeds` is a SAML federation data management extension to [Smee](https://github.com/Digital-Identity-Labs/smee) for use in
research, testing and development.
[Smee](https://github.com/Digital-Identity-Labs/smee) has tools for handling the sources of SAML metadata but
nothing to represent the publishers of metadata. SmeeFeds adds a few tools for handling federations and includes a large
collection of information about research and education federations.
## Features
* Easily find information on National Research and Education organisation (NREN) federations.
* Filter and group federations by location, type, structure and tags.
* Use federation records directly with Smee to download metadata from aggregates or MDQ servers
* Export lists of federation information as CSV, JSON or Markdown documents
* Manage and load federation data into your applications
The top level `SmeeFeds` module has tools for selecting individual federation details or lists of many at once.
SmeeFeds contain more tools for handling federations, such as:
* `SmeeFeds.Federation` - tools for accessing data such as metadata download URLs, contacts, homepages, and so on.
* `SmeeFeds.Export` - convert lists of federations into JSON or CSV data for export, or simple text reports
* `SmeeFeds.Import` - convert JSON documents into Federation lists
* `SmeeFeds.Filter` - filter lists of federations by various criteria
## IMPORTANT DISCLAIMER AND WARNING
SmeeFeds comes with a built-in list of federations, using information gathered from various sources on the Internet.
This collection of information is example data for use by **researchers, developers and testers**.
**IT IS NOT FOR USE IN PRODUCTION ENVIRONMENTS**
Metadata is the bedrock of trust and information security in SAML federations. DO NOT use metadata URLs, certificates
and certificate fingerprints to download and use metadata in live services without confirming each detail yourself.
If you must use SmeeFeds as part of a production service, then after information has been verified you can export only
the verified information you need as a JSON file and set it as the new default using
`:smee_feds, :data_file` config setting in your application (if compiled) or set a list of Federations with
`:smee_feds, :federations` at runtime.
There is absolutely no guarantee or warranty that the data in SmeeFeds is correct, and it is not supported by any of
the federations listed. It's totally unofficial.
"""
alias SmeeFeds.Federation
alias SmeeFeds.DefaultData
alias SmeeFeds.Utils
alias Smee.Metadata
alias Smee.Entity
alias Smee.Source
@doc """
Returns a list of `SmeeFeds.Federation` structs from the default collection.
Returns all known federations from the default collection.
## Example
iex> federations = SmeeFeds.federations()
iex> Enum.count(federations)
76
"""
@spec federations() :: list(Federation.t())
def federations() do
DefaultData.federations()
|> Map.values()
|> Enum.sort_by(& &1.id)
end
@doc """
Returns a list of the specified `SmeeFeds.Federation` structs from the default collection.
## Example
iex> federations = SmeeFeds.federations([:ukamf, :wayf])
iex> Enum.count(federations)
2
"""
@spec federations(ids :: atom() | list(atom() | binary())) :: list(Federation.t())
def federations(ids) do
DefaultData.federations()
|> Map.take(Utils.to_safe_atoms(ids))
|> Map.values()
end
@doc """
Finds a federation in the default database by ID and returns the full federation record.
## Example
iex> incommon = SmeeFeds.federation(:incommon)
iex> incommon.policy
"https://incommon.org/about/policies/"
"""
@spec federation(federation :: atom() | binary()) :: Federation.t() | nil
def federation(id) do
DefaultData.federations()
|> Map.get(Utils.to_safe_atom(id))
end
@doc """
Returns the ids of all federations in the provided list of federations as a list of atoms.
## Example
iex> ids = SmeeFeds.ids(SmeeFeds.federations())
iex> Enum.slice(ids, 0..3)
[:aaf, :aaieduhr, :aaiedumk, :aconet]
"""
@spec ids(federations :: list()) :: list(atom())
def ids(federations) do
federations
|> Enum.map(fn f -> f.id end)
end
@doc """
Returns a filtered list of `SmeeFeds.Federation` structs when passed a list of
federations and federation IDs (as atoms) to select.
## Example
iex> all_federations = SmeeFeds.federations()
iex> my_federations = SmeeFeds.take(all_federations, [:ukamf, :switch])
iex> Enum.map(my_federations, fn f -> f.name end)
["SWITCHaai", "UK Access Management Federation"]
"""
@spec take(federations :: list(), federation_ids :: list(atom())) :: list(Federation.t())
def take(federations, federation_ids) when is_list(federations) do
federations
|> SmeeFeds.Filter.ids(federation_ids)
end
@doc """
Finds a federation in the supplied list by ID and returns the full federation record.
## Example
iex> federations = SmeeFeds.federations()
iex> incommon = SmeeFeds.get(federations, :incommon)
iex> incommon.url
"http://incommon.org/"
"""
@spec get(federations :: list(), id :: atom() | binary()) :: Federation.t() | nil
def get(federations, id) do
federations
|> Enum.find(fn f -> Utils.to_safe_atom(id) == f.id end)
end
@doc """
Tries to find the federation that published the provided Smee record (source, entity or metadata)
The first matching federation record will be returned if found, or nil if no federations match.
## Example
iex> source = Smee.source("http://metadata.ukfederation.org.uk/ukfederation-metadata.xml")
iex> federations = SmeeFeds.federations()
iex> federation = SmeeFeds.publisher(federations, source)
%SmeeFeds.Federation{id: :ukamf} = federation
"""
@spec publisher(federations :: list(), smee_struct :: Source.t() | Metadata.t() | Entity.t()) :: Federation.t() | nil
def publisher(federations, smee_struct) do
federations
|> Enum.find(fn federation -> publisher?(federation, smee_struct) end)
end
@doc """
Is a federation the publisher of the provided Smee Source, Metadata, or Entity?
Returns true if the federation and source, metadata or entity share a URL or publisher URI, false otherwise.
## Example
iex> source = Smee.source("http://metadata.ukfederation.org.uk/ukfederation-metadata.xml")
iex> federations = SmeeFeds.federations()
iex> federation = SmeeFeds.get(federations, :ukamf)
iex> SmeeFeds.publisher?(federation, source)
true
"""
@spec publisher?(Federation.t(), smee_struct :: Source.t() | Metadata.t() | Entity.t()) :: boolean()
def publisher?(federation, %Metadata{uri: uri, url: url}) do
cond do
uri == federation.uri -> true
Enum.any?(Federation.sources(federation), fn s -> s.url == url end) -> true
true -> false
end
end
def publisher?(federation, %Entity{metadata_uri: uri}) do
cond do
uri == federation.uri -> true
true -> false
end
end
def publisher?(federation, %Source{url: url}) do
cond do
Enum.any?(Federation.sources(federation), fn s -> s.url == url end) -> true
true -> false
end
end
## Needs an update to Smee S, M, E modules first
# def registrar()
# def registrar?()
@doc """
Lists all countries in the provided list of federations
## Examples
iex> SmeeFeds.federations([:ukamf, :incommon]) |> SmeeFeds.countries()
"""
@spec countries(federations :: list(Federation.t())) :: list(struct())
def countries(federations) do
federations
|> List.wrap()
|> Enum.flat_map(fn f -> Map.get(f, :countries, []) end)
|> Enum.uniq()
|> Enum.sort()
|> Enum.map(fn code -> Countries.get(code) end)
end
@doc """
Lists all regions in the provided list of federations
## Examples
iex> SmeeFeds.federations([:ukamf, :incommon]) |> SmeeFeds.regions()
["Americas", "Europe"]
"""
@spec regions(federations :: list(Federation.t())) :: list(struct())
def regions(federations) do
federations
|> countries()
|> Enum.map(fn c -> c.region end)
|> Enum.uniq()
|> Enum.sort()
end
@doc """
Lists all sub_regions in the provided list of federations
## Examples
iex> SmeeFeds.federations([:ukamf, :incommon]) |> SmeeFeds.sub_regions()
["Northern America", "Northern Europe"]
"""
@spec sub_regions(federations :: list(Federation.t())) :: list(struct())
def sub_regions(federations) do
federations
|> countries()
|> Enum.map(fn c -> c.subregion end)
|> Enum.uniq()
|> Enum.sort()
end
@doc """
Lists all super_regions in the provided list of federations
## Examples
iex> SmeeFeds.federations([:ukamf, :incommon]) |> SmeeFeds.super_regions()
["AMER", "EMEA"]
"""
@spec super_regions(federations :: list(Federation.t())) :: list(struct())
def super_regions(federations) do
federations
|> countries()
|> Enum.map(fn c -> c.world_region end)
|> Enum.uniq()
|> Enum.sort()
end
@doc """
Lists all federation types in the provided list of federations, as atoms.
## Examples
iex> SmeeFeds.federations() |> SmeeFeds.types()
[:inter, :misc, :nren]
"""
@spec types(federations :: list(Federation.t())) :: list(atom())
def types(federations) do
federations
|> Enum.map(fn f -> Map.get(f, :type) end)
|> Enum.uniq
|> Enum.sort()
end
@doc """
Lists all types of structures in the provided list of federations, as atoms.
## Examples
iex> SmeeFeds.federations() |> SmeeFeds.structures()
[:has, :mesh]
"""
@spec structures(federations :: list(Federation.t())) :: list(atom())
def structures(federations) do
federations
|> Enum.map(fn f -> Map.get(f, :structure) end)
|> Enum.uniq
|> Enum.sort()
end
@doc """
Lists all ID types in the provided list of federations, as atoms.
## Examples
iex> SmeeFeds.federations() |> SmeeFeds.id_types()
[:edugain, :met, :smee, :uri]
"""
@spec id_types(federations :: list(Federation.t())) :: list(atom())
def id_types(federations) do
federations
|> Enum.map(
fn f ->
Map.get(f, :alt_ids, %{})
|> Map.keys() end
)
|> Enum.concat([:smee, :uri])
|> List.flatten()
|> Enum.uniq
|> Enum.sort()
end
@doc """
Lists all unique tags in the provided list of federations, as atoms.
## Examples
iex> SmeeFeds.federations() |> SmeeFeds.tags()
["noSlow", "noTest"]
"""
@spec tags(federations :: list(Federation.t())) :: list(atom())
def tags(federations) do
federations
|> Enum.map(fn f -> Map.get(f, :tags) end)
|> List.flatten()
|> Enum.uniq
|> Enum.sort()
end
@doc """
Lists all unique protocols in the provided list of federations, as atoms.
## Examples
iex> SmeeFeds.federations() |> SmeeFeds.protocols()
[:saml2]
"""
@spec protocols(federations :: list(Federation.t())) :: list(atom())
def protocols(federations) do
federations
|> Enum.map(fn f -> Map.get(f, :protocols) end)
|> List.flatten()
|> Enum.uniq
|> Enum.sort()
end
@doc """
Lists all upstream federation IDs in the provided list of federations, as atoms.
## Examples
iex> SmeeFeds.federations() |> SmeeFeds.upstream()
[:edugain]
iex> [%SmeeFeds.Federation{id: :edugain}] = SmeeFeds.federations |> SmeeFeds.upstream() |> SmeeFeds.federations()
"""
@spec upstream(federations :: list(Federation.t())) :: list(atom())
def upstream(federations) do
federations
|> Enum.map(fn f -> Map.get(f, :interfederates) end)
|> List.flatten()
|> Enum.uniq
|> Enum.sort()
end
@doc """
Returns a federation record if one with the specified ID is present in the list, or nil if one can't be found
Available IDs can be found using `SmeeFeds.id_types/1`. Two are built-in: `:uri` and `:smee`. Any other
ID key used in the `alt_ids` part of the Federation struct can be searched.
## Examples
iex> SmeeFeds.federations() |> SmeeFeds.get_by(:uri, "http://ukfederation.org.uk")
iex> SmeeFeds.federations() |> SmeeFeds.get_by(:edugain, "HAKA")
"""
@spec get_by(federations :: list(Federation.t()), id_type :: atom(), id :: atom() | binary()) :: Federation.t() | nil
def get_by(federations, id_type, id)
def get_by(federations, :smee, id) do
get(federations, id)
end
def get_by(federations, :uri, id) do
federations
|> Enum.find(fn f -> id == f.uri end)
end
def get_by(federations, id_type, id) do
id = "#{id}"
id_type = Utils.to_safe_atom(id_type)
federations
|> Enum.find(
fn f ->
id == Map.get(f, :alt_ids, %{})
|> Map.get(id_type, nil) end
)
end
@doc """
Accepts a list of federations, and returns a list of federations with tags automatically added.
Uses `Federation.autotag!/2`, and works with lists and streams.
## Examples
iex> tags = SmeeFeds.federations() |> SmeeFeds.autotag!() |> SmeeFeds.tags()
iex> "mesh" in tags
true
"""
@spec autotag!(enum :: Enumerable.t(), options :: keyword()) :: Enumerable.t()
def autotag!(federations, options \\ []) do
federations
|> List.wrap()
|> Enum.map(fn f -> Federation.autotag!(f, options) end)
end
#############################################################################
end