Merge pull request 'Rolle | Wiki.js' (#1) from dev-wiki.js into main

Reviewed-on: #1
This commit is contained in:
roydfalk 2024-10-11 09:25:20 +02:00
commit 2f43ccd173
18 changed files with 1638 additions and 0 deletions

View file

@ -0,0 +1,6 @@
{
"var_authelia_for_wiki_js_wiki_js_url_base": "https://wiki-js.example.org",
"var_authelia_for_wiki_js_client_id": "wiki_js",
"var_authelia_for_wiki_js_client_secret": "REPLACE_ME",
"var_authelia_for_wiki_js_strategy_id": "authelia"
}

View file

@ -0,0 +1,9 @@
## Beschreibung
Um [Wiki.js](../wiki.js) gegen [Authelia](../authelia) authentifizieren zu lassen
## Verweise
[Wiki.js-Dokumentation | Authentication](https://docs.requarks.io/auth)
[Authelia-Dokumentation | Wiki.js Integration](https://www.authelia.com/integration/openid-connect/wikijs/)

View file

@ -0,0 +1,25 @@
[
{
"name": "configuration | emplace",
"become": true,
"ansible.builtin.template": {
"src": "authelia-client-conf.json.j2",
"dest": "/etc/authelia/conf.d/clients/wiki_js.json"
}
},
{
"name": "configuration | apply",
"become": true,
"ansible.builtin.command": {
"cmd": "/usr/bin/authelia-conf-compose"
}
},
{
"name": "restart service",
"become": true,
"ansible.builtin.systemd_service": {
"state": "restarted",
"name": "authelia"
}
}
]

View file

@ -0,0 +1,17 @@
{
"client_id": "{{var_authelia_for_wiki_js_client_id}}",
"client_secret": "{{var_authelia_for_wiki_js_client_secret}}",
"client_name": "Wiki.js",
"public": false,
"authorization_policy": "one_factor",
"redirect_uris": [
"{{var_authelia_for_wiki_js_wiki_js_url_base}}/login/{{var_authelia_for_wiki_js_strategy_id}}/callback"
],
"scopes": [
"openid",
"email",
"profile"
],
"userinfo_signed_response_alg": "none",
"token_endpoint_auth_method": "client_secret_post"
}

View file

@ -0,0 +1,5 @@
{
"var_postgresql_for_wiki_js_username": "wiki_js_user",
"var_postgresql_for_wiki_js_password": "REPLACE_ME",
"var_postgresql_for_wiki_js_schema": "wiki_js"
}

View file

@ -0,0 +1,49 @@
[
{
"name": "packages",
"become": true,
"ansible.builtin.apt": {
"update_cache": true,
"pkg": [
"acl",
"python3-psycopg2"
]
}
},
{
"name": "user",
"become": true,
"become_user": "postgres",
"community.postgresql.postgresql_user": {
"state": "present",
"name": "{{var_postgresql_for_wiki_js_username}}",
"password": "{{var_postgresql_for_wiki_js_password}}"
},
"environment": {
"PGOPTIONS": "-c password_encryption=scram-sha-256"
}
},
{
"name": "schema",
"become": true,
"become_user": "postgres",
"community.postgresql.postgresql_db": {
"state": "present",
"name": "{{var_postgresql_for_wiki_js_schema}}",
"owner": "{{var_postgresql_for_wiki_js_username}}"
}
},
{
"name": "rights",
"become": true,
"become_user": "postgres",
"community.postgresql.postgresql_privs": {
"state": "present",
"db": "{{var_postgresql_for_wiki_js_schema}}",
"objs": "ALL_IN_SCHEMA",
"roles": "{{var_postgresql_for_wiki_js_username}}",
"privs": "ALL",
"grant_option": true
}
}
]

View file

@ -0,0 +1,5 @@
{
"var_wiki_js_and_nginx_domain": "wiki-js.example.org",
"var_wiki_js_and_nginx_internal_port": 5632,
"var_wiki_js_and_nginx_tls_mode": "force"
}

View file

@ -0,0 +1,3 @@
## Verweise
- [Wiki.js-Dokumentation | Web Server](https://docs.requarks.io/en/install/requirements#web-server)

View file

@ -0,0 +1,35 @@
[
{
"name": "deactivate default site",
"become": true,
"ansible.builtin.file": {
"state": "absent",
"dest": "/etc/nginx/sites-enabled/default"
}
},
{
"name": "emplace configuration | data",
"become": true,
"ansible.builtin.template": {
"src": "conf.j2",
"dest": "/etc/nginx/sites-available/{{var_wiki_js_and_nginx_domain}}"
}
},
{
"name": "emplace configuration | link",
"become": true,
"ansible.builtin.file": {
"state": "link",
"src": "/etc/nginx/sites-available/{{var_wiki_js_and_nginx_domain}}",
"dest": "/etc/nginx/sites-enabled/{{var_wiki_js_and_nginx_domain}}"
}
},
{
"name": "restart nginx",
"become": true,
"ansible.builtin.systemd_service": {
"state": "restarted",
"name": "nginx"
}
}
]

View file

@ -0,0 +1,33 @@
{% macro wiki_js_common() %}
location / {
proxy_pass http://localhost:{{var_wiki_js_and_nginx_internal_port | string}};
}
{% endmacro %}
server {
server_name {{var_wiki_js_and_nginx_domain}};
listen 80;
listen [::]:80;
{% if (var_wiki_js_and_nginx_tls_mode == 'force') %}
return 301 https://$http_host$request_uri;
{% else %}
{{ wiki_js_common() }}
{% endif %}
}
{% if (var_wiki_js_and_nginx_tls_mode != 'disable') %}
server {
server_name {{var_wiki_js_and_nginx_domain}};
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate_key /etc/ssl/private/{{var_wiki_js_and_nginx_domain}}.pem;
ssl_certificate /etc/ssl/fullchains/{{var_wiki_js_and_nginx_domain}}.pem;
include /etc/nginx/ssl-hardening.conf;
{{ wiki_js_common() }}
}
{% endif %}

View file

@ -0,0 +1,19 @@
{
"domain": {
"type": "string",
"mandatory": false
},
"internal_port": {
"type": "integer",
"mandatory": false
},
"tls_mode": {
"type": "string",
"options": [
"disable",
"enable",
"force"
],
"mandatory": false
}
}

View file

@ -0,0 +1,36 @@
{
"var_wiki_js_port": 5632,
"var_wiki_js_distributed": false,
"var_wiki_js_domain": "wiki-js.example.org",
"var_wiki_js_user": "wiki_js",
"var_wiki_js_directory": "/opt/wiki_js",
"var_wiki_js_data_path": "/var/wiki_js/data",
"var_wiki_js_log_level": "info",
"var_wiki_js_log_format": "default",
"var_wiki_js_database_kind": "sqlite",
"var_wiki_js_database_data_sqlite_path": "/var/wiki_js/data.sqlite",
"var_wiki_js_database_data_postgresql_host": "postgresql.example.org",
"var_wiki_js_database_data_postgresql_port": 5432,
"var_wiki_js_database_data_postgresql_username": "wiki_js_user",
"var_wiki_js_database_data_postgresql_password": "REPLACE_ME",
"var_wiki_js_database_data_postgresql_schema": "wiki_js",
"var_wiki_js_authentication_kind": "internal",
"var_wiki_js_authentication_data_authelia_provider_id": "authelia",
"var_wiki_js_authentication_data_authelia_provider_name": "Authelia",
"var_wiki_js_authentication_data_authelia_client_id": "wiki_js",
"var_wiki_js_authentication_data_authelia_client_secret": "REPLACE_ME",
"var_wiki_js_authentication_data_authelia_url_base": "https://authelia.example.org",
"var_wiki_js_smtp_host": "smtp.example.org",
"var_wiki_js_smtp_port": 465,
"var_wiki_js_smtp_username": "REPLACE_ME",
"var_wiki_js_smtp_password": "REPLACE_ME",
"var_wiki_js_email_sending_sender_name": "Wiki.js",
"var_wiki_js_email_sending_sender_email_address": "wiki-js@example.org",
"var_wiki_js_admin_email_address": "wiki-js-admin@example.org",
"var_wiki_js_admin_password": "REPLACE_ME",
"var_wiki_js_additional_locales": [],
"var_wiki_js_user_group_name": "Default",
"var_wiki_js_allow_guest_view": true,
"var_wiki_js_dark_mode": false,
"var_wiki_js_toc_position": "left"
}

959
roles/wiki_js/files/wiki-js-cli Executable file
View file

@ -0,0 +1,959 @@
#!/usr/bin/env node
var _wiki_js_cli;
(function (_wiki_js_cli) {
var helpers;
(function (helpers) {
var string;
(function (string) {
/**
*/
function coin(template, arguments_) {
let result = template;
Object.entries(arguments_).forEach(([key, value]) => {
result = result.replace(new RegExp("{{" + key + "}}", "g"), value);
});
return result;
}
string.coin = coin;
})(string = helpers.string || (helpers.string = {}));
})(helpers = _wiki_js_cli.helpers || (_wiki_js_cli.helpers = {}));
})(_wiki_js_cli || (_wiki_js_cli = {}));
var _wiki_js_cli;
(function (_wiki_js_cli) {
var helpers;
(function (helpers) {
var file;
(function (file) {
/**
*/
function read(path) {
const nm_fs = require("fs");
return (new Promise((resolve, reject) => {
nm_fs.readFile(path, {}, (err, data) => {
if (err) {
reject(err);
}
else {
resolve(data.toString());
}
});
}));
}
file.read = read;
})(file = helpers.file || (helpers.file = {}));
})(helpers = _wiki_js_cli.helpers || (_wiki_js_cli.helpers = {}));
})(_wiki_js_cli || (_wiki_js_cli = {}));
var _wiki_js_cli;
(function (_wiki_js_cli) {
var helpers;
(function (helpers) {
var log;
(function (log) {
/**
*/
let enum_level;
(function (enum_level) {
enum_level["debug"] = "debug";
enum_level["info"] = "info";
enum_level["notice"] = "notice";
enum_level["warning"] = "warning";
enum_level["error"] = "error";
})(enum_level = log.enum_level || (log.enum_level = {}));
/**
*/
const _level_order = [
enum_level.debug,
enum_level.info,
enum_level.notice,
enum_level.warning,
enum_level.error,
];
/**
*/
var _threshold = enum_level.info;
/**
*/
function setup(threshold) {
_threshold = threshold;
return Promise.resolve(undefined);
}
log.setup = setup;
/**
*/
function write(level, incident, details) {
if (_level_order.indexOf(level) < _level_order.indexOf(_threshold)) {
// do nothing
}
else {
process.stderr.write(_wiki_js_cli.helpers.string.coin("\n<{{datetime}}> [{{level}}] {{incident}}\n{{details}}\n\n", {
"datetime": (new Date()).toISOString(),
"level": level,
"incident": incident,
"details": JSON.stringify(details, undefined, "\t"),
}));
}
}
log.write = write;
})(log = helpers.log || (helpers.log = {}));
})(helpers = _wiki_js_cli.helpers || (_wiki_js_cli.helpers = {}));
})(_wiki_js_cli || (_wiki_js_cli = {}));
var _wiki_js_cli;
(function (_wiki_js_cli) {
var helpers;
(function (helpers) {
var http;
(function (http) {
/**
*/
async function call(http_request) {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.debug, "http_call_request", http_request);
const fetch_request = new Request(http_request.target, {
"method": http_request.method,
"headers": http_request.headers,
"body": http_request.body,
});
const fetch_response = await fetch(fetch_request);
const http_response = {
"status_code": fetch_response.status,
"headers": Object.fromEntries(fetch_response.headers.entries()),
"body": await fetch_response.text(),
};
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.debug, "http_call_response", http_response);
return http_response;
}
http.call = call;
})(http = helpers.http || (helpers.http = {}));
})(helpers = _wiki_js_cli.helpers || (_wiki_js_cli.helpers = {}));
})(_wiki_js_cli || (_wiki_js_cli = {}));
var _wiki_js_cli;
(function (_wiki_js_cli) {
var helpers;
(function (helpers) {
var args;
(function (args) {
/**
*/
function parse(args_raw) {
let result = {
"positional": [],
"volatile": {},
};
let state = "free";
let key = null;
args_raw.forEach((arg_raw) => {
switch (state) {
case "free": {
if (arg_raw.startsWith("-")) {
key = arg_raw.slice(1);
state = "bound";
}
else {
if (key === null) {
result.positional.push(arg_raw);
key = null;
state = "free";
}
else {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.warning, "arg_discarded", {
"arg_raw": arg_raw,
});
key = null;
state = "free";
}
}
break;
}
case "bound": {
if (!(key in result["volatile"])) {
result["volatile"][key] = [];
}
else {
// do nothing
}
result["volatile"][key].push(arg_raw);
key = null;
state = "free";
}
}
});
return result;
}
args.parse = parse;
})(args = helpers.args || (helpers.args = {}));
})(helpers = _wiki_js_cli.helpers || (_wiki_js_cli.helpers = {}));
})(_wiki_js_cli || (_wiki_js_cli = {}));
var _wiki_js_cli;
(function (_wiki_js_cli) {
var conf;
(function (conf) {
/**
*/
var _data = null;
/**
*/
async function load(path) {
let data_raw;
if (path === null) {
data_raw = {};
}
else {
const content = await _wiki_js_cli.helpers.file.read(path);
data_raw = JSON.parse(content);
}
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.debug, "conf_raw", data_raw);
_data = {
"api": {
"url_base": (data_raw?.api?.url_base
??
"http://localhost:3000"),
},
"login": {
"username": (data_raw?.login?.username
??
"admin"),
"password": (data_raw?.login?.password
??
"admin"),
},
"log": {
"threshold": (data_raw?.log?.threshold
??
"info"),
}
};
return Promise.resolve(undefined);
}
conf.load = load;
/**
*/
function set(data) {
_data = data;
}
conf.set = set;
/**
*/
function get() {
return _data;
}
conf.get = get;
})(conf = _wiki_js_cli.conf || (_wiki_js_cli.conf = {}));
})(_wiki_js_cli || (_wiki_js_cli = {}));
var _wiki_js_cli;
(function (_wiki_js_cli) {
var api;
(function (api) {
/**
*/
var _login_token = null;
/**
*/
var _url_base = null;
/**
*/
function init(url_base) {
_url_base = url_base;
return Promise.resolve(undefined);
}
api.init = init;
/**
*/
async function call_generic_graphql(graphql_query, options) {
options = Object.assign({
"variables": {},
}, options);
const http_request = {
"target": (_url_base + "/graphql"),
"method": "POST",
"headers": Object.assign({
"Content-Type": "application/json",
}, ((_login_token === null)
?
{}
:
{ "Cookie": ("jwt=" + _login_token) })),
"body": JSON.stringify([
{
"operationName": null,
"variables": options.variables,
"extensions": {},
"query": graphql_query,
}
]),
};
const http_response = await _wiki_js_cli.helpers.http.call(http_request);
const data = JSON.parse(http_response.body);
return Promise.resolve(data[0]["data"]);
}
/**
*/
async function call_finalize(admin_email, admin_password, site_url, telemetry) {
const http_request = {
"target": (_wiki_js_cli.conf.get().api.url_base + "/finalize"),
"method": "POST",
"headers": {
"Content-Type": "application/json",
},
"body": JSON.stringify({
"adminEmail": admin_email,
"adminPassword": admin_password,
"adminPasswordConfirm": admin_password,
"siteUrl": site_url,
"telemetry": telemetry,
}),
};
const http_response = await _wiki_js_cli.helpers.http.call(http_request);
const data = JSON.parse(http_response.body);
return Promise.resolve(undefined);
}
api.call_finalize = call_finalize;
/**
* executes a local login
*/
async function call_login_local(username, password) {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.info, "api_call_login_local", {});
return (call_generic_graphql("mutation ($username: String!, $password: String!, $strategy: String!) {authentication {login(username: $username, password: $password, strategy: $strategy) {responseResult {succeeded errorCode slug message __typename} jwt mustChangePwd mustProvideTFA mustSetupTFA continuationToken redirect tfaQRImage __typename} __typename}}", {
"variables": {
"strategy": "local",
"username": username,
"password": password,
}
})
.then((data) => {
if (data["authentication"]["login"]["responseResult"]["succeeded"]) {
_login_token = data["authentication"]["login"]["jwt"];
return Promise.resolve(undefined);
}
else {
return Promise.reject(new Error("login failed"));
}
}));
}
api.call_login_local = call_login_local;
/**
*/
function call_email_settings_set(settings) {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.info, "api_call_email_settings_set", {
"settings": settings,
});
return (call_generic_graphql("mutation ($senderName: String!, $senderEmail: String!, $host: String!, $port: Int!, $name: String!, $secure: Boolean!, $verifySSL: Boolean!, $user: String!, $pass: String!, $useDKIM: Boolean!, $dkimDomainName: String!, $dkimKeySelector: String!, $dkimPrivateKey: String!) {mail {updateConfig(senderName: $senderName, senderEmail: $senderEmail, host: $host, port: $port, name: $name, secure: $secure, verifySSL: $verifySSL, user: $user, pass: $pass, useDKIM: $useDKIM, dkimDomainName: $dkimDomainName, dkimKeySelector: $dkimKeySelector, dkimPrivateKey: $dkimPrivateKey) {responseResult {succeeded errorCode slug message __typename} __typename} __typename}}", {
"variables": {
"senderName": settings.sender_name,
"senderEmail": settings.sender_email_address,
"host": settings.smtp_host,
"port": settings.smtp_port,
"name": settings.name,
"secure": settings.secure,
"verifySSL": settings.verify_ssl,
"user": settings.smtp_username,
"pass": settings.smtp_password,
"useDKIM": settings.use_dkim,
"dkimDomainName": settings.dkim_domain_name,
"dkimKeySelector": settings.dkim_key_selector,
"dkimPrivateKey": settings.dkim_private_key,
}
}));
}
api.call_email_settings_set = call_email_settings_set;
/**
*/
function call_locale_download(locale) {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.info, "api_call_locale_download", {
"locale": locale,
});
return (call_generic_graphql("mutation ($locale: String!) {localization {downloadLocale(locale: $locale) {responseResult {succeeded errorCode slug message __typename} __typename}__typename}}", {
"variables": {
"locale": locale,
}
}));
}
api.call_locale_download = call_locale_download;
/**
*/
function call_group_list(name) {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.info, "api_call_group_list", {});
return (call_generic_graphql("{groups {list {id name isSystem userCount createdAt updatedAt __typename} __typename}}", {
"variables": {}
}));
}
api.call_group_list = call_group_list;
/**
*/
function call_group_create(name) {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.info, "api_call_group_create", {
"name": name,
});
return (call_generic_graphql("mutation ($name: String!) {groups {create(name: $name) {responseResult {succeeded errorCode slug message __typename} group {id name createdAt updatedAt __typename} __typename} __typename}}", {
"variables": {
"name": name,
}
}));
}
api.call_group_create = call_group_create;
/**
* @param permissions_general {
* Array<
* string
* >
* }
* @param permissions_page_specific {
* Array<
* {
* id:string;
* path:string;
* roles:Array<
* string
* >;
* match:string;
* deny:boolean;
* locales:Array<
* string
* >;
* }
* >
* }
*/
function call_group_update(group_id, name, permissions_general, permissions_page_specific) {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.info, "api_call_group_update", {});
return (call_generic_graphql("mutation ($id: Int!, $name: String!, $redirectOnLogin: String!, $permissions: [String]!, $pageRules: [PageRuleInput]!) {groups {update(id: $id, name: $name, redirectOnLogin: $redirectOnLogin, permissions: $permissions, pageRules: $pageRules) {responseResult {succeeded errorCode slug message __typename} __typename} __typename}}", {
"variables": {
"id": group_id,
"name": name,
"redirectOnLogin": "/",
"permissions": permissions_general,
"pageRules": permissions_page_specific,
}
}));
}
api.call_group_update = call_group_update;
/**
*/
function call_authentication_strategy_list() {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.info, "api_call_authentication_strategy_list", {});
return (call_generic_graphql("{authentication {activeStrategies {key strategy {key title description useForm logo website __typename} config {key value __typename} order isEnabled displayName selfRegistration domainWhitelist autoEnrollGroups __typename} __typename}}", {})
.then((data) => Promise.resolve(data["authentication"]["activeStrategies"])));
}
api.call_authentication_strategy_list = call_authentication_strategy_list;
/**
*/
function call_authentication_strategy_set(strategies) {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.info, "api_call_authentication_strategy_set", {
"strategies": strategies,
});
return (call_generic_graphql("mutation ($strategies: [AuthenticationStrategyInput]!) {authentication {updateStrategies(strategies: $strategies) {responseResult {succeeded errorCode slug message __typename} __typename} __typename}}", {
"variables": {
"strategies": strategies,
}
}));
}
api.call_authentication_strategy_set = call_authentication_strategy_set;
/**
*/
function call_theming_set(dark_mode, toc_position) {
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.info, "api_call_theming_set", {
"dark_mode": dark_mode,
"toc_position": toc_position,
});
return (call_generic_graphql("mutation ($theme: String!, $iconset: String!, $darkMode: Boolean!, $tocPosition: String, $injectCSS: String, $injectHead: String, $injectBody: String) {theming {setConfig(theme: $theme, iconset: $iconset, darkMode: $darkMode, tocPosition: $tocPosition, injectCSS: $injectCSS, injectHead: $injectHead, injectBody: $injectBody) {responseResult {succeeded errorCode slug message __typename} __typename} __typename}}", {
"variables": {
"theme": "default",
"iconset": "mdi",
"darkMode": dark_mode,
"tocPosition": toc_position,
"injectCSS": "",
"injectHead": "",
"injectBody": ""
}
}));
}
api.call_theming_set = call_theming_set;
})(api = _wiki_js_cli.api || (_wiki_js_cli.api = {}));
})(_wiki_js_cli || (_wiki_js_cli = {}));
var _wiki_js_cli;
(function (_wiki_js_cli) {
var logic;
(function (logic) {
/**
*/
async function login(options = {}) {
options = Object.assign({
"username": _wiki_js_cli.conf.get().login.username,
"password": _wiki_js_cli.conf.get().login.password,
}, options);
await _wiki_js_cli.api.call_login_local(options.username, options.password);
return Promise.resolve(undefined);
}
logic.login = login;
/**
*/
async function initialize(admin_email_address, admin_password, options = {}) {
options = Object.assign({
"site_url": "http://localhost:3000",
"allow_telemetry": false,
}, options);
await _wiki_js_cli.api.call_finalize(admin_email_address, admin_password, options.site_url, options.allow_telemetry);
return Promise.resolve(undefined);
}
logic.initialize = initialize;
/**
*/
async function email_settings_set(smtp_host, smtp_port, smtp_username, smtp_password, sender_name, sender_email_address, options = {}) {
options = Object.assign({}, options);
const result = await _wiki_js_cli.api.call_email_settings_set({
"sender_name": sender_name,
"sender_email_address": sender_email_address,
"smtp_host": smtp_host,
"smtp_port": smtp_port,
"secure": true,
"verify_ssl": true,
"smtp_username": smtp_username,
"smtp_password": smtp_password,
"name": "",
"use_dkim": false,
"dkim_domain_name": "",
"dkim_key_selector": "",
"dkim_private_key": "",
});
return Promise.resolve(undefined);
}
logic.email_settings_set = email_settings_set;
/**
*/
async function locale_add(locale_code) {
_wiki_js_cli.api.call_locale_download(locale_code);
return Promise.resolve(undefined);
}
logic.locale_add = locale_add;
/**
*/
async function group_identify(name) {
const data = await _wiki_js_cli.api.call_group_list(name);
const hits = (data["groups"]["list"]
.filter((entry) => (entry["name"] === name)));
if (hits.length !== 1) {
return Promise.reject(new Error("not found or ambiguous"));
}
else {
return Promise.resolve(hits[0]["id"]);
}
}
logic.group_identify = group_identify;
/**
* returns the ID of the generated group
*/
async function group_add(name, options = {}) {
options = Object.assign({
"permissions_general": [],
"permissions_specific": [],
}, options);
const result_1 = await _wiki_js_cli.api.call_group_create(name);
const id = result_1["groups"]["create"]["group"]["id"];
const result_2 = await _wiki_js_cli.api.call_group_update(id, name, options.permissions_general, options.permissions_specific);
return Promise.resolve(id);
}
logic.group_add = group_add;
/**
*/
async function group_modify(id, options = {}) {
options = Object.assign({
"permissions_general": [],
"permissions_specific": [],
}, options);
const result = await _wiki_js_cli.api.call_group_update(id, name, options.permissions_general, options.permissions_specific);
return Promise.resolve(undefined);
}
logic.group_modify = group_modify;
/**
*/
async function authentication_strategy_list() {
const result = await _wiki_js_cli.api.call_authentication_strategy_list();
return Promise.resolve(result);
}
logic.authentication_strategy_list = authentication_strategy_list;
/**
*/
async function authentication_strategy_add(strategy) {
const current = await _wiki_js_cli.api.call_authentication_strategy_list();
const result = await _wiki_js_cli.api.call_authentication_strategy_set(((current
.map((entry) => ({
"key": entry["key"],
"strategyKey": entry["strategy"]["key"],
"displayName": entry["displayName"],
"order": entry["order"],
"isEnabled": entry["isEnabled"],
"config": (entry["config"]
.map((item) => ({
"key": item["key"],
"value": JSON.stringify({ "v": JSON.parse(item["value"])["value"] }),
}))),
"selfRegistration": entry["selfRegistration"],
"domainWhitelist": entry["domainWhitelist"],
"autoEnrollGroups": entry["autoEnrollGroups"],
})))
.concat([
{
"key": strategy.key,
"strategyKey": "oauth2",
"displayName": strategy.name,
"order": ((current
.map(x => x["order"])
.reduce((x, y) => (((x === null) || (x < y)) ? y : x), null))
+
1),
"isEnabled": true,
"config": [
{
"key": "clientId",
"value": JSON.stringify({ "v": strategy.client_id }),
},
{
"key": "clientSecret",
"value": JSON.stringify({ "v": strategy.client_secret }),
},
{
"key": "authorizationURL",
"value": JSON.stringify({ "v": strategy.authorization_url }),
},
{
"key": "tokenURL",
"value": JSON.stringify({ "v": strategy.token_url }),
},
{
"key": "userInfoURL",
"value": JSON.stringify({ "v": strategy.user_info_url }),
},
{
"key": "userIdClaim",
"value": JSON.stringify({ "v": "id" }),
},
{
"key": "displayNameClaim",
"value": JSON.stringify({ "v": "name" }),
},
{
"key": "emailClaim",
"value": JSON.stringify({ "v": "email" }),
},
{
"key": "mapGroups",
"value": JSON.stringify({ "v": false }),
},
{
"key": "groupsClaim",
"value": JSON.stringify({ "v": "groups" }),
},
{
"key": "logoutURL",
"value": JSON.stringify({ "v": "" }),
},
{
"key": "scope",
"value": JSON.stringify({ "v": "openid profile email" }),
},
{
"key": "useQueryStringForAccessToken",
"value": JSON.stringify({ "v": false }),
},
{
"key": "enableCSRFProtection",
"value": JSON.stringify({ "v": true }),
}
],
"selfRegistration": true,
"domainWhitelist": [],
"autoEnrollGroups": await Promise.all(strategy.group_assignments
.map(x => group_identify(x))),
},
])));
return Promise.resolve(undefined);
}
logic.authentication_strategy_add = authentication_strategy_add;
/**
*/
async function theming_set(options = {}) {
options = Object.assign({
"dark_mode": false,
"toc_position": "left",
}, options);
const result = await _wiki_js_cli.api.call_theming_set(options.dark_mode, options.toc_position);
return Promise.resolve(result);
}
logic.theming_set = theming_set;
})(logic = _wiki_js_cli.logic || (_wiki_js_cli.logic = {}));
})(_wiki_js_cli || (_wiki_js_cli = {}));
var _wiki_js_cli;
(function (_wiki_js_cli) {
/**
*/
function parse_boolean(indicator) {
if ((indicator === "1")
||
(indicator === "yes")
||
(indicator === "on")
||
(indicator === "true")) {
return true;
}
else {
if ((indicator === "0")
||
(indicator === "no")
||
(indicator === "off")
||
(indicator === "false")) {
return false;
}
else {
throw (new Error("invalid boolean indicator: " + indicator));
}
}
}
/**
*/
async function main(args_raw) {
// args
const args = _wiki_js_cli.helpers.args.parse(args_raw);
const override_url_base = ((("b" in args.volatile)
&&
(args.volatile["b"].length >= 0))
?
args.volatile["b"][0]
:
null);
const override_username = ((("u" in args.volatile)
&&
(args.volatile["u"].length >= 0))
?
args.volatile["u"][0]
:
null);
const override_password = ((("p" in args.volatile)
&&
(args.volatile["p"].length >= 0))
?
args.volatile["p"][0]
:
null);
const override_log_level = ((("l" in args.volatile)
&&
(args.volatile["l"].length >= 0))
?
args.volatile["l"][0]
:
null);
// conf
const conf_path = ((("c" in args.volatile)
&&
(args.volatile["c"].length >= 0))
?
args.volatile["c"][0]
:
null);
await _wiki_js_cli.conf.load(conf_path);
_wiki_js_cli.helpers.log.write(_wiki_js_cli.helpers.log.enum_level.debug, "conf", _wiki_js_cli.conf.get());
// init
await _wiki_js_cli.helpers.log.setup(override_log_level ?? _wiki_js_cli.conf.get().log.threshold);
await _wiki_js_cli.api.init((override_url_base ?? _wiki_js_cli.conf.get().api.url_base));
// exec
if (args.positional.length < 1) {
return Promise.reject("SYNTAX: [node] cli.js [-c <conf-path>] [-b <api-url-base>] [-u <login-username>] [-p <login-password>] <action> [<arg-1> [<arg-2> […]]]\n\n\t<action> = init | email-settings-set | locale-add | group-add | group-modify | auth-strat-add-oauth2 | theming-set");
}
else {
const action = args.positional[0];
switch (action) {
default: {
return Promise.reject("invalid action: " + action);
break;
}
case "init": {
if (args.positional.length <= 2) {
return Promise.reject("SYNTAX: … init <admin-email-address> <admin-password> [<site-url> [<allow-telemetry>]]");
}
else {
const admin_email_address = args.positional[1];
const admin_password = args.positional[2];
const site_url = ((args.positional.length >= 4)
?
args.positional[3]
:
undefined);
const allow_telemetry = ((args.positional.length >= 5)
?
parse_boolean(args.positional[4])
:
undefined);
await _wiki_js_cli.logic.initialize(admin_email_address, admin_password, {
"site_url": site_url,
"allow_telemetry": allow_telemetry
});
return Promise.resolve(undefined);
}
break;
}
case "email-settings-set": {
if (args.positional.length <= 6) {
return Promise.reject("SYNTAX: … email-settings-set <smtp-host> <smtp-port> <smtp-username> <smtp-password> <sender-name> <sender-email-address>");
}
else {
const smtp_host = args.positional[1];
const smtp_port = parseInt(args.positional[2]);
const smtp_username = args.positional[3];
const smtp_password = args.positional[4];
const sender_name = args.positional[5];
const sender_email_address = args.positional[6];
await _wiki_js_cli.logic.login({
"username": override_username,
"password": override_password,
});
await _wiki_js_cli.logic.email_settings_set(smtp_host, smtp_port, smtp_username, smtp_password, sender_name, sender_email_address, {});
return Promise.resolve(undefined);
}
break;
}
case "locale-add": {
if (args.positional.length <= 1) {
return Promise.reject("SYNTAX: … locale-add <locale-code>");
}
else {
const locale_code = args.positional[1];
await _wiki_js_cli.logic.login({
"username": override_username,
"password": override_password,
});
await _wiki_js_cli.logic.locale_add(locale_code);
return Promise.resolve(undefined);
}
break;
}
case "group-add": {
if (args.positional.length <= 2) {
return Promise.reject("SYNTAX: … group-add <name> <permissions>");
}
else {
const name = args.positional[1];
const permissions = args.positional[2].split(",");
await _wiki_js_cli.logic.login({
"username": override_username,
"password": override_password,
});
const result = await _wiki_js_cli.logic.group_add(name, {
"permissions_general": permissions,
"permissions_specific": [
{
"id": "default",
"path": "",
"roles": permissions,
"match": "START",
"deny": false,
"locales": []
}
],
});
process.stdout.write(JSON.stringify(result, undefined, "\t")
+
"\n");
}
break;
}
case "group-modify": {
if (args.positional.length <= 2) {
return Promise.reject("SYNTAX: … group-modify <name> <permissions>");
}
else {
const name = args.positional[1];
const permissions = args.positional[2].split(",");
const id = await _wiki_js_cli.logic.group_identify(name);
await _wiki_js_cli.logic.login({
"username": override_username,
"password": override_password,
});
const result = await _wiki_js_cli.logic.group_modify(id, {
"permissions_general": permissions,
"permissions_specific": [
{
"id": "default",
"path": "",
"roles": permissions,
"match": "START",
"deny": false,
"locales": []
}
],
});
}
break;
}
case "auth-strat-add-oauth2": {
if (args.positional.length <= 8) {
return Promise.reject("SYNTAX: … auth-strat-add-oauth2 <strategy-key> <strategy-name> <strategy-client-id> <strategy-client-secret> <strategy-authorization-url> <strategy-token-url> <strategy-user-info-url> <group-assignments>");
}
else {
const strategy = {
"key": args.positional[1],
"name": args.positional[2],
"client_id": args.positional[3],
"client_secret": args.positional[4],
"authorization_url": args.positional[5],
"token_url": args.positional[6],
"user_info_url": args.positional[7],
"group_assignments": args.positional[8].split(","),
};
await _wiki_js_cli.logic.login({
"username": override_username,
"password": override_password,
});
await _wiki_js_cli.logic.authentication_strategy_add(strategy);
return Promise.resolve(undefined);
}
break;
}
case "theming-set": {
if (args.positional.length <= 1) {
return Promise.reject("SYNTAX: … theming-set <dark-mode> [<toc-position>]");
}
else {
const dark_mode = parse_boolean(args.positional[1]);
const toc_position_raw = ((args.positional.length <= 2)
?
"left"
:
args.positional[2]);
const toc_position = (() => {
switch (toc_position_raw) {
case "left": return "left";
case "right": return "right";
case "hidden": return "off";
default: {
return null;
}
}
})();
if (toc_position === null) {
return Promise.reject("invalid toc-position: " + toc_position_raw + "; valid values are: left,right,hidden");
}
else {
await _wiki_js_cli.logic.login({
"username": override_username,
"password": override_password,
});
await _wiki_js_cli.logic.theming_set({
"dark_mode": dark_mode,
"toc_position": toc_position,
});
return Promise.resolve(undefined);
}
}
break;
}
}
}
}
_wiki_js_cli.main = main;
})(_wiki_js_cli || (_wiki_js_cli = {}));
(_wiki_js_cli.main(process.argv.slice(2))
.then(() => {
})
.catch((reason) => {
process.stderr.write("-- " + String(reason) + "\n");
}));

19
roles/wiki_js/info.md Normal file
View file

@ -0,0 +1,19 @@
## Beschreibung
- Einrichtung der Aufgaben-Verwaltung [Wiki.js](https://js.wiki/)
## Verweise
- [Wiki.js-Dokumentation | Linux-Installation](https://docs.requarks.io/install/linux)
- [Wiki.js-Dokumentation | Konfiguration](https://docs.requarks.io/install/config)
## Anmerkungen
- `files/wiki-js-cli` stammt aus dem Projekt [wiki-js-cli](https://forgejo.linke.sx/misc/wiki-js-cli)
## ToDo
- start page

View file

@ -0,0 +1,193 @@
[
{
"name": "packages",
"become": true,
"ansible.builtin.apt": {
"update_cache": true,
"pkg": [
"npm"
]
}
},
{
"name": "user",
"become": true,
"ansible.builtin.user": {
"name": "{{var_wiki_js_user}}",
"create_home": true,
"home": "{{var_wiki_js_directory}}"
}
},
{
"name": "directories",
"become": true,
"loop": [
"{{var_wiki_js_data_path}}"
],
"ansible.builtin.file": {
"owner": "{{var_wiki_js_user}}",
"state": "directory",
"path": "{{item}}"
}
},
{
"name": "download",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.get_url": {
"url": "https://github.com/Requarks/wiki/releases/latest/download/wiki-js.tar.gz",
"dest": "/tmp/wiki_js.tar.gz"
}
},
{
"name": "unpack",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.unarchive": {
"remote_src": true,
"src": "/tmp/wiki_js.tar.gz",
"dest": "{{var_wiki_js_directory}}"
}
},
{
"name": "cli client",
"become": true,
"ansible.builtin.copy": {
"src": "wiki-js-cli",
"dest": "/usr/local/bin/wiki-js-cli",
"mode": "0700"
}
},
{
"name": "database | sqlite | dirctory",
"when": "var_wiki_js_database_kind == 'sqlite'",
"become": true,
"ansible.builtin.file": {
"path": "{{var_wiki_js_database_data_sqlite_path | dirname}}",
"state": "directory",
"owner": "{{var_wiki_js_user}}"
}
},
{
"name": "database | sqlite | file",
"when": "var_wiki_js_database_kind == 'sqlite'",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.file": {
"path": "{{var_wiki_js_database_data_sqlite_path}}",
"state": "touch"
}
},
{
"name": "conf",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.template": {
"src": "config.yml.j2",
"dest": "{{var_wiki_js_directory}}/config.yml"
}
},
{
"name": "database | sqlite | setup",
"when": "var_wiki_js_database_kind == 'sqlite'",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.command": {
"chdir": "{{var_wiki_js_directory}}",
"cmd": "npm rebuild sqlite3"
}
},
{
"name": "systemd-unit",
"become": true,
"ansible.builtin.template": {
"src": "systemd-unit.j2",
"dest": "/etc/systemd/system/wiki_js.service"
}
},
{
"name": "start service",
"become": true,
"ansible.builtin.systemd_service": {
"state": "restarted",
"enabled": true,
"name": "wiki_js"
}
},
{
"name": "setup | initialize",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.command": {
"chdir": "{{var_wiki_js_directory}}",
"cmd": "/usr/local/bin/wiki-js-cli -b http://127.0.0.1:{{var_wiki_js_port | string}} init {{var_wiki_js_admin_email_address}} {{var_wiki_js_admin_password}} https://{{var_wiki_js_domain}} no"
}
},
{
"name": "setup | email settings",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.command": {
"chdir": "{{var_wiki_js_directory}}",
"cmd": "/usr/local/bin/wiki-js-cli -b http://127.0.0.1:{{var_wiki_js_port | string}} -u {{var_wiki_js_admin_email_address}} -p {{var_wiki_js_admin_password}} email-settings-set {{var_wiki_js_smtp_host}} {{var_wiki_js_smtp_port}} {{var_wiki_js_smtp_username}} {{var_wiki_js_smtp_password}} {{email_sending_sender_name}} {{email_sending_sender_email_address}}"
}
},
{
"name": "setup | locales",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"loop": "{{var_wiki_js_additional_locales}}",
"ansible.builtin.command": {
"chdir": "{{var_wiki_js_directory}}",
"cmd": "/usr/local/bin/wiki-js-cli -b http://127.0.0.1:{{var_wiki_js_port | string}} -u {{var_wiki_js_admin_email_address}} -p {{var_wiki_js_admin_password}} locale-add {{item}}"
}
},
{
"name": "setup | set guest access | negative",
"when": "not var_wiki_js_allow_guest_view",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.command": {
"chdir": "{{var_wiki_js_directory}}",
"cmd": "/usr/local/bin/wiki-js-cli -b http://127.0.0.1:{{var_wiki_js_port | string}} -u {{var_wiki_js_admin_email_address}} -p {{var_wiki_js_admin_password}} group-modify Guests"
}
},
{
"name": "setup | set guest access | positive",
"when": "var_wiki_js_allow_guest_view",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.command": {
"chdir": "{{var_wiki_js_directory}}",
"cmd": "/usr/local/bin/wiki-js-cli -b http://127.0.0.1:{{var_wiki_js_port | string}} -u {{var_wiki_js_admin_email_address}} -p {{var_wiki_js_admin_password}} group-modify Guests read:pages read:assets read:comments"
}
},
{
"name": "setup | define user group",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.command": {
"chdir": "{{var_wiki_js_directory}}",
"cmd": "/usr/local/bin/wiki-js-cli -b http://127.0.0.1:{{var_wiki_js_port | string}} -u {{var_wiki_js_admin_email_address}} -p {{var_wiki_js_admin_password}} group-add {{var_wiki_js_user_group_name}} {{var_wiki_js_allow_guest_view | ternary('read:pages,read:assets,read:comments,write:comments,write:pages,manage:pages,delete:pages,write:styles,write:scripts,read:source,read:history,write:assets,manage:assets,manage:comments','')}}"
}
},
{
"name": "setup | authentication | authelia",
"when": "var_wiki_js_authentication_kind == 'authelia'",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.command": {
"chdir": "{{var_wiki_js_directory}}",
"cmd": "/usr/local/bin/wiki-js-cli -b http://127.0.0.1:{{var_wiki_js_port | string}} -u {{var_wiki_js_admin_email_address}} -p {{var_wiki_js_admin_password}} auth-strat-add-oauth2 {{var_wiki_js_authentication_data_authelia_provider_id}} {{var_wiki_js_authentication_data_authelia_provider_name}} {{var_wiki_js_authentication_data_authelia_client_id}} {{var_wiki_js_authentication_data_authelia_client_secret}} {{var_wiki_js_authentication_data_authelia_url_base}}/api/oidc/authorization {{var_wiki_js_authentication_data_authelia_url_base}}/api/oidc/token {{var_wiki_js_authentication_data_authelia_url_base}}/api/oidc/userinfo {{var_wiki_js_user_group_name}}"
}
},
{
"name": "setup | theming",
"become": true,
"become_user": "{{var_wiki_js_user}}",
"ansible.builtin.command": {
"chdir": "{{var_wiki_js_directory}}",
"cmd": "/usr/local/bin/wiki-js-cli -b http://127.0.0.1:{{var_wiki_js_port | string}} -u {{var_wiki_js_admin_email_address}} -p {{var_wiki_js_admin_password}} theming-set {{var_wiki_js_dark_mode | ternary('yes','no')}} {{var_wiki_js_toc_position}}"
}
}
]

View file

@ -0,0 +1,49 @@
port: {{var_wiki_js_port | string}}
{% if var_wiki_js_database_kind == 'sqlite' %}
db:
type: sqlite
storage: {{var_wiki_js_database_data_sqlite_path}}
{% endif %}
{% if var_wiki_js_database_kind == 'postgresql' %}
db:
type: postgres
host: {{var_wiki_js_database_data_postgresql_host}}
port: {{var_wiki_js_database_data_postgresql_port | to_yaml}}
user: {{var_wiki_js_database_data_postgresql_username}}
pass: {{var_wiki_js_database_data_postgresql_password}}
db: {{var_wiki_js_database_data_postgresql_schema}}
ssl: false
# schema: public
{% endif %}
ssl:
enabled: false
port: 3443
# Provider to use, possible values: custom, letsencrypt
provider: custom
format: pem
key: path/to/key.pem
cert: path/to/cert.pem
pfx: path/to/cert.pfx
passphrase: null
dhparam: null
domain: wiki.yourdomain.com
subscriberEmail: admin@example.com
bindIP: 0.0.0.0
logLevel: {{var_wiki_js_log_level}}
logFormat: {{var_wiki_js_log_format}}
offline: false
ha: {{var_wiki_js_distributed | to_yaml}}
dataPath: {{var_wiki_js_data_path}}
bodyParserLimit: 5mb

View file

@ -0,0 +1,14 @@
[Unit]
Description=Wiki.js
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/node server
Restart=always
User={{var_wiki_js_user}}
Environment=NODE_ENV=production
WorkingDirectory={{var_wiki_js_directory}}
[Install]
WantedBy=multi-user.target

162
roles/wiki_js/vardef.json Normal file
View file

@ -0,0 +1,162 @@
{
"port": {
"mandatory": false,
"type": "integer"
},
"distributed": {
"mandatory": false,
"type": "boolean"
},
"domain": {
"mandatory": false,
"type": "string"
},
"user": {
"mandatory": false,
"type": "string"
},
"directory": {
"mandatory": false,
"type": "string"
},
"log_level": {
"mandatory": false,
"type": "string",
"options": [
"error",
"warn",
"info",
"verbose",
"debug",
"silly"
]
},
"log_format": {
"mandatory": false,
"type": "string",
"options": [
"default",
"json"
]
},
"data_path": {
"mandatory": false,
"type": "string"
},
"database_kind": {
"mandatory": false,
"type": "string",
"options": [
"sqlite",
"postgresql"
]
},
"database_data_sqlite_path": {
"mandatory": false,
"type": "string"
},
"database_data_postgresql_host": {
"mandatory": false,
"type": "string"
},
"database_data_postgresql_port": {
"mandatory": false,
"type": "integer"
},
"database_data_postgresql_username": {
"mandatory": false,
"type": "string"
},
"database_data_postgresql_password": {
"mandatory": false,
"type": "string"
},
"database_data_postgresql_schema": {
"mandatory": false,
"type": "string"
},
"authentication_kind": {
"mandatory": false,
"type": "string",
"options": [
"internal",
"authelia"
]
},
"authentication_data_authelia_provider_id": {
"type": "string",
"mandatory": false
},
"authentication_data_authelia_provider_name": {
"type": "string",
"mandatory": false
},
"authentication_data_authelia_client_id": {
"type": "string",
"mandatory": false
},
"authentication_data_authelia_client_secret": {
"type": "string",
"mandatory": false
},
"authentication_data_authelia_url_base": {
"type": "string",
"mandatory": false
},
"smtp_host": {
"mandatory": false,
"type": "string"
},
"smtp_port": {
"mandatory": false,
"type": "integer"
},
"smtp_username": {
"mandatory": false,
"type": "string"
},
"smtp_password": {
"mandatory": false,
"type": "string"
},
"email_sending_sender_name": {
"mandatory": false,
"type": "string"
},
"email_sending_sender_email_address": {
"mandatory": false,
"type": "string"
},
"admin_email_address": {
"mandatory": false,
"type": "string"
},
"admin_password": {
"mandatory": false,
"type": "string"
},
"additional_locales": {
"mandatory": false,
"type": "array",
"items": {
"nullable": false,
"type": "string"
}
},
"user_group_name": {
"mandatory": false,
"type": "string"
},
"allow_guest_view": {
"mandatory": false,
"type": "boolean"
},
"dark_mode": {
"mandatory": false,
"type": "boolean"
},
"toc_position": {
"mandatory": false,
"type": "string"
}
}