Compare commits

...
7 Commits
21 changed files with 139 additions and 29 deletions
@@ -2,6 +2,11 @@ name: Build and push Docker DEV image
on: on:
push: push:
paths-ignore:
- '.github/**'
- 'charts/**'
- 'docker-*.sh'
- '*.md'
branches: [master] branches: [master]
jobs: jobs:
+3 -4
View File
@@ -20,10 +20,9 @@
## How to deploy Planka ## How to deploy Planka
There are 2 types of installation: There are many ways to install Planka
- [Without Docker](https://docs.planka.cloud/docs/installl-planka/Debian%20&%20Ubuntu) ([for Windows](https://docs.planka.cloud/docs/installl-planka/Windows))
- [Dockerized](https://docs.planka.cloud/docs/installl-planka/Docker%20Compose) [Check them out](https://docs.planka.cloud/docs/intro)
- [Automated installation](https://github.com/plankanban/planka-installer)
For configuration, please see the [configuration section](https://docs.planka.cloud/docs/category/configuration). For configuration, please see the [configuration section](https://docs.planka.cloud/docs/category/configuration).
+2 -2
View File
@@ -15,13 +15,13 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes # This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version. # to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/) # Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.7 version: 0.1.9
# This is the version number of the application being deployed. This version number should be # This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to # incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using. # follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes. # It is recommended to use it with quotes.
appVersion: "1.14.2" appVersion: "1.14.3"
dependencies: dependencies:
- alias: postgresql - alias: postgresql
+23 -1
View File
@@ -21,7 +21,11 @@ git clone https://github.com/Chris-Greaves/planka-helm-chart.git
cd planka-helm-chart cd planka-helm-chart
helm dependency build helm dependency build
export SECRETKEY=$(openssl rand -hex 64) export SECRETKEY=$(openssl rand -hex 64)
helm install planka . --set secretkey=$SECRETKEY helm install planka . --set secretkey=$SECRETKEY \
--set admin_email="demo@demo.demo" \
--set admin_password="demo" \
--set admin_name="Demo Demo" \
--set admin_username="demo"
``` ```
> **NOTE:** The command `openssl rand -hex 64` is needed to create a random hexadecimal key for planka. On Windows you can use Git Bash to run that command. > **NOTE:** The command `openssl rand -hex 64` is needed to create a random hexadecimal key for planka. On Windows you can use Git Bash to run that command.
@@ -39,11 +43,19 @@ To access Planka externally you can use the following configuration
```bash ```bash
# HTTP only # HTTP only
helm install planka . --set secretkey=$SECRETKEY \ helm install planka . --set secretkey=$SECRETKEY \
--set admin_email="demo@demo.demo" \
--set admin_password="demo" \
--set admin_name="Demo Demo" \
--set admin_username="demo"
--set ingress.enabled=true \ --set ingress.enabled=true \
--set ingress.hosts[0].host=planka.example.dev \ --set ingress.hosts[0].host=planka.example.dev \
# HTTPS # HTTPS
helm install planka . --set secretkey=$SECRETKEY \ helm install planka . --set secretkey=$SECRETKEY \
--set admin_email="demo@demo.demo" \
--set admin_password="demo" \
--set admin_name="Demo Demo" \
--set admin_username="demo"
--set ingress.enabled=true \ --set ingress.enabled=true \
--set ingress.hosts[0].host=planka.example.dev \ --set ingress.hosts[0].host=planka.example.dev \
--set ingress.tls[0].secretName=planka-tls \ --set ingress.tls[0].secretName=planka-tls \
@@ -54,6 +66,16 @@ or create a values.yaml file like:
```yaml ```yaml
secretkey: "<InsertSecretKey>" secretkey: "<InsertSecretKey>"
# The admin section needs to be present for new instances of Planka, after the first start you can remove the lines starting with admin_. If you want the admin user to be unchangeable admin_email: has to stay
# After changing the config you have to run ```helm upgrade planka . -f values.yaml```
# Admin user
admin_email: "demo@demo.demo" # Do not remove if you want to prevent this user from being edited/deleted
admin_password: "demo"
admin_name: "Demo Demo"
admin_username: "demo"
# Admin user
ingress: ingress:
enabled: true enabled: true
hosts: hosts:
+8
View File
@@ -75,6 +75,14 @@ spec:
value: {{ required "A secret key needs to be generated using 'openssl rand -hex 64' and assigned to secretkey." .Values.secretkey }} value: {{ required "A secret key needs to be generated using 'openssl rand -hex 64' and assigned to secretkey." .Values.secretkey }}
- name: TRUST_PROXY - name: TRUST_PROXY
value: "0" value: "0"
- name: DEFAULT_ADMIN_EMAIL
value: {{ .Values.admin_email }}
- name: DEFAULT_ADMIN_PASSWORD
value: {{ .Values.admin_password }}
- name: DEFAULT_ADMIN_NAME
value: {{ .Values.admin_name }}
- name: DEFAULT_ADMIN_USERNAME
value: {{ .Values.admin_username }}
{{ range $k, $v := .Values.env }} {{ range $k, $v := .Values.env }}
- name: {{ $k | quote }} - name: {{ $k | quote }}
value: {{ $v | quote }} value: {{ $v | quote }}
+1 -1
View File
@@ -1 +1 @@
REACT_APP_VERSION=1.14.2 REACT_APP_VERSION=1.14.3
@@ -155,7 +155,7 @@ const ActionsStep = React.memo(
</Menu.Item> </Menu.Item>
</> </>
)} )}
{!user.isLockedAdmin && ( {!user.isDeletionLocked && (
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}> <Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
{t('action.deleteUser', { {t('action.deleteUser', {
context: 'title', context: 'title',
@@ -18,7 +18,8 @@ const Item = React.memo(
phone, phone,
isAdmin, isAdmin,
isLocked, isLocked,
isLockedAdmin, isRoleLocked,
isDeletionLocked,
emailUpdateForm, emailUpdateForm,
passwordUpdateForm, passwordUpdateForm,
usernameUpdateForm, usernameUpdateForm,
@@ -48,7 +49,7 @@ const Item = React.memo(
<Table.Cell>{username || '-'}</Table.Cell> <Table.Cell>{username || '-'}</Table.Cell>
<Table.Cell>{email}</Table.Cell> <Table.Cell>{email}</Table.Cell>
<Table.Cell> <Table.Cell>
<Radio toggle checked={isAdmin} disabled={isLockedAdmin} onChange={handleIsAdminChange} /> <Radio toggle checked={isAdmin} disabled={isRoleLocked} onChange={handleIsAdminChange} />
</Table.Cell> </Table.Cell>
<Table.Cell textAlign="right"> <Table.Cell textAlign="right">
<ActionsPopup <ActionsPopup
@@ -60,7 +61,7 @@ const Item = React.memo(
phone, phone,
isAdmin, isAdmin,
isLocked, isLocked,
isLockedAdmin, isDeletionLocked,
emailUpdateForm, emailUpdateForm,
passwordUpdateForm, passwordUpdateForm,
usernameUpdateForm, usernameUpdateForm,
@@ -93,7 +94,8 @@ Item.propTypes = {
phone: PropTypes.string, phone: PropTypes.string,
isAdmin: PropTypes.bool.isRequired, isAdmin: PropTypes.bool.isRequired,
isLocked: PropTypes.bool.isRequired, isLocked: PropTypes.bool.isRequired,
isLockedAdmin: PropTypes.bool.isRequired, isRoleLocked: PropTypes.bool.isRequired,
isDeletionLocked: PropTypes.bool.isRequired,
/* eslint-disable react/forbid-prop-types */ /* eslint-disable react/forbid-prop-types */
emailUpdateForm: PropTypes.object.isRequired, emailUpdateForm: PropTypes.object.isRequired,
passwordUpdateForm: PropTypes.object.isRequired, passwordUpdateForm: PropTypes.object.isRequired,
@@ -111,7 +111,8 @@ const UsersModal = React.memo(
phone={item.phone} phone={item.phone}
isAdmin={item.isAdmin} isAdmin={item.isAdmin}
isLocked={item.isLocked} isLocked={item.isLocked}
isLockedAdmin={item.isLockedAdmin} isRoleLocked={item.isRoleLocked}
isDeletionLocked={item.isDeletionLocked}
emailUpdateForm={item.emailUpdateForm} emailUpdateForm={item.emailUpdateForm}
passwordUpdateForm={item.passwordUpdateForm} passwordUpdateForm={item.passwordUpdateForm}
usernameUpdateForm={item.usernameUpdateForm} usernameUpdateForm={item.usernameUpdateForm}
+2 -1
View File
@@ -45,7 +45,8 @@ export default class extends BaseModel {
subscribeToOwnCards: attr(), subscribeToOwnCards: attr(),
isAdmin: attr(), isAdmin: attr(),
isLocked: attr(), isLocked: attr(),
isLockedAdmin: attr(), isRoleLocked: attr(),
isDeletionLocked: attr(),
deletedAt: attr(), deletedAt: attr(),
createdAt: attr({ createdAt: attr({
getDefault: () => new Date(), getDefault: () => new Date(),
+16
View File
@@ -0,0 +1,16 @@
version: '3'
services:
postgres:
image: postgres:alpine
restart: unless-stopped
volumes:
- db-data:/var/lib/postgresql/data
ports:
- 5432:5432
environment:
- POSTGRES_DB=planka
- POSTGRES_HOST_AUTH_METHOD=trust
volumes:
db-data:
+52 -3
View File
@@ -1,16 +1,65 @@
version: '3' version: '3'
services: services:
planka:
image: ghcr.io/plankanban/planka:dev
command: >
bash -c
"for i in `seq 1 30`; do
./start.sh &&
s=$$? && break || s=$$?;
echo \"Tried $$i times. Waiting 5 seconds...\";
sleep 5;
done; (exit $$s)"
restart: unless-stopped
volumes:
- user-avatars:/app/public/user-avatars
- project-background-images:/app/public/project-background-images
- attachments:/app/private/attachments
ports:
- 3000:1337
environment:
- BASE_URL=http://localhost:3000
- DATABASE_URL=postgresql://postgres@postgres/planka
- SECRET_KEY=notsecretkey
# - TRUST_PROXY=0
# - TOKEN_EXPIRES_IN=365 # In days
# related: https://github.com/knex/knex/issues/2354
# As knex does not pass query parameters from the connection string we
# have to use environment variables in order to pass the desired values, e.g.
# - PGSSLMODE=<value>
# Configure knex to accept SSL certificates
# - KNEX_REJECT_UNAUTHORIZED_SSL_CERTIFICATE=false
# - DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted
# - DEFAULT_ADMIN_PASSWORD=demo
# - DEFAULT_ADMIN_NAME=Demo Demo
# - DEFAULT_ADMIN_USERNAME=demo
# - OIDC_ISSUER=
# - OIDC_CLIENT_ID=
# - OIDC_CLIENT_SECRET=
# - OIDC_SCOPES=openid email profile
# - OIDC_ADMIN_ROLES=admin
# - OIDC_ROLES_ATTRIBUTE=groups
# - OIDC_IGNORE_ROLES=true
depends_on:
- postgres
postgres: postgres:
image: postgres:alpine image: postgres:14-alpine
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- db-data:/var/lib/postgresql/data - db-data:/var/lib/postgresql/data
ports:
- 5432:5432
environment: environment:
- POSTGRES_DB=planka - POSTGRES_DB=planka
- POSTGRES_HOST_AUTH_METHOD=trust - POSTGRES_HOST_AUTH_METHOD=trust
volumes: volumes:
user-avatars:
project-background-images:
attachments:
db-data: db-data:
+1
View File
@@ -45,6 +45,7 @@ services:
# - OIDC_SCOPES=openid email profile # - OIDC_SCOPES=openid email profile
# - OIDC_ADMIN_ROLES=admin # - OIDC_ADMIN_ROLES=admin
# - OIDC_ROLES_ATTRIBUTE=groups # - OIDC_ROLES_ATTRIBUTE=groups
# - OIDC_IGNORE_ROLES=true
depends_on: depends_on:
- postgres - postgres
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "planka", "name": "planka",
"version": "1.14.2", "version": "1.14.3",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "planka", "name": "planka",
"version": "1.14.2", "version": "1.14.3",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "planka", "name": "planka",
"version": "1.14.2", "version": "1.14.3",
"private": true, "private": true,
"homepage": "https://plankanban.github.io/planka", "homepage": "https://plankanban.github.io/planka",
"repository": { "repository": {
+1
View File
@@ -28,6 +28,7 @@ SECRET_KEY=notsecretkey
# OIDC_SCOPES=openid email profile # OIDC_SCOPES=openid email profile
# OIDC_ADMIN_ROLES=admin # OIDC_ADMIN_ROLES=admin
# OIDC_ROLES_ATTRIBUTE=groups # OIDC_ROLES_ATTRIBUTE=groups
# OIDC_IGNORE_ROLES=true
## Do not edit this ## Do not edit this
+4
View File
@@ -73,6 +73,10 @@ module.exports = {
delete inputs.name; delete inputs.name;
/* eslint-enable no-param-reassign */ /* eslint-enable no-param-reassign */
} else if (user.isSso) { } else if (user.isSso) {
if (!sails.config.custom.oidcIgnoreRoles) {
delete inputs.isAdmin; // eslint-disable-line no-param-reassign
}
delete inputs.name; // eslint-disable-line no-param-reassign delete inputs.name; // eslint-disable-line no-param-reassign
} }
@@ -90,7 +90,10 @@ module.exports = {
}); });
} }
const updateFieldKeys = ['email', 'isAdmin', 'isSso', 'name', 'username']; const updateFieldKeys = ['email', 'isSso', 'name', 'username'];
if (!sails.config.custom.oidcIgnoreRoles) {
updateFieldKeys.push('isAdmin');
}
const updateValues = {}; const updateValues = {};
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
+4 -3
View File
@@ -110,12 +110,13 @@ module.exports = {
tableName: 'user_account', tableName: 'user_account',
customToJSON() { customToJSON() {
const isLockedAdmin = this.email === sails.config.custom.defaultAdminEmail; const isDefaultAdmin = this.email === sails.config.custom.defaultAdminEmail;
return { return {
..._.omit(this, ['password', 'isSso', 'avatar', 'passwordChangedAt']), ..._.omit(this, ['password', 'isSso', 'avatar', 'passwordChangedAt']),
isLockedAdmin, isLocked: this.isSso || isDefaultAdmin,
isLocked: this.isSso || isLockedAdmin, isRoleLocked: (this.isSso && !sails.config.custom.oidcIgnoreRoles) || isDefaultAdmin,
isDeletionLocked: isDefaultAdmin,
avatarUrl: avatarUrl:
this.avatar && this.avatar &&
`${sails.config.custom.userAvatarsUrl}/${this.avatar.dirname}/square-100.${this.avatar.extension}`, `${sails.config.custom.userAvatarsUrl}/${this.avatar.dirname}/square-100.${this.avatar.extension}`,
+1
View File
@@ -39,6 +39,7 @@ module.exports.custom = {
oidcScopes: process.env.OIDC_SCOPES || 'openid email profile', oidcScopes: process.env.OIDC_SCOPES || 'openid email profile',
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [], oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [],
oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE || 'groups', oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE || 'groups',
oidcIgnoreRoles: process.env.OIDC_IGNORE_ROLES === 'true',
// TODO: move client base url to environment variable? // TODO: move client base url to environment variable?
oidcRedirectUri: `${ oidcRedirectUri: `${
-4
View File
@@ -34,10 +34,6 @@ exports.seed = async (knex) => {
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
}); });
} catch (error) { } catch (error) {
if (Object.keys(data).length === 0) {
return;
}
await knex('user_account').update(data).where('email', process.env.DEFAULT_ADMIN_EMAIL); await knex('user_account').update(data).where('email', process.env.DEFAULT_ADMIN_EMAIL);
} }
}; };