Skip to content

Commit c719d52

Browse files
authored
admin can list users & admins; replaces user signup with request for invite (#115)
* lists users & admins, generates & validates admin invites; replaces user signup with request for invite * Fixes bug that prevented using a proxy for HTTPS termination * OAuth stores parameters in session, rather than passing to client & back * Allows cross-platform passkeys, cross-origin access to storage, & user selection of grant duration * Corrects caching headers and improves error messages when session expires * Refactors contactURLToLink, assembleContactURL & protocol options into new protocols module w/ planned configurability * Adds robots.txt file to discourage crawling * Logging casts a wider net when extracting username * Adds passkey logo everywhere passkeys are mentioned * Logged-in user can invite themself to create another passkey * npm audit fix * Adds rate-limiting to defend against buggy and compromised clients * Implements /.well-known/change-password as redirect, for password managers
1 parent 062aa91 commit c719d52

File tree

87 files changed

+6173
-1363
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+6173
-1363
lines changed

DEVELOPMENT.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
## Setup
44

55
1. Run `git clone https://github.com/remotestorage/armadietto.git` to pull the code.
6-
2. Run `yarn install` or `npm install`. to install the dependencies.
6+
2. Run `npm install` to install the dependencies.
7+
3. Register for S3-compatible storage with your hosting provider, install [a self-hosted implementation](notes/S3-store-router.md), or use the public account on `play.min.io` (which is slow, and to which anyone in the world can read and write!).
78

89
## Development
910

10-
* Run `npm test` to run the automated tests.
11-
* Run `npm run dev` to start a server on your local machine, and have it automatically restart when you update a source code file in `/lib`.
11+
* Run `npm test` to run the automated tests for both monolithic and modular servers (except the tests for S3 store router).
12+
* Set the S3 environment variables and run Mocha with `./node_modules/mocha/bin/mocha.js -u bdd-lazy-var/getter spec/armadietto/storage_spec.js` to test the S3 store router using your configured S3 server. (If the S3 environment variables aren't set, the S3 router uses the shared public account on play.min.io.) If any tests fail, add a note to [the S3 notes](notes/S3-store-router.md)
13+
* Run `npm run modular` to start a modular server on your local machine, and have it automatically restart when you update a source code file in `/lib`.
14+
* Run `npm run dev` to start a monolithic server on your local machine, and have it automatically restart when you update a source code file in `/lib`.
1215

13-
Set the environment `DEBUG` to enable verbose logging of HTTP requests.
16+
Set the environment `DEBUG` to enable verbose logging of HTTP requests. For the modular server, these are the requests to the S3 server. For the monolithic server, these are the requests to Armadietto.
1417

1518
Add automated tests for any new functionality. For bug fixes, start by writing an automated test that fails under the buggy code, but will pass when you've written a fix. Using TDD is not required, but will help you write better code.
1619

README.md

+15-76
Original file line numberDiff line numberDiff line change
@@ -24,46 +24,24 @@ This is a complete rewrite of [reStore](https://github.com/jcoglan/restore).
2424

2525
See the `notes` directory for configuring a reverse proxy and other recipes.
2626

27-
1. Run `armadietto -e` to see a sample configuration file.
28-
2. Create a configuration file at `/etc/armadietto/conf.json` (or elsewhere). See below for values and their meanings.
29-
3. Run `armadietto -c /etc/armadietto/conf.json`
27+
### Modular (new) Server
3028

31-
To see all options, run `armadietto -h`. Set the environment `DEBUG` to log the headers of every request.
29+
* Streaming storage (documents don't have to fit in server memory)
30+
* S3-compatible storage (requires separate S3 server; AWS S3 allows documents up to 5 TB)
31+
* Can run multiple application servers to increase capacity to enterprise-scale
32+
* Bug Fix: correctly handles If-None-Match with ETag
33+
* Bug Fix: returns empty listing for nonexistent folder
34+
* Implements current spec: draft-dejong-remotestorage-22
3235

33-
## Use as a library
36+
See [the modular-server-specific documentation](./modular-server.md) for usage.
3437

35-
The following Node script will run a basic server:
38+
### Monolithic (old) Server
3639

37-
```js
38-
process.umask(077);
39-
40-
const Armadietto = require('armadietto');
41-
store = new Armadietto.FileTree({path: 'path/to/storage'}),
42-
43-
server = new Armadietto({
44-
store: store,
45-
http: {host: '127.0.0.1', port: 8000}
46-
});
47-
48-
server.boot();
49-
```
50-
51-
The `host` option is optional and specifies the hostname the server will listen
52-
on. Its default value is `0.0.0.0`, meaning it will listen on all interfaces.
40+
* Stores user documents in server file system
41+
* More thoroughly tested
42+
* Implements older spec: draft-dejong-remotestorage-01
5343

54-
The server does not allow users to sign up, out of the box. If you need to allow
55-
that, use the `allow.signup` option:
56-
57-
```js
58-
var server = new Armadietto({
59-
store: store,
60-
http: { host: '127.0.0.1', port: 8000 },
61-
allow: { signup: true }
62-
});
63-
```
64-
65-
If you navigate to `http://localhost:8000/` you should then see a sign-up link
66-
in the navigation.
44+
See [the monolithic-server-specific documentation](./monolithic-server.md) for usage.
6745

6846
## Storage security
6947

@@ -159,45 +137,6 @@ setup, you can set `https.force = true` but omit `https.port`; this means
159137
armadietto itself will not accept encrypted connections but will apply the above
160138
behaviour to enforce secure connections.
161139

162-
## Storage backends
163-
164-
armadietto supports pluggable storage backends, and comes with a file system
165-
implementation out of the box (redis storage backend is on the way in
166-
`feature/redis` branch):
167-
168-
* `Armadietto.FileTree` - Uses the filesystem hierarchy and stores each item in its
169-
own individual file. Content and metadata are stored in separate files so the
170-
content does not need base64-encoding and can be hand-edited. Must only be run
171-
using a single server process.
172-
173-
All the backends support the same set of features, including the ability to
174-
store arbitrary binary data with content types and modification times.
175-
176-
They are configured as follows:
177-
178-
```js
179-
// To use the file tree store:
180-
const store = new Armadietto.FileTree({path: 'path/to/storage'});
181-
182-
// Then create the server with your store:
183-
const server = new Armadietto({
184-
store: store,
185-
http: {port: process.argv[2]}
186-
});
187-
188-
server.boot();
189-
```
190-
191-
## Lock file contention
192-
193-
The data-access locking mechanism is lock-file based.
194-
195-
You may need to tune the lock-file timeouts in your configuration:
196-
197-
- *lock_timeout_ms* - millis to wait for lock file to be available
198-
- *lock_stale_after_ms* - millis to wait to deem lockfile stale
199-
200-
To tune run the [hosted RS load test](https://overhide.github.io/armadietto/example/load.html) or follow instructions in [example/README.md](example/README.md) for local setup and subsequently run [example/load.html](example/load.html) off of `npm run serve` therein.
201140

202141
## Debugging an installation
203142

@@ -211,8 +150,8 @@ See `DEVELOPMENT.md`
211150

212151
(The MIT License)
213152

214-
Copyright (c) 2012-2015 James Coglan
215-
Copyright (c) 2018 remoteStorage contributors
153+
Copyright © 20122015 James Coglan
154+
Copyright © 2018–2024 remoteStorage contributors
216155

217156
Permission is hereby granted, free of charge, to any person obtaining a copy of
218157
this software and associated documentation files (the 'Software'), to deal in

bin/www

+14-11
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ const http = require('http');
44
const fs = require("fs");
55
const path = require("path");
66
const {ArgumentParser} = require("argparse");
7+
const { stat } = require('node:fs/promises');
78
const appFactory = require('../lib/appFactory');
89
const {configureLogger, getLogger} = require("../lib/logger");
9-
const S3Handler = require("../lib/routes/S3_store_router");
10+
const S3StoreRouter = require("../lib/routes/S3_store_router");
1011
const process = require("process");
1112
const https = require("https");
13+
const errToMessages = require("../lib/util/errToMessages");
1214

1315
const SSL_CIPHERS = 'ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM';
1416
const SSL_OPTIONS = require('crypto').constants.SSL_OP_CIPHER_SERVER_PREFERENCE;
@@ -51,23 +53,23 @@ if (!hostIdentity) {
5153
const userNameSuffix = conf.user_name_suffix ?? '-' + hostIdentity;
5254

5355
if (conf.http?.port) {
54-
start( Object.assign({}, conf.http, process.env.PORT && {port: process.env.PORT}));
56+
start( Object.assign({}, conf.http, process.env.PORT && {port: process.env.PORT})).catch(getLogger.error);
5557
}
5658

5759
if (conf.https?.port) {
58-
start(conf.https);
60+
start(conf.https).catch(getLogger.error);
5961
}
6062

6163

62-
function start(network) {
63-
// If the environment variables aren't set, s3handler uses a shared public account on play.min.io,
64+
async function start(network) {
65+
// If the environment variables aren't set, s3storeRouter uses a shared public account on play.min.io,
6466
// to which anyone in the world can read and write!
65-
// It is not entirely compatible with S3Handler.
66-
const s3handler = new S3Handler({endPoint: process.env.S3_ENDPOINT,
67+
// It is not entirely compatible with S3StoreRouter.
68+
const s3storeRouter = new S3StoreRouter({endPoint: process.env.S3_ENDPOINT,
6769
accessKey: process.env.S3_ACCESS_KEY, secretKey: process.env.S3_SECRET_KEY, region: process.env.S3_REGION || 'us-east-1',
6870
userNameSuffix});
6971

70-
const app = appFactory({hostIdentity, jwtSecret, account: s3handler, storeRouter: s3handler, basePath});
72+
const app = await appFactory({hostIdentity, jwtSecret, accountMgr: s3storeRouter, storeRouter: s3storeRouter, basePath});
7173

7274
const port = normalizePort( network?.port || '8000');
7375
app.set('port', port);
@@ -106,7 +108,7 @@ function start(network) {
106108

107109
server.listen(port);
108110
server.on('error', onError);
109-
server.on('clientError', clientError);
111+
server.on('clientError', clientError); // a client connection emitted an 'error' event
110112
server.on('listening', onListening);
111113

112114
/** Event listener for HTTP server "error" event. */
@@ -212,10 +214,11 @@ function clientError (err, socket) {
212214
status = 400;
213215
message = 'Bad Request';
214216
}
215-
getLogger().warning(`${socket.address().address} n/a n/a ${status} ${message} ${err.toString()}`);
217+
const logNotes = errToMessages(err, new Set([message]));
218+
getLogger().warning(`- - - - - ${status} - “${Array.from(logNotes).join(' ')}”`);
216219

217220
if (err.code !== 'ECONNRESET' && socket.writable) {
218221
socket.end(`HTTP/1.1 ${status} ${message}\r\n\r\n`);
219222
}
220-
socket.destroy(err);
223+
socket.destroySoon();
221224
}

contrib/systemd/armadietto.service

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Restart=always
1111
RestartSec=1
1212
User=armadietto
1313
Group=armadietto
14-
Environment=
14+
Environment=NODE_ENV=production
1515
ExecStartPre=
1616
ExecStart=/usr/bin/armadietto -c /etc/armadietto/conf.json
1717
ExecStartPost=

0 commit comments

Comments
 (0)