Compare commits

..
Author SHA1 Message Date
Maksim Eltyshev 04f52a63f2 chore: Update version 2024-07-17 14:03:00 +02:00
Maksim Eltyshev 6b16a104b1 docs: Update docker pulls counter 2024-07-16 12:39:57 +02:00
Maksim Eltyshev aff853c602 ref: Refactoring, fix linting 2024-07-16 12:33:38 +02:00
aleb_the_flash ad2966c5d6 feat: Improve OIDC support for strict providers (#824) 2024-07-16 12:19:27 +02:00
Maksim Eltyshev 8d74cc1732 chore: Update version 2024-07-15 16:24:31 +02:00
Emmanuel Guyot ea94fe8bf3 fix: Order users by name (#806) 2024-07-15 15:26:26 +02:00
Αλέξανδρος 6052f8999f fix: Fix application crashing when webhook is unavailable (#815) 2024-07-15 14:40:33 +02:00
ItzAndriss 67d1b1c194 fix: Update Hungarian translation (#823) 2024-07-15 14:25:31 +02:00
Stephan Michard b3cb8405f5 fix: Update German translation (#821) 2024-07-15 14:19:06 +02:00
Maksim Eltyshev 7acaec6d04 fix: Fix incorrect related data when transferring card
Closes #431, closes #803
2024-07-15 14:01:06 +02:00
Maksim Eltyshev 72896a66b3 chore: Update version 2024-06-21 12:11:15 +02:00
Maksim Eltyshev 4be4783251 fix: Fix icon spacing for action buttons 2024-06-21 12:01:46 +02:00
HannesOberreiter 5c6c6a0bcf feat: Add copy link action to card modal (#804) 2024-06-21 11:48:36 +02:00
HannesOberreiter ba178d66fa docs: Add types to webhook function (#796) 2024-06-18 15:41:12 +02:00
HannesOberreiter 270ce29f87 feat: Add custom user agent with base url to webhook (#799) 2024-06-18 15:36:51 +02:00
HannesOberreiter de1ed3c784 ref: Define all possible event types in webhook module (#795) 2024-06-18 15:32:39 +02:00
Maksim Eltyshev caf8790f28 chore: Update version 2024-06-14 17:18:42 +02:00
Sarah Soo 81436b373d feat: Ability to specify existing secrets for values in Helm (#791)
Closes #790
2024-06-14 16:41:10 +02:00
NathanVss cc1e886a31 feat: Ability to allow everyone to create projects (#787) 2024-06-14 16:38:06 +02:00
Maksim Eltyshev f0e9fe7904 chore: Update version 2024-06-12 09:06:20 +02:00
Gavin Mogan 5fcea5d651 fix: Handle WEBHOOKS env variable being unset (#785)
Closes #784
2024-06-12 08:59:36 +02:00
34 changed files with 348 additions and 66 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
# Planka # Planka
#### Elegant open source project tracking. #### Elegant open source project tracking.
![David (path)](https://img.shields.io/github/package-json/v/plankanban/planka) ![Docker Pulls](https://img.shields.io/docker/pulls/meltyshev/planka) ![GitHub](https://img.shields.io/github/license/plankanban/planka) ![David (path)](https://img.shields.io/github/package-json/v/plankanban/planka) ![Docker Pulls](https://img.shields.io/badge/docker_pulls-4M%2B-%23066da5) ![GitHub](https://img.shields.io/github/license/plankanban/planka)
![](https://raw.githubusercontent.com/plankanban/planka/master/demo.gif) ![](https://raw.githubusercontent.com/plankanban/planka/master/demo.gif)
+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.34 version: 0.2.4
# 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.18.0" appVersion: "1.20.0"
dependencies: dependencies:
- alias: postgresql - alias: postgresql
+22 -2
View File
@@ -90,17 +90,37 @@ spec:
value: http://localhost:3000 value: http://localhost:3000
{{- end }} {{- end }}
- name: SECRET_KEY - name: SECRET_KEY
{{- if .Values.existingSecretkeySecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.existingSecretkeySecret }}
key: key
{{- else }}
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 }}
{{- end }}
- name: TRUST_PROXY - name: TRUST_PROXY
value: "0" value: "0"
- name: DEFAULT_ADMIN_EMAIL - name: DEFAULT_ADMIN_EMAIL
value: {{ .Values.admin_email }} value: {{ .Values.admin_email }}
- name: DEFAULT_ADMIN_PASSWORD
value: {{ .Values.admin_password }}
- name: DEFAULT_ADMIN_NAME - name: DEFAULT_ADMIN_NAME
value: {{ .Values.admin_name }} value: {{ .Values.admin_name }}
{{- if .Values.existingAdminCredsSecret }}
- name: DEFAULT_ADMIN_USERNAME
valueFrom:
secretKeyRef:
name: {{ .Values.existingAdminCredsSecret }}
key: username
- name: DEFAULT_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.existingAdminCredsSecret }}
key: password
{{- else }}
- name: DEFAULT_ADMIN_USERNAME - name: DEFAULT_ADMIN_USERNAME
value: {{ .Values.admin_username }} value: {{ .Values.admin_username }}
- name: DEFAULT_ADMIN_PASSWORD
value: {{ .Values.admin_password }}
{{- end }}
{{ range $k, $v := .Values.env }} {{ range $k, $v := .Values.env }}
- name: {{ $k | quote }} - name: {{ $k | quote }}
value: {{ $v | quote }} value: {{ $v | quote }}
+10
View File
@@ -17,6 +17,16 @@ fullnameOverride: ""
# Generate a secret using openssl rand -base64 45 # Generate a secret using openssl rand -base64 45
secretkey: "" secretkey: ""
## @param existingSecretkeySecret Name of an existing secret containing the session key string
## NOTE: Must contain key `key`
## NOTE: When it's set, the secretkey parameter is ignored
existingSecretkeySecret: ""
## @param existingAdminCredsSecret Name of an existing secret containing the admin username and password
## NOTE: Must contain keys `username` and `password`
## NOTE: When it's set, the `admin_username` and `admin_password` parameters are ignored
existingAdminCredsSecret: ""
# Base url for Planka. Will override `ingress.hosts[0].host` # Base url for Planka. Will override `ingress.hosts[0].host`
# Defaults to `http://localhost:3000` if ingress is disabled. # Defaults to `http://localhost:3000` if ingress is disabled.
baseUrl: "" baseUrl: ""
+1 -1
View File
@@ -1 +1 @@
REACT_APP_VERSION=1.18.0 REACT_APP_VERSION=1.20.0
+6 -1
View File
@@ -57,10 +57,15 @@ updateCard.failure = (id, error) => ({
}, },
}); });
const handleCardUpdate = (card) => ({ const handleCardUpdate = (card, isFetched, cardMemberships, cardLabels, tasks, attachments) => ({
type: ActionTypes.CARD_UPDATE_HANDLE, type: ActionTypes.CARD_UPDATE_HANDLE,
payload: { payload: {
card, card,
isFetched,
cardMemberships,
cardLabels,
tasks,
attachments,
}, },
}); });
+18 -1
View File
@@ -1,4 +1,4 @@
import React, { useCallback, useRef } from 'react'; import React, { useCallback, useRef, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -81,6 +81,7 @@ const CardModal = React.memo(
onClose, onClose,
}) => { }) => {
const [t] = useTranslation(); const [t] = useTranslation();
const [isLinkCopied, setIsLinkCopied] = useState(false);
const isGalleryOpened = useRef(false); const isGalleryOpened = useRef(false);
@@ -146,6 +147,14 @@ const CardModal = React.memo(
onClose(); onClose();
}, [onDuplicate, onClose]); }, [onDuplicate, onClose]);
const handleCopyLinkClick = useCallback(() => {
navigator.clipboard.writeText(window.location.href);
setIsLinkCopied(true);
setTimeout(() => {
setIsLinkCopied(false);
}, 5000);
}, []);
const handleGalleryOpen = useCallback(() => { const handleGalleryOpen = useCallback(() => {
isGalleryOpened.current = true; isGalleryOpened.current = true;
}, []); }, []);
@@ -506,6 +515,14 @@ const CardModal = React.memo(
<Icon name="copy outline" className={styles.actionIcon} /> <Icon name="copy outline" className={styles.actionIcon} />
{t('action.duplicate')} {t('action.duplicate')}
</Button> </Button>
<Button fluid className={styles.actionButton} onClick={handleCopyLinkClick}>
<Icon name={isLinkCopied ? 'linkify' : 'unlink'} className={styles.actionIcon} />
{isLinkCopied
? t('common.linkIsCopied')
: t('action.copyLink', {
context: 'title',
})}
</Button>
<DeletePopup <DeletePopup
title="common.deleteCard" title="common.deleteCard"
content="common.areYouSureYouWantToDeleteThisCard" content="common.areYouSureYouWantToDeleteThisCard"
@@ -20,7 +20,6 @@
.actionIcon { .actionIcon {
color: #17394d; color: #17394d;
display: inline;
margin-right: 8px; margin-right: 8px;
} }
-3
View File
@@ -195,9 +195,6 @@ export default {
CARD_UPDATE__SUCCESS: 'CARD_UPDATE__SUCCESS', CARD_UPDATE__SUCCESS: 'CARD_UPDATE__SUCCESS',
CARD_UPDATE__FAILURE: 'CARD_UPDATE__FAILURE', CARD_UPDATE__FAILURE: 'CARD_UPDATE__FAILURE',
CARD_UPDATE_HANDLE: 'CARD_UPDATE_HANDLE', CARD_UPDATE_HANDLE: 'CARD_UPDATE_HANDLE',
CARD_TRANSFER: 'CARD_TRANSFER',
CARD_TRANSFER__SUCCESS: 'CARD_TRANSFER__SUCCESS',
CARD_TRANSFER__FAILURE: 'CARD_TRANSFER__FAILURE',
CARD_DUPLICATE: 'CARD_DUPLICATE', CARD_DUPLICATE: 'CARD_DUPLICATE',
CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS', CARD_DUPLICATE__SUCCESS: 'CARD_DUPLICATE__SUCCESS',
CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE', CARD_DUPLICATE__FAILURE: 'CARD_DUPLICATE__FAILURE',
+2 -1
View File
@@ -6,12 +6,13 @@ import entryActions from '../entry-actions';
import Projects from '../components/Projects'; import Projects from '../components/Projects';
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
const { allowAllToCreateProjects } = selectors.selectConfig(state);
const { isAdmin } = selectors.selectCurrentUser(state); const { isAdmin } = selectors.selectCurrentUser(state);
const projects = selectors.selectProjectsForCurrentUser(state); const projects = selectors.selectProjectsForCurrentUser(state);
return { return {
items: projects, items: projects,
canAdd: isAdmin, canAdd: allowAllToCreateProjects || isAdmin,
}; };
}; };
+4
View File
@@ -104,6 +104,7 @@ export default {
language: 'Sprache', language: 'Sprache',
leaveBoard_title: 'Board verlassen', leaveBoard_title: 'Board verlassen',
leaveProject_title: 'Projekt verlassen', leaveProject_title: 'Projekt verlassen',
linkIsCopied: 'Link kopiert',
list: 'Listen', list: 'Listen',
listActions_title: 'Aufgaben auflisten', listActions_title: 'Aufgaben auflisten',
managers: 'Manager', managers: 'Manager',
@@ -169,6 +170,7 @@ export default {
addTask: 'Aufgabe hinzufügen', addTask: 'Aufgabe hinzufügen',
addToCard: 'Zu Karte hinzufügen', addToCard: 'Zu Karte hinzufügen',
addUser: 'Benutzer hinzufügen', addUser: 'Benutzer hinzufügen',
copyLink_title: 'Link kopieren',
createBoard: 'Board erstellen', createBoard: 'Board erstellen',
createFile: 'Datei erstellen', createFile: 'Datei erstellen',
createLabel: 'Label erstellen', createLabel: 'Label erstellen',
@@ -190,6 +192,8 @@ export default {
deleteTask: 'Aufgabe löschen', deleteTask: 'Aufgabe löschen',
deleteTask_title: 'Aufgabe löschen', deleteTask_title: 'Aufgabe löschen',
deleteUser: 'Benutzer löschen', deleteUser: 'Benutzer löschen',
duplicate: 'Kopieren',
duplicateCard_title: 'Karte kopieren',
edit: 'Bearbeiten', edit: 'Bearbeiten',
editDueDate_title: 'Fälligkeitsdatum bearbeiten', editDueDate_title: 'Fälligkeitsdatum bearbeiten',
editDescription_title: 'Beschreibung ändern', editDescription_title: 'Beschreibung ändern',
+2
View File
@@ -106,6 +106,7 @@ export default {
language: 'Language', language: 'Language',
leaveBoard_title: 'Leave Board', leaveBoard_title: 'Leave Board',
leaveProject_title: 'Leave Project', leaveProject_title: 'Leave Project',
linkIsCopied: 'Link is copied',
list: 'List', list: 'List',
listActions_title: 'List Actions', listActions_title: 'List Actions',
managers: 'Managers', managers: 'Managers',
@@ -183,6 +184,7 @@ export default {
addTask: 'Add task', addTask: 'Add task',
addToCard: 'Add to card', addToCard: 'Add to card',
addUser: 'Add user', addUser: 'Add user',
copyLink_title: 'Copy Link',
createBoard: 'Create board', createBoard: 'Create board',
createFile: 'Create file', createFile: 'Create file',
createLabel: 'Create label', createLabel: 'Create label',
+27 -4
View File
@@ -15,6 +15,16 @@ export default {
translation: { translation: {
common: { common: {
aboutPlanka: 'Plankáról',
account: 'Fiók',
actions: 'Műveletek',
addAttachment_title: 'Melléklet hozzáadása',
addComment: 'Megjegyzés hozzáadása',
addManager_title: 'Menedzser hozzáadása',
addMember_title: 'Tag létrehozása',
addUser_title: 'Felhasználó hozzáadása',
administrator: 'Rendszergazda',
all: 'Összes',
allChangesWillBeAutomaticallySavedAfterConnectionRestored: allChangesWillBeAutomaticallySavedAfterConnectionRestored:
'Az összes változás automatikusan mentésre kerül a kapcsolat helyreállása után.', 'Az összes változás automatikusan mentésre kerül a kapcsolat helyreállása után.',
areYouSureYouWantToDeleteThisAttachment: 'Biztosan törölni szeretné ezt a mellékletet?', areYouSureYouWantToDeleteThisAttachment: 'Biztosan törölni szeretné ezt a mellékletet?',
@@ -29,7 +39,7 @@ export default {
areYouSureYouWantToLeaveBoard: 'Biztosan el akarja hagyni a táblát?', areYouSureYouWantToLeaveBoard: 'Biztosan el akarja hagyni a táblát?',
areYouSureYouWantToLeaveProject: 'Biztosan el akarja hagyni a projektet?', areYouSureYouWantToLeaveProject: 'Biztosan el akarja hagyni a projektet?',
areYouSureYouWantToRemoveThisManagerFromProject: areYouSureYouWantToRemoveThisManagerFromProject:
'Biztosan eltávolítja ezt a vezetőt a projektből?', 'Biztosan eltávolítja ezt a menedzsert a projektből?',
areYouSureYouWantToRemoveThisMemberFromBoard: 'Biztosan eltávolítja ezt a tagot a tábláról?', areYouSureYouWantToRemoveThisMemberFromBoard: 'Biztosan eltávolítja ezt a tagot a tábláról?',
attachment: 'Melléklet', attachment: 'Melléklet',
attachments: 'Mellékletek', attachments: 'Mellékletek',
@@ -44,6 +54,7 @@ export default {
cardNotFound_title: 'Kártya nem található', cardNotFound_title: 'Kártya nem található',
cardOrActionAreDeleted: 'A kártya vagy művelet törölve lett.', cardOrActionAreDeleted: 'A kártya vagy művelet törölve lett.',
color: 'Szín', color: 'Szín',
copy_inline: 'másolás',
createBoard_title: 'Tábla létrehozása', createBoard_title: 'Tábla létrehozása',
createLabel_title: 'Címke létrehozása', createLabel_title: 'Címke létrehozása',
createNewOneOrSelectExistingOne: 'Hozzon létre egy újat, vagy válasszon ki egy meglévőt.', createNewOneOrSelectExistingOne: 'Hozzon létre egy újat, vagy válasszon ki egy meglévőt.',
@@ -52,6 +63,7 @@ export default {
currentPassword: 'Jelenlegi jelszó', currentPassword: 'Jelenlegi jelszó',
dangerZone_title: 'Veszélyzóna', dangerZone_title: 'Veszélyzóna',
date: 'Dátum', date: 'Dátum',
dueDate: 'Határidő',
dueDate_title: 'Esedékesség dátuma', dueDate_title: 'Esedékesség dátuma',
deleteAttachment_title: 'Melléklet törlése', deleteAttachment_title: 'Melléklet törlése',
deleteBoard_title: 'Tábla törlése', deleteBoard_title: 'Tábla törlése',
@@ -97,13 +109,17 @@ export default {
language: 'Nyelv', language: 'Nyelv',
leaveBoard_title: 'Tábla elhagyása', leaveBoard_title: 'Tábla elhagyása',
leaveProject_title: 'Projekt elhagyása', leaveProject_title: 'Projekt elhagyása',
linkIsCopied: 'Link másolva',
list: 'Lista', list: 'Lista',
listActions_title: 'Lista műveletek', listActions_title: 'Lista műveletek',
managers: 'Vezetők', managers: 'Menedzserek',
managerActions_title: 'Menedzseri műveletek',
members: 'Tagok', members: 'Tagok',
memberActions_title: 'Tag műveletek',
minutes: 'Percek', minutes: 'Percek',
moveCard_title: 'Kártya áthelyezése', moveCard_title: 'Kártya áthelyezése',
name: 'Név', name: 'Név',
newestFirst: 'Újabbak előre',
newEmail: 'Új e-mail', newEmail: 'Új e-mail',
newPassword: 'Új jelszó', newPassword: 'Új jelszó',
newUsername: 'Új felhasználónév', newUsername: 'Új felhasználónév',
@@ -113,6 +129,7 @@ export default {
noProjects: 'Nincsenek projektek', noProjects: 'Nincsenek projektek',
notifications: 'Értesítések', notifications: 'Értesítések',
noUnreadNotifications: 'Nincsenek olvasatlan értesítések.', noUnreadNotifications: 'Nincsenek olvasatlan értesítések.',
oldestFirst: 'Régebbi előre',
openBoard_title: 'Tábla megnyitása', openBoard_title: 'Tábla megnyitása',
optional_inline: 'opcionális', optional_inline: 'opcionális',
organization: 'Szervezet', organization: 'Szervezet',
@@ -122,17 +139,19 @@ export default {
'Tipp: nyomja meg a Ctrl-V (Cmd-V a Mac-en) billentyűkombinációt a vágólapról történő melléklet hozzáadásához.', 'Tipp: nyomja meg a Ctrl-V (Cmd-V a Mac-en) billentyűkombinációt a vágólapról történő melléklet hozzáadásához.',
project: 'Projekt', project: 'Projekt',
projectNotFound_title: 'Projekt nem található', projectNotFound_title: 'Projekt nem található',
removeManager_title: 'Vezető eltávolítása', removeManager_title: 'Menedzser eltávolítása',
removeMember_title: 'Tag eltávolítása', removeMember_title: 'Tag eltávolítása',
searchLabels: 'Címkék keresése...', searchLabels: 'Címkék keresése...',
searchMembers: 'Tagok keresése...', searchMembers: 'Tagok keresése...',
searchUsers: 'Felhasználók keresése...', searchUsers: 'Felhasználók keresése...',
searchCards: 'Kártyák keresése..',
seconds: 'Másodpercek', seconds: 'Másodpercek',
selectBoard: 'Válassza ki a táblát', selectBoard: 'Válassza ki a táblát',
selectList: 'Válassza ki a listát', selectList: 'Válassza ki a listát',
selectPermissions_title: 'Jogosultságok kiválasztása', selectPermissions_title: 'Jogosultságok kiválasztása',
selectProject: 'Válassza ki a projektet', selectProject: 'Válassza ki a projektet',
settings: 'Beállítások', settings: 'Beállítások',
sortList_title: 'Rendezés listában',
stopwatch: 'Stopper', stopwatch: 'Stopper',
subscribeToMyOwnCardsByDefault: 'Alapértelmezés szerint feliratkozás a saját kártyáimra', subscribeToMyOwnCardsByDefault: 'Alapértelmezés szerint feliratkozás a saját kártyáimra',
taskActions_title: 'Feladat műveletek', taskActions_title: 'Feladat műveletek',
@@ -170,6 +189,7 @@ export default {
addTask: 'Feladat hozzáadása', addTask: 'Feladat hozzáadása',
addToCard: 'Kártyához hozzáadása', addToCard: 'Kártyához hozzáadása',
addUser: 'Felhasználó hozzáadása', addUser: 'Felhasználó hozzáadása',
copyLink_title: 'Link másolása',
createBoard: 'Tábla létrehozása', createBoard: 'Tábla létrehozása',
createFile: 'Fájl létrehozása', createFile: 'Fájl létrehozása',
createLabel: 'Címke létrehozása', createLabel: 'Címke létrehozása',
@@ -191,6 +211,8 @@ export default {
deleteTask: 'Feladat törlése', deleteTask: 'Feladat törlése',
deleteTask_title: 'Feladat törlése', deleteTask_title: 'Feladat törlése',
deleteUser: 'Felhasználó törlése', deleteUser: 'Felhasználó törlése',
duplicate: 'Másolás',
duplicateCard_title: 'Kártya másolása',
edit: 'Szerkesztés', edit: 'Szerkesztés',
editDueDate_title: 'Esedékesség dátumának szerkesztése', editDueDate_title: 'Esedékesség dátumának szerkesztése',
editDescription_title: 'Leírás szerkesztése', editDescription_title: 'Leírás szerkesztése',
@@ -214,12 +236,13 @@ export default {
removeCover_title: 'Borító eltávolítása', removeCover_title: 'Borító eltávolítása',
removeFromBoard: 'Eltávolítás a tábláról', removeFromBoard: 'Eltávolítás a tábláról',
removeFromProject: 'Eltávolítás a projektről', removeFromProject: 'Eltávolítás a projektről',
removeManager: 'Vezető eltávolítása', removeManager: 'Menedzser eltávolítása',
removeMember: 'Tag eltávolítása', removeMember: 'Tag eltávolítása',
save: 'Mentés', save: 'Mentés',
showAllAttachments: 'Összes melléklet megjelenítése ({{hidden}} rejtve)', showAllAttachments: 'Összes melléklet megjelenítése ({{hidden}} rejtve)',
showDetails: 'Részletek megjelenítése', showDetails: 'Részletek megjelenítése',
showFewerAttachments: 'Kevesebb melléklet megjelenítése', showFewerAttachments: 'Kevesebb melléklet megjelenítése',
sortList_title: 'Rendezés listában',
start: 'Indítás', start: 'Indítás',
stop: 'Megállítás', stop: 'Megállítás',
subscribe: 'Feliratkozás', subscribe: 'Feliratkozás',
+1
View File
@@ -28,6 +28,7 @@ export default class extends BaseModel {
case ActionTypes.CORE_INITIALIZE: case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE: case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE: case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.attachments) { if (payload.attachments) {
payload.attachments.forEach((attachment) => { payload.attachments.forEach((attachment) => {
Attachment.upsert(attachment); Attachment.upsert(attachment);
+7 -4
View File
@@ -1,3 +1,4 @@
import orderBy from 'lodash/orderBy';
import { attr, fk, many } from 'redux-orm'; import { attr, fk, many } from 'redux-orm';
import BaseModel from './BaseModel'; import BaseModel from './BaseModel';
@@ -218,10 +219,6 @@ export default class extends BaseModel {
} }
} }
getOrderedMembershipsQuerySet() {
return this.memberships.orderBy('createdAt');
}
getOrderedLabelsQuerySet() { getOrderedLabelsQuerySet() {
return this.labels.orderBy('position'); return this.labels.orderBy('position');
} }
@@ -230,6 +227,12 @@ export default class extends BaseModel {
return this.lists.orderBy('position'); return this.lists.orderBy('position');
} }
getOrderedMembershipsModelArray() {
return orderBy(this.memberships.toModelArray(), (boardMembershipModel) =>
boardMembershipModel.user.name.toLocaleLowerCase(),
);
}
getMembershipModelForUser(userId) { getMembershipModelForUser(userId) {
return this.memberships return this.memberships
.filter({ .filter({
+34 -3
View File
@@ -180,7 +180,6 @@ export default class extends BaseModel {
break; break;
case ActionTypes.CARD_CREATE: case ActionTypes.CARD_CREATE:
case ActionTypes.CARD_UPDATE__SUCCESS: case ActionTypes.CARD_UPDATE__SUCCESS:
case ActionTypes.CARD_UPDATE_HANDLE:
Card.upsert(payload.card); Card.upsert(payload.card);
break; break;
@@ -202,8 +201,40 @@ export default class extends BaseModel {
break; break;
} }
case ActionTypes.CARD_UPDATE: case ActionTypes.CARD_UPDATE: {
Card.withId(payload.id).update(payload.data); const cardModel = Card.withId(payload.id);
// TODO: introduce separate action?
if (payload.data.boardId && payload.data.boardId !== cardModel.boardId) {
cardModel.deleteWithRelated();
} else {
cardModel.update(payload.data);
}
break;
}
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.isFetched) {
const cardModel = Card.withId(payload.card.id);
if (cardModel) {
cardModel.deleteWithRelated();
}
}
Card.upsert(payload.card);
if (payload.cardMemberships) {
payload.cardMemberships.forEach(({ cardId, userId }) => {
Card.withId(cardId).users.add(userId);
});
}
if (payload.cardLabels) {
payload.cardLabels.forEach(({ cardId, labelId }) => {
Card.withId(cardId).labels.add(labelId);
});
}
break; break;
case ActionTypes.CARD_DUPLICATE: { case ActionTypes.CARD_DUPLICATE: {
+1
View File
@@ -27,6 +27,7 @@ export default class extends BaseModel {
case ActionTypes.CORE_INITIALIZE: case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE: case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE: case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.tasks) { if (payload.tasks) {
payload.tasks.forEach((task) => { payload.tasks.forEach((task) => {
Task.upsert(task); Task.upsert(task);
+1 -1
View File
@@ -298,7 +298,7 @@ export default class extends BaseModel {
static getOrderedUndeletedQuerySet() { static getOrderedUndeletedQuerySet() {
return this.filter({ return this.filter({
deletedAt: null, deletedAt: null,
}).orderBy('createdAt'); }).orderBy((user) => user.name.toLocaleLowerCase());
} }
getOrderedProjectManagersQuerySet() { getOrderedProjectManagersQuerySet() {
+23 -2
View File
@@ -81,9 +81,30 @@ export function* updateCurrentCard(data) {
yield call(updateCard, cardId, data); yield call(updateCard, cardId, data);
} }
// TODO: handle card transfer
export function* handleCardUpdate(card) { export function* handleCardUpdate(card) {
yield put(actions.handleCardUpdate(card)); let fetch = false;
if (card.boardId) {
const prevCard = yield select(selectors.selectCardById, card.id);
fetch = !prevCard || prevCard.boardId !== card.boardId;
}
let cardMemberships;
let cardLabels;
let tasks;
let attachments;
if (fetch) {
try {
({
item: card, // eslint-disable-line no-param-reassign
included: { cardMemberships, cardLabels, tasks, attachments },
} = yield call(request, api.getCard, card.id));
} catch (error) {
fetch = false;
}
}
yield put(actions.handleCardUpdate(card, fetch, cardMemberships, cardLabels, tasks, attachments));
} }
export function* moveCard(id, listId, index = 0) { export function* moveCard(id, listId, index = 0) {
+8 -11
View File
@@ -55,17 +55,14 @@ export const selectMembershipsForCurrentBoard = createSelector(
return boardModel; return boardModel;
} }
return boardModel return boardModel.getOrderedMembershipsModelArray().map((boardMembershipModel) => ({
.getOrderedMembershipsQuerySet() ...boardMembershipModel.ref,
.toModelArray() isPersisted: !isLocalId(boardMembershipModel.id),
.map((boardMembershipModel) => ({ user: {
...boardMembershipModel.ref, ...boardMembershipModel.user.ref,
isPersisted: !isLocalId(boardMembershipModel.id), isCurrent: boardMembershipModel.user.id === currentUserId,
user: { },
...boardMembershipModel.user.ref, }));
isCurrent: boardMembershipModel.user.id === currentUserId,
},
}));
}, },
); );
+9 -4
View File
@@ -1,7 +1,6 @@
version: '3.8' version: '3.8'
services: services:
server: server:
build: build:
context: ./server context: ./server
@@ -13,6 +12,7 @@ services:
- NODE_ENV=development - NODE_ENV=development
- DATABASE_URL=postgresql://user:password@postgres:5432/planka_db - DATABASE_URL=postgresql://user:password@postgres:5432/planka_db
- SECRET_KEY=notsecretkey - SECRET_KEY=notsecretkey
# - TRUST_PROXY=0 # - TRUST_PROXY=0
# - TOKEN_EXPIRES_IN=365 # In days # - TOKEN_EXPIRES_IN=365 # In days
@@ -23,10 +23,17 @@ services:
# Configure knex to accept SSL certificates # Configure knex to accept SSL certificates
# - KNEX_REJECT_UNAUTHORIZED_SSL_CERTIFICATE=false # - KNEX_REJECT_UNAUTHORIZED_SSL_CERTIFICATE=false
# - ALLOW_ALL_TO_CREATE_PROJECTS=true
# - OIDC_ISSUER= # - OIDC_ISSUER=
# - OIDC_CLIENT_ID= # - OIDC_CLIENT_ID=
# - OIDC_CLIENT_SECRET= # - OIDC_CLIENT_SECRET=
# - OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG=
# - OIDC_USERINFO_SIGNED_RESPONSE_ALG=
# - OIDC_SCOPES=openid email profile # - OIDC_SCOPES=openid email profile
# - OIDC_RESPONSE_MODE=fragment
# - OIDC_USE_DEFAULT_RESPONSE_MODE=true
# - OIDC_ADMIN_ROLES=admin # - OIDC_ADMIN_ROLES=admin
# - OIDC_EMAIL_ATTRIBUTE=email # - OIDC_EMAIL_ATTRIBUTE=email
# - OIDC_NAME_ATTRIBUTE=name # - OIDC_NAME_ATTRIBUTE=name
@@ -56,7 +63,6 @@ services:
# - SLACK_BOT_TOKEN= # - SLACK_BOT_TOKEN=
# - SLACK_CHANNEL_ID= # - SLACK_CHANNEL_ID=
working_dir: /app working_dir: /app
command: ["sh", "-c", "npm run start"] command: ["sh", "-c", "npm run start"]
depends_on: depends_on:
@@ -85,11 +91,11 @@ services:
dockerfile: ../config/development/Dockerfile.server dockerfile: ../config/development/Dockerfile.server
environment: environment:
- DATABASE_URL=postgresql://user:password@postgres:5432/planka_db - DATABASE_URL=postgresql://user:password@postgres:5432/planka_db
# - DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted # - 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_PASSWORD=demo
# - DEFAULT_ADMIN_NAME=Demo Demo # - DEFAULT_ADMIN_NAME=Demo Demo
# - DEFAULT_ADMIN_USERNAME=demo # - DEFAULT_ADMIN_USERNAME=demo
working_dir: /app working_dir: /app
command: ["sh", "-c", "npm run db:init"] command: ["sh", "-c", "npm run db:init"]
volumes: volumes:
@@ -126,6 +132,5 @@ services:
- server - server
- client - client
volumes: volumes:
db-data: db-data:
+6
View File
@@ -31,10 +31,16 @@ services:
# - DEFAULT_ADMIN_NAME=Demo Demo # - DEFAULT_ADMIN_NAME=Demo Demo
# - DEFAULT_ADMIN_USERNAME=demo # - DEFAULT_ADMIN_USERNAME=demo
# - ALLOW_ALL_TO_CREATE_PROJECTS=true
# - OIDC_ISSUER= # - OIDC_ISSUER=
# - OIDC_CLIENT_ID= # - OIDC_CLIENT_ID=
# - OIDC_CLIENT_SECRET= # - OIDC_CLIENT_SECRET=
# - OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG=
# - OIDC_USERINFO_SIGNED_RESPONSE_ALG=
# - OIDC_SCOPES=openid email profile # - OIDC_SCOPES=openid email profile
# - OIDC_RESPONSE_MODE=fragment
# - OIDC_USE_DEFAULT_RESPONSE_MODE=true
# - OIDC_ADMIN_ROLES=admin # - OIDC_ADMIN_ROLES=admin
# - OIDC_EMAIL_ATTRIBUTE=email # - OIDC_EMAIL_ATTRIBUTE=email
# - OIDC_NAME_ATTRIBUTE=name # - OIDC_NAME_ATTRIBUTE=name
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "planka", "name": "planka",
"version": "1.18.0", "version": "1.20.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "planka", "name": "planka",
"version": "1.18.0", "version": "1.20.0",
"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.18.0", "version": "1.20.0",
"private": true, "private": true,
"homepage": "https://plankanban.github.io/planka", "homepage": "https://plankanban.github.io/planka",
"repository": { "repository": {
+6
View File
@@ -22,10 +22,16 @@ SECRET_KEY=notsecretkey
# DEFAULT_ADMIN_NAME=Demo Demo # DEFAULT_ADMIN_NAME=Demo Demo
# DEFAULT_ADMIN_USERNAME=demo # DEFAULT_ADMIN_USERNAME=demo
# ALLOW_ALL_TO_CREATE_PROJECTS=true
# OIDC_ISSUER= # OIDC_ISSUER=
# OIDC_CLIENT_ID= # OIDC_CLIENT_ID=
# OIDC_CLIENT_SECRET= # OIDC_CLIENT_SECRET=
# OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG=
# OIDC_USERINFO_SIGNED_RESPONSE_ALG=
# OIDC_SCOPES=openid email profile # OIDC_SCOPES=openid email profile
# OIDC_RESPONSE_MODE=fragment
# OIDC_USE_DEFAULT_RESPONSE_MODE=true
# OIDC_ADMIN_ROLES=admin # OIDC_ADMIN_ROLES=admin
# OIDC_EMAIL_ATTRIBUTE=email # OIDC_EMAIL_ATTRIBUTE=email
# OIDC_NAME_ATTRIBUTE=name # OIDC_NAME_ATTRIBUTE=name
@@ -4,6 +4,9 @@ const Errors = {
INVALID_CODE_OR_NONCE: { INVALID_CODE_OR_NONCE: {
invalidCodeOrNonce: 'Invalid code or nonce', invalidCodeOrNonce: 'Invalid code or nonce',
}, },
INVALID_USERINFO_SIGNATURE: {
invalidUserinfoSignature: 'Invalid signature on userinfo due to client misconfiguration',
},
EMAIL_ALREADY_IN_USE: { EMAIL_ALREADY_IN_USE: {
emailAlreadyInUse: 'Email already in use', emailAlreadyInUse: 'Email already in use',
}, },
@@ -31,6 +34,9 @@ module.exports = {
invalidCodeOrNonce: { invalidCodeOrNonce: {
responseType: 'unauthorized', responseType: 'unauthorized',
}, },
invalidUserinfoSignature: {
responseType: 'unauthorized',
},
emailAlreadyInUse: { emailAlreadyInUse: {
responseType: 'conflict', responseType: 'conflict',
}, },
@@ -51,6 +57,7 @@ module.exports = {
sails.log.warn(`Invalid code or nonce! (IP: ${remoteAddress})`); sails.log.warn(`Invalid code or nonce! (IP: ${remoteAddress})`);
return Errors.INVALID_CODE_OR_NONCE; return Errors.INVALID_CODE_OR_NONCE;
}) })
.intercept('invalidUserinfoSignature', () => Errors.INVALID_USERINFO_SIGNATURE)
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE) .intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE)
.intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE) .intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE)
.intercept('missingValues', () => Errors.MISSING_VALUES); .intercept('missingValues', () => Errors.MISSING_VALUES);
+16
View File
@@ -1,3 +1,9 @@
const Errors = {
NOT_ENOUGH_RIGHTS: {
notEnoughRights: 'Not enough rights',
},
};
module.exports = { module.exports = {
inputs: { inputs: {
name: { name: {
@@ -6,9 +12,19 @@ module.exports = {
}, },
}, },
exits: {
notEnoughRights: {
responseType: 'forbidden',
},
},
async fn(inputs) { async fn(inputs) {
const { currentUser } = this.req; const { currentUser } = this.req;
if (!currentUser.isAdmin && !sails.config.custom.allowAllToCreateProjects) {
throw Errors.NOT_ENOUGH_RIGHTS;
}
const values = _.pick(inputs, ['name']); const values = _.pick(inputs, ['name']);
const { project, projectManager } = await sails.helpers.projects.createOne.with({ const { project, projectManager } = await sails.helpers.projects.createOne.with({
+10 -4
View File
@@ -4,11 +4,16 @@ module.exports = {
if (sails.hooks.oidc.isActive()) { if (sails.hooks.oidc.isActive()) {
const oidcClient = sails.hooks.oidc.getClient(); const oidcClient = sails.hooks.oidc.getClient();
const authorizationUrlParams = {
scope: sails.config.custom.oidcScopes,
};
if (!sails.config.custom.oidcUseDefaultResponseMode) {
authorizationUrlParams.response_mode = sails.config.custom.oidcResponseMode;
}
oidc = { oidc = {
authorizationUrl: oidcClient.authorizationUrl({ authorizationUrl: oidcClient.authorizationUrl(authorizationUrlParams),
scope: sails.config.custom.oidcScopes,
response_mode: 'fragment',
}),
endSessionUrl: oidcClient.issuer.end_session_endpoint ? oidcClient.endSessionUrl({}) : null, endSessionUrl: oidcClient.issuer.end_session_endpoint ? oidcClient.endSessionUrl({}) : null,
isEnforced: sails.config.custom.oidcEnforced, isEnforced: sails.config.custom.oidcEnforced,
}; };
@@ -17,6 +22,7 @@ module.exports = {
return { return {
item: { item: {
oidc, oidc,
allowAllToCreateProjects: sails.config.custom.allowAllToCreateProjects,
}, },
}; };
}, },
+9
View File
@@ -204,6 +204,15 @@ module.exports = {
), ),
); );
sails.sockets.broadcast(
`board:${inputs.record.boardId}`,
'cardDelete', // TODO: introduce separate event
{
item: inputs.record,
},
inputs.request,
);
sails.sockets.broadcast(`board:${card.boardId}`, 'cardUpdate', { sails.sockets.broadcast(`board:${card.boardId}`, 'cardUpdate', {
item: card, item: card,
}); });
@@ -12,6 +12,7 @@ module.exports = {
exits: { exits: {
invalidCodeOrNonce: {}, invalidCodeOrNonce: {},
invalidUserinfoSignature: {},
missingValues: {}, missingValues: {},
emailAlreadyInUse: {}, emailAlreadyInUse: {},
usernameAlreadyInUse: {}, usernameAlreadyInUse: {},
@@ -34,6 +35,14 @@ module.exports = {
); );
userInfo = await client.userinfo(tokenSet); userInfo = await client.userinfo(tokenSet);
} catch (e) { } catch (e) {
if (
e instanceof SyntaxError &&
e.message.includes('Unexpected token e in JSON at position 0')
) {
sails.log.warn('Error while exchanging OIDC code: userinfo response is signed');
throw 'invalidUserinfoSignature';
}
sails.log.warn(`Error while exchanging OIDC code: ${e}`); sails.log.warn(`Error while exchanging OIDC code: ${e}`);
throw 'invalidCodeOrNonce'; throw 'invalidCodeOrNonce';
} }
+87 -12
View File
@@ -1,3 +1,58 @@
const EVENT_TYPES = {
ACTION_CREATE: 'actionCreate',
ACTION_DELETE: 'actionDelete',
ACTION_UPDATE: 'actionUpdate',
ATTACHMENT_CREATE: 'attachmentCreate',
ATTACHMENT_DELETE: 'attachmentDelete',
ATTACHMENT_UPDATE: 'attachmentUpdate',
BOARD_CREATE: 'boardCreate',
BOARD_DELETE: 'boardDelete',
BOARD_UPDATE: 'boardUpdate',
BOARD_MEMBERSHIP_CREATE: 'boardMembershipCreate',
BOARD_MEMBERSHIP_DELETE: 'boardMembershipDelete',
BOARD_MEMBERSHIP_UPDATE: 'boardMembershipUpdate',
CARD_CREATE: 'cardCreate',
CARD_DELETE: 'cardDelete',
CARD_UPDATE: 'cardUpdate',
CARD_LABEL_CREATE: 'cardLabelCreate',
CARD_LABEL_DELETE: 'cardLabelDelete',
CARD_MEMBERSHIP_CREATE: 'cardMembershipCreate',
CARD_MEMBERSHIP_DELETE: 'cardMembershipDelete',
LABEL_CREATE: 'labelCreate',
LABEL_DELETE: 'labelDelete',
LABEL_UPDATE: 'labelUpdate',
LIST_CREATE: 'listCreate',
LIST_DELETE: 'listDelete',
LIST_SORT: 'listSort',
LIST_UPDATE: 'listUpdate',
NOTIFICATION_CREATE: 'notificationCreate',
NOTIFICATION_UPDATE: 'notificationUpdate',
PROJECT_CREATE: 'projectCreate',
PROJECT_DELETE: 'projectDelete',
PROJECT_UPDATE: 'projectUpdate',
PROJECT_MANAGER_CREATE: 'projectManagerCreate',
PROJECT_MANAGER_DELETE: 'projectManagerDelete',
TASK_CREATE: 'taskCreate',
TASK_DELETE: 'taskDelete',
TASK_UPDATE: 'taskUpdate',
USER_CREATE: 'userCreate',
USER_DELETE: 'userDelete',
USER_UPDATE: 'userUpdate',
};
const jsonifyData = (data) => { const jsonifyData = (data) => {
const nextData = {}; const nextData = {};
@@ -22,18 +77,33 @@ const jsonifyData = (data) => {
return nextData; return nextData;
}; };
/**
* @typedef {Object} Included
* @property {any[]} [projects] - Array of projects (optional).
* @property {any[]} [boards] - Array of boards (optional).
* @property {any[]} [lists] - Array of lists (optional).
* @property {any[]} [cards] - Array of cards (optional).
*/
/**
* @typedef {Object} Data
* @property {any} item - Actual event data.
* @property {Included} [included] - Optional included data.
*/
/** /**
* Sends a webhook notification to a configured URL. * Sends a webhook notification to a configured URL.
* *
* @param {*} webhook - Webhook configuration. * @param {*} webhook - Webhook configuration.
* @param {string} event - The event (see {@link Events}). * @param {string} event - The event (see {@link EVENT_TYPES}).
* @param {*} data - The actual data related to the event. * @param {Data} data - The data object containing event data and optionally included data.
* @param {ref} user - User object associated with the event. * @param {ref} user - User object associated with the event.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function sendWebhook(webhook, event, data, user) { async function sendWebhook(webhook, event, data, user) {
const headers = { const headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'User-Agent': `planka (+${sails.config.custom.baseUrl})`,
}; };
if (webhook.accessToken) { if (webhook.accessToken) {
@@ -46,18 +116,22 @@ async function sendWebhook(webhook, event, data, user) {
user: sails.helpers.utils.jsonifyRecord(user), user: sails.helpers.utils.jsonifyRecord(user),
}); });
const response = await fetch(webhook.url, { try {
headers, const response = await fetch(webhook.url, {
body, headers,
method: 'POST', body,
}); method: 'POST',
});
if (!response.ok) { if (!response.ok) {
const message = await response.text(); const message = await response.text();
sails.log.error( sails.log.error(
`Webhook ${webhook.url} failed with status ${response.status} and message: ${message}`, `Webhook ${webhook.url} failed with status ${response.status} and message: ${message}`,
); );
}
} catch (e) {
sails.log.error(`Webhook ${webhook.url} failed with error message: ${e.message}`);
} }
} }
@@ -68,6 +142,7 @@ module.exports = {
event: { event: {
type: 'string', type: 'string',
required: true, required: true,
isIn: Object.values(EVENT_TYPES),
}, },
data: { data: {
type: 'ref', type: 'ref',
+9 -2
View File
@@ -25,12 +25,19 @@ module.exports = function defineOidcHook(sails) {
const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer); const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer);
client = new issuer.Client({ const metadata = {
client_id: sails.config.custom.oidcClientId, client_id: sails.config.custom.oidcClientId,
client_secret: sails.config.custom.oidcClientSecret, client_secret: sails.config.custom.oidcClientSecret,
redirect_uris: [sails.config.custom.oidcRedirectUri], redirect_uris: [sails.config.custom.oidcRedirectUri],
response_types: ['code'], response_types: ['code'],
}); userinfo_signed_response_alg: sails.config.custom.oidcUserinfoSignedResponseAlg,
};
if (sails.config.custom.oidcIdTokenSignedResponseAlg) {
metadata.id_token_signed_response_alg = sails.config.custom.oidcIdTokenSignedResponseAlg;
}
client = new issuer.Client(metadata);
}, },
getClient() { getClient() {
+7 -1
View File
@@ -34,10 +34,16 @@ module.exports.custom = {
defaultAdminEmail: defaultAdminEmail:
process.env.DEFAULT_ADMIN_EMAIL && process.env.DEFAULT_ADMIN_EMAIL.toLowerCase(), process.env.DEFAULT_ADMIN_EMAIL && process.env.DEFAULT_ADMIN_EMAIL.toLowerCase(),
allowAllToCreateProjects: process.env.ALLOW_ALL_TO_CREATE_PROJECTS === 'true',
oidcIssuer: process.env.OIDC_ISSUER, oidcIssuer: process.env.OIDC_ISSUER,
oidcClientId: process.env.OIDC_CLIENT_ID, oidcClientId: process.env.OIDC_CLIENT_ID,
oidcClientSecret: process.env.OIDC_CLIENT_SECRET, oidcClientSecret: process.env.OIDC_CLIENT_SECRET,
oidcIdTokenSignedResponseAlg: process.env.OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG,
oidcUserinfoSignedResponseAlg: process.env.OIDC_USERINFO_SIGNED_RESPONSE_ALG,
oidcScopes: process.env.OIDC_SCOPES || 'openid email profile', oidcScopes: process.env.OIDC_SCOPES || 'openid email profile',
oidcResponseMode: process.env.OIDC_RESPONSE_MODE || 'fragment',
oidcUseDefaultResponseMode: process.env.OIDC_USE_DEFAULT_RESPONSE_MODE === 'true',
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [], oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [],
oidcEmailAttribute: process.env.OIDC_EMAIL_ATTRIBUTE || 'email', oidcEmailAttribute: process.env.OIDC_EMAIL_ATTRIBUTE || 'email',
oidcNameAttribute: process.env.OIDC_NAME_ATTRIBUTE || 'name', oidcNameAttribute: process.env.OIDC_NAME_ATTRIBUTE || 'name',
@@ -60,7 +66,7 @@ module.exports.custom = {
smtpPassword: process.env.SMTP_PASSWORD, smtpPassword: process.env.SMTP_PASSWORD,
smtpFrom: process.env.SMTP_FROM, smtpFrom: process.env.SMTP_FROM,
webhooks: JSON.parse(process.env.WEBHOOKS), // TODO: validate structure webhooks: JSON.parse(process.env.WEBHOOKS || '[]'), // TODO: validate structure
slackBotToken: process.env.SLACK_BOT_TOKEN, slackBotToken: process.env.SLACK_BOT_TOKEN,
slackChannelId: process.env.SLACK_CHANNEL_ID, slackChannelId: process.env.SLACK_CHANNEL_ID,
-2
View File
@@ -21,8 +21,6 @@ module.exports.policies = {
'users/create': ['is-authenticated', 'is-admin'], 'users/create': ['is-authenticated', 'is-admin'],
'users/delete': ['is-authenticated', 'is-admin'], 'users/delete': ['is-authenticated', 'is-admin'],
'projects/create': ['is-authenticated', 'is-admin'],
'show-config': true, 'show-config': true,
'access-tokens/create': true, 'access-tokens/create': true,
'access-tokens/exchange-using-oidc': true, 'access-tokens/exchange-using-oidc': true,