summaryrefslogtreecommitdiff
path: root/arch.html
blob: 5e9f8ac207e1f80c89cfdb47732b9ab042450a21 (plain)
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
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="author" content="Lars Wirzenius" />
  <title>Effireg architecture</title>
  <style type="text/css">
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
  </style>
  <style type="text/css">html {
font-family: serif;
margin-left: 4em;
margin-right: 4em;
}
h1, h2, h3 {
font-family: sans-serif;
margin-top: 2em;
}
h1 { font-size: 3em; }
h2 { font-size: 2em; }
h3 { font-size: 1em; }
</style>
</head>
<body>
<header>
<h1 class="title">Effireg architecture</h1>
<p class="author">Lars Wirzenius</p>
<p class="date">Version: trial-1-g4f2e1de-dirty</p>
</header>
<nav id="TOC">
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#architecture-overview">Architecture overview</a><ul>
<li><a href="#assumptions">Assumptions</a></li>
<li><a href="#components">Components</a></li>
<li><a href="#authentication">Authentication</a></li>
</ul></li>
<li><a href="#data-model">Data model</a><ul>
<li><a href="#subject-resource">Subject resource</a></li>
<li><a href="#password-resource">Password resource</a></li>
<li><a href="#member-resource-memb">Member resource (memb)</a></li>
</ul></li>
<li><a href="#effiapi">effiapi</a><ul>
<li><a href="#manage-memberships">Manage memberships</a></li>
</ul></li>
<li><a href="#appendix-yarn-scenario-step-implementations">Appendix: Yarn scenario step implementations</a></li>
</ul>
</nav>
<h1 id="introduction">Introduction</h1>
<p>Effireg is a web-based membership register for the Effi non-profit association. See <a href="https://effi.org/" class="uri">https://effi.org/</a> for more information about Effi.</p>
<p>Effireg is written for Effi, but it is free software, released under the Affero GPL v3 license, and may be used by others. No guarantees of quality.</p>
<h1 id="architecture-overview">Architecture overview</h1>
<h2 id="assumptions">Assumptions</h2>
<p>The architecture has been designed under the following assumptions:</p>
<ul>
<li><p>Privacy is important, and should be the default. People should only have access to the information they are authorized to access.</p></li>
<li><p>Members should be able to see their own information, without having to go through the Effi membership register admin.</p></li>
<li><p>It’s not practical to have every member have a password. Authentication can be done by sending the member a unique, single-use link when they want to log in.</p></li>
</ul>
<h2 id="components">Components</h2>
<figure>
<img src="data:image/png;base64,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" alt="Architectural components" /><figcaption>Architectural components</figcaption>
</figure>
<p>Effireg consists of four main components:</p>
<ul>
<li><p><strong>effiapi</strong> provides a RESTful HTTP API for managing the membership data, and for doing things with or to the data. All operations go via the API.</p></li>
<li><p><strong>effiweb</strong> provides the frontend for the web application to use the membership register. It renders HTML to the user, is accesses via a web browser, and generally is the user-visible part of Effireg.</p></li>
<li><p><strong>Qvisqve</strong> provides authentication of end-users (the members, and admins). It lets users log in, and keeps track of what each user is allowed to do.</p></li>
<li><p><strong>Muck-POC</strong> stores the actual membership register data, and controls access to it, based on access tokens from Qvisqve.</p></li>
</ul>
<h2 id="authentication">Authentication</h2>
<p>End-users are authenticated using the <a href="https://openid.net/specs/openid-connect-core-1_0.html">OpenID Connect</a> protocol, specifically the authorization code flow. In this flow, Qvisqve provides cryptographically signed access tokens, which identify the user and specify a list of things the user may do. The signature guarantees the token comes from Qvisqve.</p>
<p>Non-interactive API clients are authenticated using the <a href="https://oauth.net/2/">OAuth2</a> protocol, specifically using client credential grants. This also provides an access token, similar to the one from end-user authentication.</p>
<h1 id="data-model">Data model</h1>
<p>The membership register stores data as “resources” in Muck-POC. Each resource is a JSON object. The following types of objects are supported:</p>
<ul>
<li><strong>subject</strong> represents a person who is allowed to use the register; it it used to identify the user for authentication</li>
<li><strong>password</strong> stores the encrypted password for a subject</li>
<li><strong>memb</strong> represets a subject’s membership in Effi</li>
</ul>
<h3 id="subject-resource">Subject resource</h3>
<p>A subject resource has the following fields:</p>
<ul>
<li><code>username</code> — the username of the subject; this can change, the subject is identified by the resource identifier in the register, not by the username</li>
</ul>
<p>The subject resource does not have any data that isn’t needed for end-user identification. Effiapi manages and Qvisqve uses the subject resource.</p>
<h3 id="password-resource">Password resource</h3>
<p>A password resource has the following fields:</p>
<ul>
<li><code>subject_id</code> — resource id of the subject whose password this is</li>
<li><code>version</code> — version of the password resource (identifies algorithm); must be 1</li>
<li><code>hash</code> — the password, encrypted with the scrypt algorithm</li>
<li><code>salt</code> — a random string to prevent dictionary attacks</li>
<li><code>key_len</code> — parameter for scrypt</li>
<li><code>N</code> — parameter for scrypt</li>
<li><code>r</code> — parameter for scrypt</li>
<li><code>p</code> — parameter for scrypt</li>
</ul>
<p>Effiapi manages and Qvisqve uses the password resource.</p>
<h3 id="member-resource-memb">Member resource (memb)</h3>
<p>A membership resource has the following fields:</p>
<ul>
<li><code>subject-id</code> — the resource id of the subject resource for the member</li>
<li><code>fullname</code> — the full name of the member</li>
<li><code>primary-email</code> — the primary email address for the member (and currently the only one)</li>
<li><code>years-paid</code> — list of integers for the years for which the member has paid their membership</li>
</ul>
<p>Effiapi manages and uses the memb resource. Effiweb renders it for the user.</p>
<h1 id="effiapi">effiapi</h1>
<p>This chapter descibes the effiapi API, as a <a href="https://liw.fi/cmdtest/">yarn</a> automated scenario test. It is meant to be understandable to Effi sysadmins, as well as be an executable test suite for the API.</p>
<p>The API is a simple RESTful HTTP API using JSON. This means that:</p>
<ul>
<li><p>each API call is an HTTP request, with a response</p></li>
<li><p>all data is represented as JSON in the body of the request of response</p></li>
<li><p>metadata about the data is represeneted as HTTP headers</p></li>
<li><p>standard HTTP verbs (POST, PUT, GET, DELETE) are used to indicate the action</p></li>
<li><p>standard HTTP status codes are used to indicate result of the request (200 = OK, 404 = not found, etc)</p></li>
</ul>
<h2 id="manage-memberships">Manage memberships</h2>
<p>This section shows the API calls to manage a memberhip: to create the member, to update and retrieve it, and to search memberships.</p>
<pre><code>SCENARIO Manage memberships

GIVEN An effiapi instance
WHEN admin requests POST /memb with body { &quot;fullname&quot;: &quot;James Bond&quot; }
THEN the member id is ID

WHEN admin requests GET /memb with header Muck-Id: ${ID}
THEN HTTP status 200
AND HTTP body matches { &quot;fullname&quot;: &quot;James Bond&quot; }</code></pre>
<p>TODO:</p>
<ul>
<li>update</li>
<li>delete</li>
<li>search</li>
<li>members can see their own data, and can’t see each others’</li>
<li>member follows authn link emailed to them</li>
</ul>
<h1 id="appendix-yarn-scenario-step-implementations">Appendix: Yarn scenario step implementations</h1>
</body>
</html>