Skip to content

Commit

Permalink
Merge 645e472 into 5965268
Browse files Browse the repository at this point in the history
  • Loading branch information
adlerfaulkner authored Mar 7, 2022
2 parents 5965268 + 645e472 commit aca1a65
Show file tree
Hide file tree
Showing 21 changed files with 206 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/schedule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
matrix:
branch:
- 'main'
- 'versions/3.0.0'
- 'versions/4.0.0'
timeout-minutes: 10
steps:
- name: Use Node.js 16.x
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ npm start -- # add parameters if needed
```

### 📦 Running via Docker
Docker allows you to run the server without having Node.js installed. Images are built on each tagged version and hosted on [Docker Hub](https://hub.docker.com/r/solidproject/community-server).
Docker allows you to run the server without having Node.js installed. Images are built on each tagged version and hosted on [Docker Hub](https://hub.docker.com/r/solidproject/community-server).

```shell
# Clone the repo to get access to the configs
Expand All @@ -83,6 +83,9 @@ docker run --rm -p 3000:3000 -it solidproject/community-server -c config/default
docker run --rm -v ~/solid-config:/config -p 3000:3000 -it solidproject/community-server -c /config/my-config.json
```

### 🗃️ Helm Chart
The official [Helm](https://helm.sh/) Chart for Kubernetes deployment is maintained and published on [ArtifactHUB](https://artifacthub.io/packages/helm/idlab-gent/css).

## 🔧 Configuring the server
The Community Solid Server is designed to be flexible
such that people can easily run different configurations.
Expand All @@ -107,6 +110,7 @@ to some commonly used settings:
| `--sparqlEndpoint, -s` | | URL of the SPARQL endpoint, when using a quadstore-based configuration. |
| `--showStackTrace, -t` | false | Enables detailed logging on error pages. |
| `--podConfigJson` | `./pod-config.json` | Path to the file that keeps track of dynamic Pod configurations. |
| `--seededPodConfigJson` | | Path to the file that keeps track of seeded Pod configurations. |
| `--mainModulePath, -m` | | Path from where Components.js will start its lookup when initializing configurations.

### 🧶 Custom configurations
Expand Down
9 changes: 8 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ These changes are relevant if you wrote custom modules for the server that depen
This is important for anyone who starts the server from code.
- When logging in, a consent screen will now provide information about the client.

### Data migration
The following actions are required if you are upgrading from a v2 server and want to retain your data.

Due to changes in the keys used by the IDP, you will need to delete the stored keys and sessions.
If you are using a file backend, delete the `.internal/idp/` folder in your data folder and restart the server.
This will not delete the user accounts, but users will have to log in again.

### Configuration changes
You might need to make changes to your v2 configuration if you use a custom config.

Expand All @@ -42,7 +49,7 @@ The `@context` needs to be updated to

The following changes pertain to the imports in the default configs:
- A new configuration option needs to be imported:
- `/app/variables/default/json` contains everything related to parsing CLI arguments
- `/app/variables/default.json` contains everything related to parsing CLI arguments
and assigning values to variables.

The following changes are relevant for v2 custom configs that replaced certain features.
Expand Down
2 changes: 1 addition & 1 deletion config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ it is always possible to not choose any of them and create your own custom versi

# How to use
The easiest way to create a new config is by creating a JSON-LD file
that imports one option from every component subfolder
that imports one option from every component subfolder
(such as either `allow-all.json` or `webacl.json` from `ldp/authorization`).
In case none of the available options suffice, there are 2 other ways to handle this:

Expand Down
2 changes: 2 additions & 0 deletions config/app/init/base/init.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"files-scs:config/app/init/initializers/base-url.json",
"files-scs:config/app/init/initializers/logger.json",
"files-scs:config/app/init/initializers/server.json",
"files-scs:config/app/init/initializers/seeded-pod.json",
"files-scs:config/app/init/initializers/version.json"
],
"@graph": [
Expand All @@ -15,6 +16,7 @@
{ "@id": "urn:solid-server:default:LoggerInitializer" },
{ "@id": "urn:solid-server:default:BaseUrlVerifier" },
{ "@id": "urn:solid-server:default:ParallelInitializer" },
{ "@id": "urn:solid-server:default:SeededPodInitializer" },
{ "@id": "urn:solid-server:default:ServerInitializer" },
{ "@id": "urn:solid-server:default:ModuleVersionVerifier" }
]
Expand Down
23 changes: 23 additions & 0 deletions config/app/init/initializers/seeded-pod.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^3.0.0/components/context.jsonld",
"@graph": [
{
"comment": "Separate manager from the RegistrationHandler in case registration is disabled.",
"@id": "urn:solid-server:default:SeededPodRegistrationManager",
"@type": "RegistrationManager",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_webIdSuffix": "/profile/card#me",
"args_identifierGenerator": { "@id": "urn:solid-server:default:IdentifierGenerator" },
"args_ownershipValidator": { "@id": "urn:solid-server:auth:password:OwnershipValidator" },
"args_accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" },
"args_podManager": { "@id": "urn:solid-server:default:PodManager" }
},
{
"comment": "Initializer that instantiates all the seeded accounts and pods.",
"@id": "urn:solid-server:default:SeededPodInitializer",
"@type": "SeededPodInitializer",
"registrationManager": { "@id": "urn:solid-server:default:SeededPodRegistrationManager" },
"configFilePath": { "@id": "urn:solid-server:default:variable:seededPodConfigJson" },
}
]
}
5 changes: 5 additions & 0 deletions config/app/variables/cli/cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@
"requiresArg": true,
"type": "string",
"describe": "Path to the file that keeps track of dynamic Pod configurations."
},
"seededPodConfigJson": {
"requiresArg": true,
"type": "string",
"describe": "Path to the file that will be used to seed pods."
}
},
"options": {
Expand Down
9 changes: 8 additions & 1 deletion config/app/variables/resolver/resolver.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,19 @@
}
},
{
"CombinedSettingsResolver:_computers_key": "urn:solid-server:default:variable:AssetPathResolver",
"CombinedSettingsResolver:_computers_key": "urn:solid-server:default:variable:podConfigJson",
"CombinedSettingsResolver:_computers_value": {
"@type": "AssetPathExtractor",
"key": "podConfigJson",
"defaultPath": "./pod-config.json"
}
},
{
"CombinedSettingsResolver:_computers_key": "urn:solid-server:default:variable:seededPodConfigJson",
"CombinedSettingsResolver:_computers_value": {
"@type": "AssetPathExtractor",
"key": "seededPodConfigJson"
}
}
]
}
Expand Down
5 changes: 5 additions & 0 deletions config/util/variables/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
"comment": "Path to the JSON file used to store configuration for dynamic pods.",
"@id": "urn:solid-server:default:variable:podConfigJson",
"@type": "Variable"
},
{
"comment": "Path to the JSON file used to seed pods.",
"@id": "urn:solid-server:default:variable:seededPodConfigJson",
"@type": "Variable"
}
]
}
25 changes: 25 additions & 0 deletions guides/seeding-pods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# How to seed Accounts and Pods
If you need to seed accounts and pods, set the `--seededPodConfigJson` option to a file such as `./seeded-pod-config.json` to set your desired accounts and pods. The contents of `./seeded-pod-config.json` (or whatever file name you choose) should be a JSON array whose entries are objects which include
`podName`, `email`, and `password`. For example:
```json
[
{
"podName": "example",
"email": "hello@example.com",
"password": "abc123"
}
]
```

You may optionally specify other parameters accepted by the `register` method of [RegistrationManager](https://github.com/solid/community-server/blob/3b353affb1f0919fdcb66172364234eb59c2e3f6/src/identity/interaction/email-password/util/RegistrationManager.ts#L173). For example:

To use a pre-existing wedId:
```json
createWebId: false,
webId: "https://pod.inrupt.com/example/profile/card#me"
```

To use a specific pod template:
```json
template: "./templates/custom_pod_template"
```
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ export * from './init/ConfigPodInitializer';
export * from './init/ContainerInitializer';
export * from './init/Initializer';
export * from './init/LoggerInitializer';
export * from './init/SeededPodInitializer';
export * from './init/ServerInitializer';
export * from './init/ModuleVersionVerifier';

Expand Down
51 changes: 51 additions & 0 deletions src/init/SeededPodInitializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { promises as fsPromises } from 'fs';
import type { RegistrationManager } from '../identity/interaction/email-password/util/RegistrationManager';
import { getLoggerFor } from '../logging/LogUtil';
import { Initializer } from './Initializer';

/**
* Uses a {@link RegistrationManager} to initialize accounts and pods
* for all seeded pods. Reads the pod settings from seededPodConfigJson.
*/
export class SeededPodInitializer extends Initializer {
protected readonly logger = getLoggerFor(this);

private readonly registrationManager: RegistrationManager;
private readonly configFilePath: string | null;

public constructor(registrationManager: RegistrationManager, configFilePath: string | null) {
super();
this.registrationManager = registrationManager;
this.configFilePath = configFilePath;
}

public async handle(): Promise<void> {
if (this.configFilePath) {
const configText = await fsPromises.readFile(this.configFilePath, 'utf8');
const configuration: NodeJS.Dict<unknown>[] = JSON.parse(configText);

let count = 0;
for await (const input of configuration) {
const config = {
confirmPassword: input.password,
createPod: true,
createWebId: true,
register: true,
...input,
};

this.logger.info(`Initializing pod ${input.podName}`);

// Validate the input JSON
const validated = this.registrationManager.validateInput(config, true);
this.logger.debug(`Validated input: ${JSON.stringify(validated)}`);

// Register and/or create a pod as requested. Potentially does nothing if all booleans are false.
await this.registrationManager.register(validated, true);
this.logger.info(`Initialized seeded pod and account for "${input.podName}".`);
count += 1;
}
this.logger.info(`Initialized ${count} seeded pods.`);
}
}
}
11 changes: 8 additions & 3 deletions src/init/variables/extractors/AssetPathExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ export class AssetPathExtractor extends SettingsExtractor {

public async handle(args: Settings): Promise<unknown> {
const path = args[this.key] ?? this.defaultPath;
if (typeof path !== 'string') {
throw new Error(`Invalid ${this.key} argument`);
if (path) {
if (typeof path !== 'string') {
throw new Error(`Invalid ${this.key} argument`);
}

return resolveAssetPath(path);
}
return resolveAssetPath(path);

return null;
}
}
2 changes: 2 additions & 0 deletions test/deploy/conformance.env
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ RESOURCE_SERVER_ROOT=http://localhost:3000
TEST_CONTAINER=/alice/
quarkus.log.category."ResultLogger".level=INFO
quarkus.log.category."com.intuit.karate".level=DEBUG
quarkus.log.category."org.solid.testharness.http.Client".level=DEBUG
quarkus.log.category."org.solid.testharness.http.AuthManager".level=DEBUG
MAXTHREADS=1
1 change: 1 addition & 0 deletions test/integration/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ export function getDefaultVariables(port: number, baseUrl?: string): Record<stri
'urn:solid-server:default:variable:port': port,
'urn:solid-server:default:variable:loggingLevel': 'off',
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:seededPodConfigJson': null,
};
}
1 change: 1 addition & 0 deletions test/integration/WebSocketsProtocol.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe('A server with the Solid WebSockets API behind a proxy', (): void => {
getTestConfigPath('server-without-auth.json'),
getDefaultVariables(port, 'https://example.pod/'),
) as App;

await app.start();
});

Expand Down
2 changes: 2 additions & 0 deletions test/integration/config/ldp-with-auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"files-scs:config/http/static/default.json",
"files-scs:config/identity/access/public.json",
"files-scs:config/identity/handler/default.json",
"files-scs:config/identity/ownership/token.json",
"files-scs:config/identity/pod/static.json",
"files-scs:config/ldp/authentication/debug-auth-header.json",
"files-scs:config/ldp/authorization/webacl.json",
"files-scs:config/ldp/handler/default.json",
Expand Down
3 changes: 3 additions & 0 deletions test/integration/config/server-without-auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"files-scs:config/http/middleware/websockets.json",
"files-scs:config/http/server-factory/websockets.json",
"files-scs:config/http/static/default.json",
"files-scs:config/identity/handler/account-store/default.json",
"files-scs:config/identity/ownership/unsafe-no-check.json",
"files-scs:config/identity/pod/static.json",
"files-scs:config/ldp/authentication/dpop-bearer.json",
"files-scs:config/ldp/authorization/allow-all.json",
"files-scs:config/ldp/handler/default.json",
Expand Down
3 changes: 3 additions & 0 deletions test/unit/init/AppRunner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ describe('AppRunner', (): void => {
'urn:solid-server:default:variable:rootFilePath': '/var/cwd/',
'urn:solid-server:default:variable:showStackTrace': false,
'urn:solid-server:default:variable:podConfigJson': '/var/cwd/pod-config.json',
'urn:solid-server:default:variable:seededPodConfigJson': '/var/cwd/seeded-pod-config.json',
};
const createdApp = await new AppRunner().create(
{
Expand Down Expand Up @@ -99,6 +100,7 @@ describe('AppRunner', (): void => {
'urn:solid-server:default:variable:rootFilePath': '/var/cwd/',
'urn:solid-server:default:variable:showStackTrace': false,
'urn:solid-server:default:variable:podConfigJson': '/var/cwd/pod-config.json',
'urn:solid-server:default:variable:seededPodConfigJson': '/var/cwd/seeded-pod-config.json',
};
await new AppRunner().run(
{
Expand Down Expand Up @@ -166,6 +168,7 @@ describe('AppRunner', (): void => {
'-s', 'http://localhost:5000/sparql',
'-t',
'--podConfigJson', '/different-path.json',
'--seededPodConfigJson', '/different-path.json',
];
process.argv = argvParameters;

Expand Down
46 changes: 46 additions & 0 deletions test/unit/init/SeededPodInitializer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import fs from 'fs';
import type { RegistrationManager } from '../../../src/identity/interaction/email-password/util/RegistrationManager';
import { SeededPodInitializer } from '../../../src/init/SeededPodInitializer';

jest.mock('fs');
const mockFs = fs as jest.Mocked<typeof fs>;

describe('A SeededPodInitializer', (): void => {
const dummyConfig = JSON.stringify([
{
podName: 'example',
email: 'hello@example.com',
password: 'abc123',
},
{
podName: 'example2',
email: 'hello2@example.com',
password: '123abc',
},
]);
let registrationManager: RegistrationManager;
let configFilePath: string | null;

beforeEach(async(): Promise<void> => {
configFilePath = './seeded-pod-config.json';
registrationManager = {
validateInput: jest.fn((input): any => input),
register: jest.fn(),
} as any;

(mockFs.promises as unknown) = { readFile: jest.fn().mockResolvedValue(dummyConfig) };
});

it('does not generate any accounts or pods if no config file is specified.', async(): Promise<void> => {
configFilePath = null;
await new SeededPodInitializer(registrationManager, configFilePath).handle();
expect(registrationManager.validateInput).not.toHaveBeenCalled();
expect(registrationManager.register).not.toHaveBeenCalled();
});

it('generates an account and a pod for every entry in the seeded pod configuration.', async(): Promise<void> => {
await new SeededPodInitializer(registrationManager, configFilePath).handle();
expect(registrationManager.validateInput).toHaveBeenCalledTimes(2);
expect(registrationManager.register).toHaveBeenCalledTimes(2);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ describe('An AssetPathExtractor', (): void => {
resolver = new AssetPathExtractor('path', '/root');
await expect(resolver.handle({ otherPath: '/var/data' })).resolves.toBe('/root');
});

it('returns null if not default value or default is provided.', async(): Promise<void> => {
resolver = new AssetPathExtractor('path');
await expect(resolver.handle({ otherPath: '/var/data' })).resolves.toBeNull();
});
});

0 comments on commit aca1a65

Please sign in to comment.