mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-06-14 10:30:27 +00:00
Compare commits
230 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb058e91a3 | |||
| 27e7407407 | |||
| 5470b51cc7 | |||
| 8e0b1d8aee | |||
| 2834459b22 | |||
| 000894dabd | |||
| 494620cdea | |||
| a502eb239d | |||
| caf775093e | |||
| f28e18e676 | |||
| b4bab1d5b9 | |||
| c4d5072e5c | |||
| 852bf750ca | |||
| 47359c4113 | |||
| 15f2c4c769 | |||
| a0174c61e8 | |||
| 5b30dce609 | |||
| 8f6099e3e4 | |||
| 7c44375223 | |||
| 72e204f8fd | |||
| b5f5b53e37 | |||
| 1c15133a52 | |||
| 7c9c2c35f8 | |||
| 9806e568c0 | |||
| b4bb4e2938 | |||
| de7b809229 | |||
| a40df1ff87 | |||
| a161aa2c92 | |||
| cad0f25345 | |||
| 2ed453a400 | |||
| 452d8a686f | |||
| 90f77f6d5c | |||
| 0c11cf747a | |||
| 6d36475ed3 | |||
| fee6ff43bf | |||
| 57cd5ec818 | |||
| 02512e0f4f | |||
| 555f4a8a6d | |||
| 3633766544 | |||
| e98a984417 | |||
| bc9141753f | |||
| 1f9f4157a6 | |||
| 778a3ed551 | |||
| 5ea4305185 | |||
| ef311f22bf | |||
| e202530afb | |||
| 85deeaf806 | |||
| 825c8a6abe | |||
| cdc8f63b4b | |||
| 9db9818ede | |||
| 4f7ee669d3 | |||
| 77f9947613 | |||
| a8eb3b6ac5 | |||
| 575eab1cf0 | |||
| 7a23e4fd4e | |||
| b16b276f36 | |||
| 4f380debb5 | |||
| 047c4aa3a0 | |||
| 925b220905 | |||
| 6708059227 | |||
| 1f3d9d4e1c | |||
| 0dcfac8f15 | |||
| ad8b7f0894 | |||
| 55f810b23f | |||
| 65eddee63e | |||
| 4322c98f73 | |||
| edcf789126 | |||
| b985ba4f0e | |||
| 67c0405274 | |||
| 9b32151ab5 | |||
| b51a659515 | |||
| 44a6f09a09 | |||
| 4c10525078 | |||
| c9ab8b2eff | |||
| 4bf38bf00f | |||
| 7c7c67948e | |||
| 263cb96786 | |||
| b6e3e7a658 | |||
| ceaf1423f4 | |||
| c8620a066d | |||
| 9598b503ec | |||
| 1ca566f670 | |||
| 94f4ec8b96 | |||
| 7aab2c55ff | |||
| 6abb4d34c1 | |||
| c8ccf080f3 | |||
| 0342ae926c | |||
| be08742653 | |||
| 528f7da5ef | |||
| 7d72ae3449 | |||
| 753cde0b85 | |||
| 223ba44b61 | |||
| cd02483b19 | |||
| f724662874 | |||
| bee762737e | |||
| 83efd3e506 | |||
| 2278a6cc73 | |||
| 586b60b276 | |||
| f07b9ea304 | |||
| 09dca5d76c | |||
| 65bb808441 | |||
| 83b79edb42 | |||
| b8ec244d92 | |||
| 5b924614aa | |||
| 43103add47 | |||
| 124d5d6bb2 | |||
| 58fde558f7 | |||
| 8b314acfcf | |||
| 1c0eab9893 | |||
| c62daa0c59 | |||
| 1a05101f50 | |||
| 47fb46c837 | |||
| d29580aa02 | |||
| d0fc62ef13 | |||
| b14c0e4c11 | |||
| 43ec12f4f0 | |||
| 40cf2c85e6 | |||
| 6195b7c334 | |||
| 385570c1e8 | |||
| d82cfc6c62 | |||
| fdf52dcb17 | |||
| 1ff220ccf8 | |||
| 536ab34955 | |||
| f7369f0611 | |||
| 14bc105d43 | |||
| 2efb4365bf | |||
| c1b86fc782 | |||
| 52e92cc0db | |||
| 3af2f636a5 | |||
| 6fb967cf79 | |||
| 03c49ea1f8 | |||
| 11700d7ecb | |||
| 33eb2c8801 | |||
| a835419168 | |||
| 4ce16d1ea4 | |||
| c1c7167ace | |||
| 3d538d4f14 | |||
| 7969e7116d | |||
| 4f58f2caee | |||
| 263baa81c0 | |||
| 092890b6ab | |||
| db7d7ea288 | |||
| 452daf5d5e | |||
| d373164e13 | |||
| cd7715fa0e | |||
| af9c3a8565 | |||
| dd6b8c44a4 | |||
| 499273dbb7 | |||
| 6612b892b7 | |||
| 89cea31475 | |||
| 872fa07213 | |||
| 36e4ee7738 | |||
| a139eb9bce | |||
| 7166696aa2 | |||
| 537a7908f1 | |||
| 3fe776ee69 | |||
| 581be02e53 | |||
| 71db83efce | |||
| 7ae7f25580 | |||
| 5d14baa43a | |||
| 141b397c82 | |||
| fd853cfc6f | |||
| 63f718178e | |||
| 74baf20feb | |||
| 958112af6b | |||
| 08d0f9448e | |||
| 7bcc8bd3a2 | |||
| 0eb2545773 | |||
| 714511b0a8 | |||
| c9700773f4 | |||
| 2229f87d9b | |||
| d360503443 | |||
| 838182a8b4 | |||
| 967cfedbb3 | |||
| a36645a282 | |||
| 3368a70f88 | |||
| cd1715ba52 | |||
| 0bc2a16093 | |||
| a21b3cd606 | |||
| 1c479684fc | |||
| c9dbc7c7b7 | |||
| c41dc9d8c0 | |||
| 1db5841424 | |||
| e53b068902 | |||
| 2bd436dfd8 | |||
| d13be25f45 | |||
| 6efd9dc5f9 | |||
| 1edd4012e4 | |||
| 4390c9855a | |||
| 4d53216c05 | |||
| 040206859f | |||
| d06119a21d | |||
| c27ad97287 | |||
| b1658c0f83 | |||
| 05b8609073 | |||
| 97df5c3b9c | |||
| 33e5ad2b5c | |||
| 998cb642a9 | |||
| 07ac195fea | |||
| 7d5990bf0f | |||
| 4ec982163e | |||
| 3c9502f241 | |||
| 63cecb2fd8 | |||
| 3029a2d33d | |||
| fa0d2a959d | |||
| f79cac3292 | |||
| 7a20a9941e | |||
| 24cc960379 | |||
| 353df6413f | |||
| ee844c81d2 | |||
| b6cb3b026c | |||
| 003a6342a5 | |||
| fb10764167 | |||
| 6d3798ad08 | |||
| 70921b8d15 | |||
| b185f83fc3 | |||
| 60af295c0a | |||
| e7fe52a625 | |||
| 49c506eed9 | |||
| 21fadf6df2 | |||
| 5fcccbc97d | |||
| 3ef2b6cfa2 | |||
| 84b4269c75 | |||
| a2d57d43d1 | |||
| df33f1a130 | |||
| 4c6a2055c2 | |||
| f09a3df870 | |||
| ea1a412749 | |||
| db82327d9a | |||
| ea1a02bd7d |
-120
@@ -1,120 +0,0 @@
|
|||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: integration-testing
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
clone:
|
|
||||||
disable: true
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: prepare-tests
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- git clone https://github.com/mailcow/mailcow-integration-tests.git --branch $(curl -sL https://api.github.com/repos/mailcow/mailcow-integration-tests/releases/latest | jq -r '.tag_name') --single-branch .
|
|
||||||
- chmod +x ci.sh
|
|
||||||
- chmod +x ci-ssh.sh
|
|
||||||
- chmod +x ci-piprequierments.sh
|
|
||||||
- ./ci.sh
|
|
||||||
- wget -O group_vars/all/secrets.yml $SECRETS_DOWNLOAD_URL --quiet
|
|
||||||
environment:
|
|
||||||
SECRETS_DOWNLOAD_URL:
|
|
||||||
from_secret: SECRETS_DOWNLOAD_URL
|
|
||||||
VAULT_PW:
|
|
||||||
from_secret: VAULT_PW
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: lint
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- ansible-lint ./
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: create-server
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- ./ci-piprequierments.sh
|
|
||||||
- ansible-playbook mailcow-start-server.yml --diff
|
|
||||||
- ./ci-ssh.sh
|
|
||||||
environment:
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: false
|
|
||||||
ANSIBLE_FORCE_COLOR: true
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: setup-server
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- sleep 120
|
|
||||||
- ./ci-piprequierments.sh
|
|
||||||
- ansible-playbook mailcow-setup-server.yml --private-key /drone/src/id_ssh_rsa --diff
|
|
||||||
environment:
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: false
|
|
||||||
ANSIBLE_FORCE_COLOR: true
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: run-tests
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- ./ci-piprequierments.sh
|
|
||||||
- ansible-playbook mailcow-integration-tests.yml --private-key /drone/src/id_ssh_rsa --diff
|
|
||||||
environment:
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: false
|
|
||||||
ANSIBLE_FORCE_COLOR: true
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: delete-server
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- ./ci-piprequierments.sh
|
|
||||||
- ansible-playbook mailcow-delete-server.yml --diff
|
|
||||||
environment:
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: false
|
|
||||||
ANSIBLE_FORCE_COLOR: true
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
status:
|
|
||||||
- failure
|
|
||||||
- success
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: signature
|
|
||||||
hmac: f6619243fe2a27563291c9f2a46d93ffbc3b6dced9a05f23e64b555ce03a31e5
|
|
||||||
|
|
||||||
...
|
|
||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Mark/Close Stale Issues and Pull Requests 🗑️
|
- name: Mark/Close Stale Issues and Pull Requests 🗑️
|
||||||
uses: actions/stale@v5.0.0
|
uses: actions/stale@v6.0.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
||||||
days-before-stale: 60
|
days-before-stale: 60
|
||||||
@@ -30,6 +30,7 @@ jobs:
|
|||||||
stale-issue-label: "stale"
|
stale-issue-label: "stale"
|
||||||
stale-pr-label: "stale"
|
stale-pr-label: "stale"
|
||||||
exempt-draft-pr: "true"
|
exempt-draft-pr: "true"
|
||||||
|
close-issue-reason: "not_planned"
|
||||||
operations-per-run: "250"
|
operations-per-run: "250"
|
||||||
ascending: "true"
|
ascending: "true"
|
||||||
#DRY-RUN
|
#DRY-RUN
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
name: Build mailcow Docker Images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master", "staging" ]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker_image_builds:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
images:
|
||||||
|
- "acme-mailcow"
|
||||||
|
- "clamd-mailcow"
|
||||||
|
- "dockerapi-mailcow"
|
||||||
|
- "dovecot-mailcow"
|
||||||
|
- "netfilter-mailcow"
|
||||||
|
- "olefy-mailcow"
|
||||||
|
- "php-fpm-mailcow"
|
||||||
|
- "postfix-mailcow"
|
||||||
|
- "rspamd-mailcow"
|
||||||
|
- "sogo-mailcow"
|
||||||
|
- "solr-mailcow"
|
||||||
|
- "unbound-mailcow"
|
||||||
|
- "watchdog-mailcow"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup Docker
|
||||||
|
run: |
|
||||||
|
curl -sSL https://get.docker.com/ | CHANNEL=stable sudo sh
|
||||||
|
sudo service docker start
|
||||||
|
sudo curl -L https://github.com/docker/compose/releases/download/v$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose
|
||||||
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
- name: Prepair Image Builds
|
||||||
|
run: |
|
||||||
|
cp helper-scripts/docker-compose.override.yml.d/BUILD_FLAGS/docker-compose.override.yml docker-compose.override.yml
|
||||||
|
- name: Build Docker Images
|
||||||
|
run: |
|
||||||
|
docker-compose build ${image}
|
||||||
|
env:
|
||||||
|
image: ${{ matrix.images }}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
name: mailcow Integration Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master", "staging" ]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
integration_tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Setup Ansible
|
||||||
|
run: |
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install python3 python3-pip git
|
||||||
|
sudo pip3 install ansible
|
||||||
|
- name: Prepair Test Environment
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/mailcow/mailcow-integration-tests.git --branch $(curl -sL https://api.github.com/repos/mailcow/mailcow-integration-tests/releases/latest | jq -r '.tag_name') --single-branch .
|
||||||
|
./fork_check.sh
|
||||||
|
./ci.sh
|
||||||
|
./ci-pip-requirements.sh
|
||||||
|
env:
|
||||||
|
VAULT_PW: ${{ secrets.MAILCOW_TESTS_VAULT_PW }}
|
||||||
|
VAULT_FILE: ${{ secrets.MAILCOW_TESTS_VAULT_FILE }}
|
||||||
|
- name: Start Integration Test Server
|
||||||
|
run: |
|
||||||
|
./fork_check.sh
|
||||||
|
ansible-playbook mailcow-start-server.yml --diff
|
||||||
|
env:
|
||||||
|
PY_COLORS: '1'
|
||||||
|
ANSIBLE_FORCE_COLOR: '1'
|
||||||
|
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
||||||
|
- name: Setup Integration Test Server
|
||||||
|
run: |
|
||||||
|
./fork_check.sh
|
||||||
|
sleep 30
|
||||||
|
ansible-playbook mailcow-setup-server.yml --private-key id_ssh_rsa --diff
|
||||||
|
env:
|
||||||
|
PY_COLORS: '1'
|
||||||
|
ANSIBLE_FORCE_COLOR: '1'
|
||||||
|
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
||||||
|
- name: Run Integration Tests
|
||||||
|
run: |
|
||||||
|
./fork_check.sh
|
||||||
|
ansible-playbook mailcow-integration-tests.yml --private-key id_ssh_rsa --diff
|
||||||
|
env:
|
||||||
|
PY_COLORS: '1'
|
||||||
|
ANSIBLE_FORCE_COLOR: '1'
|
||||||
|
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
||||||
|
- name: Delete Integration Test Server
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
./fork_check.sh
|
||||||
|
ansible-playbook mailcow-delete-server.yml --diff
|
||||||
|
env:
|
||||||
|
PY_COLORS: '1'
|
||||||
|
ANSIBLE_FORCE_COLOR: '1'
|
||||||
|
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
name: "Tweet trigger release"
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Tweet-trigger-publish-release
|
||||||
|
uses: mugi111/tweet-trigger-release@v1.1
|
||||||
|
with:
|
||||||
|
consumer_key: ${{ secrets.CONSUMER_KEY }}
|
||||||
|
consumer_secret: ${{ secrets.CONSUMER_SECRET }}
|
||||||
|
access_token_key: ${{ secrets.ACCESS_TOKEN_KEY }}
|
||||||
|
access_token_secret: ${{ secrets.ACCESS_TOKEN_SECRET }}
|
||||||
|
tweet_body: 'A new mailcow-dockerized Release has been Released on GitHub! Checkout our GitHub Page for the latest Release: github.com/mailcow/mailcow-dockerized/releases/latest'
|
||||||
-16
@@ -1,16 +0,0 @@
|
|||||||
sudo: required
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
script:
|
|
||||||
- echo 'Europe/Berlin' | MAILCOW_HOSTNAME=build.mailcow ./generate_config.sh
|
|
||||||
- docker-compose pull --ignore-pull-failures --parallel
|
|
||||||
- docker-compose build
|
|
||||||
- docker login --username=$DOCKER_HUB_USERNAME --password=$DOCKER_HUB_PASSWORD
|
|
||||||
- docker-compose push
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master_disabled
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- secure: MpxpTwD7f0CNEVLitSpVmocK7O9r+BwFE1deEHK4AlQo/oc9cOlhGe1EL3mx9zbglPmjlDg/8kMUGv6vSirIabfBo9Szjps76bHckFr9lr2Ykkg0e29oC8pgPpSXD1eY/1ZIN/FvIkxpUFLETo1okS/j9q/A0DCGFmti0n3EoMORsgRz9CpNAiEh0zpSd6+euPAGHuczuCrDuO84my9bIOCjA/+aPunHNeXiuM8yIM2SxCSyGtIKT0+jvquIvLF58VxivysXBlRfhDn8fhB09nXA2Ru/derYQACfcmNSn9Pd4bDpebPJW5B9H/XA8xjb58uKinUlncbAMB/QnxoT75j9YRWJZRSQ+34XNYP6ZgK9soZ2TC6djQyEKTUu45Kp/1s+poSn42m9jytJJTmmK0KxsZTRcC8JD5nrjIMZWPUNNTwC5L4+I7ZRWg2WooK3LNyq1Ng8Hn6W77wSgsvAJw2HD3Lx58AprGUhHuBeaIZRuSN9aKwZrl9vKQJLqPnOp/nF2EC6kot5HYYtcotGtETXPUDih21gWD5ZM2BqVqYfQQnJnNMgeYmMdj6QQuTFqhuNJf7hXRIRkTnD3j1gDOLKQZazW0+N2JE8XWDFwi6fKScDsxT85lJti9HmzHa7+k4RVHmUYuDgRoPuzUgjWHvPsiz3/Z8WQ9JYpH84S8w=
|
|
||||||
- secure: fWzZisT6nGDNL4lf6tXB07eFG2drgBakHxzdF/NFVvzuP861RFR6omuL+ED0PgXrEHDJBxaBLv52je8irmUXrAH1CNr7T8DWiZo/h5h609Uzr+38T1NnIu4krL0Wo6/CDwlLKnzqTq9yBIZLQSHVJmo8AOpo1JPIi2ajodqj9ZfmAxDQTQl+G6zvQjtqIkYHsHY7A44Rto0f14ykn7w2S82Jn6Ry89VNI5V1WEO3sMpM/XekNP/HokNcRIuntL/0+kuLvTJ5akGoTjBQxSnSW95opzPeGky74HRU2obExJYqKvF0VfVJRNAqejwjIiFIbbjqV0Sk5391kFuhuBErQQDM1bOHGdxZ41HsJH29qNWIl7C33Yl10qERoqecgsJ1N/bS2ZEmWqm/zQh5GClCXPvYmzEqMYsMGM3vjbKdjDlc1Wh2w/eFclsXN9LSXh1mc35rtj46frcT6e5Kof87AIfC9hTgDvk9kAsyjaHMkSHSZthbZXCIcsD8qriNm5UqfFBYD79mPIP1S2YMQ2jscCsjHOZgYVrcm0kzDF21J1w6H0Lo7d1jw37LYlegBdtLQ9gYgqY2D5m+nxWuVoD5FZmpR+5JGtK+ootyLFF8aiFoHXd4op1JCxRLjgkmnZKXzw3kTQSpE7oa7CgzchtQmK2nqcqla1b5Qk7ilVcjooo=
|
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
## We stand with 🇺🇦
|
## We stand with 🇺🇦
|
||||||
|
|
||||||
[](https://drone.mailcow.email/mailcow/mailcow-dockerized) [](https://drone.mailcow.email/mailcow/mailcow-dockerized) [](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
[](https://github.com/mailcow/mailcow-dockerized/actions/workflows/integration_tests.yml)
|
||||||
|
[](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
||||||
[](https://twitter.com/mailcow_email)
|
[](https://twitter.com/mailcow_email)
|
||||||
|
|
||||||
## Want to support mailcow?
|
## Want to support mailcow?
|
||||||
|
|||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
# Security Policies and Procedures
|
||||||
|
|
||||||
|
This document outlines security procedures and general policies for the _mailcow: dockerized_ project as found on [mailcow-dockerized](https://github.com/mailcow/mailcow-dockerized).
|
||||||
|
|
||||||
|
* [Reporting a Vulnerability](#reporting-a-vulnerability)
|
||||||
|
* [Disclosure Policy](#disclosure-policy)
|
||||||
|
* [Comments on this Policy](#comments-on-this-policy)
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
The mailcow team and community take all security vulnerabilities
|
||||||
|
seriously. Thank you for improving the security of our open source
|
||||||
|
software. We appreciate your efforts and responsible disclosure and will
|
||||||
|
make every effort to acknowledge your contributions.
|
||||||
|
|
||||||
|
Report security vulnerabilities by emailing the mailcow team at:
|
||||||
|
|
||||||
|
info at servercow.de
|
||||||
|
|
||||||
|
mailcow team will acknowledge your email as soon as possible, and will
|
||||||
|
send a more detailed response afterwards indicating the next steps in
|
||||||
|
handling your report. After the initial reply to your report, the mailcow
|
||||||
|
team will endeavor to keep you informed of the progress towards a fix and
|
||||||
|
full announcement, and may ask for additional information or guidance.
|
||||||
|
|
||||||
|
Report security vulnerabilities in third-party modules to the person or
|
||||||
|
team maintaining the module.
|
||||||
|
|
||||||
|
## Disclosure Policy
|
||||||
|
|
||||||
|
When the mailcow team receives a security bug report, they will assign it
|
||||||
|
to a primary handler. This person will coordinate the fix and release
|
||||||
|
process, involving the following steps:
|
||||||
|
|
||||||
|
* Confirm the problem and determine the affected versions.
|
||||||
|
* Audit code to find any potential similar problems.
|
||||||
|
* Prepare fixes for all releases still under maintenance.
|
||||||
|
|
||||||
|
## Comments on this Policy
|
||||||
|
|
||||||
|
If you have suggestions on how this process could be improved please submit a
|
||||||
|
pull request.
|
||||||
Regular → Executable
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.15
|
FROM alpine:3.16
|
||||||
|
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM clamav/clamav:0.104.2-2_base
|
FROM clamav/clamav:0.105.1_base
|
||||||
|
|
||||||
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
@@ -8,8 +8,14 @@ RUN apk upgrade --no-cache \
|
|||||||
bind-tools \
|
bind-tools \
|
||||||
bash
|
bash
|
||||||
|
|
||||||
COPY clamd.sh ./
|
# init
|
||||||
|
COPY clamd.sh /clamd.sh
|
||||||
RUN chmod +x /sbin/tini
|
RUN chmod +x /sbin/tini
|
||||||
|
|
||||||
|
# healthcheck
|
||||||
|
COPY healthcheck.sh /healthcheck.sh
|
||||||
|
RUN chmod +x /healthcheck.sh
|
||||||
|
HEALTHCHECK --start-period=6m CMD "/healthcheck.sh"
|
||||||
|
|
||||||
ENTRYPOINT []
|
ENTRYPOINT []
|
||||||
CMD ["/sbin/tini", "-g", "--", "/clamd.sh"]
|
CMD ["/sbin/tini", "-g", "--", "/clamd.sh"]
|
||||||
Executable
+9
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ "${SKIP_CLAMD}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||||
|
echo "SKIP_CLAMD=y, skipping ClamAV..."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run clamd healthcheck
|
||||||
|
/usr/local/bin/clamdcheck.sh
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.15
|
FROM alpine:3.16
|
||||||
|
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ FROM debian:bullseye-slim
|
|||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ARG DOVECOT=2.3.18
|
ARG DOVECOT=2.3.19.1
|
||||||
ENV LC_ALL C
|
ENV LC_ALL C
|
||||||
ENV GOSU_VERSION 1.14
|
ENV GOSU_VERSION 1.14
|
||||||
|
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
||||||
cat <<EOF > /etc/dovecot/sogo_trusted_ip.conf
|
cat <<EOF > /etc/dovecot/sogo_trusted_ip.conf
|
||||||
# Autogenerated by mailcow
|
# Autogenerated by mailcow
|
||||||
remote ${IPV4_NETWORK}.248 {
|
remote ${IPV4_NETWORK}.248 {
|
||||||
@@ -349,6 +350,14 @@ sievec /var/vmail/sieve/global_sieve_after.sieve
|
|||||||
sievec /usr/lib/dovecot/sieve/report-spam.sieve
|
sievec /usr/lib/dovecot/sieve/report-spam.sieve
|
||||||
sievec /usr/lib/dovecot/sieve/report-ham.sieve
|
sievec /usr/lib/dovecot/sieve/report-ham.sieve
|
||||||
|
|
||||||
|
for file in /var/vmail/*/*/sieve/*.sieve ; do
|
||||||
|
if [[ "$file" == "/var/vmail/*/*/sieve/*.sieve" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
sievec "$file" "$(dirname "$file")/../.dovecot.svbin"
|
||||||
|
chown vmail:vmail "$(dirname "$file")/../.dovecot.svbin"
|
||||||
|
done
|
||||||
|
|
||||||
# Fix permissions
|
# Fix permissions
|
||||||
chown root:root /etc/dovecot/sql/*.conf
|
chown root:root /etc/dovecot/sql/*.conf
|
||||||
chown root:dovecot /etc/dovecot/sql/dovecot-dict-sql-sieve* /etc/dovecot/sql/dovecot-dict-sql-quota* /etc/dovecot/lua/passwd-verify.lua
|
chown root:dovecot /etc/dovecot/sql/dovecot-dict-sql-sieve* /etc/dovecot/sql/dovecot-dict-sql-quota* /etc/dovecot/lua/passwd-verify.lua
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ try:
|
|||||||
def query_mysql(query, headers = True, update = False):
|
def query_mysql(query, headers = True, update = False):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
cnx = mysql.connector.connect(unix_socket = '/var/run/mysqld/mysqld.sock', user=os.environ.get('DBUSER'), passwd=os.environ.get('DBPASS'), database=os.environ.get('DBNAME'), charset="utf8")
|
cnx = mysql.connector.connect(unix_socket = '/var/run/mysqld/mysqld.sock', user=os.environ.get('DBUSER'), passwd=os.environ.get('DBPASS'), database=os.environ.get('DBNAME'), charset="utf8mb4", collation="utf8mb4_general_ci")
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print('%s - trying again...' % (ex))
|
print('%s - trying again...' % (ex))
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
@@ -166,4 +166,4 @@ try:
|
|||||||
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'], attrs['quarantine_category'])
|
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'], attrs['quarantine_category'])
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
os.unlink(pidfile)
|
os.unlink(pidfile)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.15
|
FROM alpine:3.16
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ENV XTABLES_LIBDIR /usr/lib/xtables
|
ENV XTABLES_LIBDIR /usr/lib/xtables
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.15
|
FROM alpine:3.16
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
FROM php:8.0-fpm-alpine3.14
|
FROM php:8.0-fpm-alpine3.16
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ENV APCU_PECL 5.1.20
|
ENV APCU_PECL 5.1.21
|
||||||
ENV IMAGICK_PECL 3.5.1
|
ENV IMAGICK_PECL 3.7.0
|
||||||
# Mailparse is pulled from master branch
|
# Mailparse is pulled from master branch
|
||||||
#ENV MAILPARSE_PECL 3.0.2
|
#ENV MAILPARSE_PECL 3.0.2
|
||||||
ENV MEMCACHED_PECL 3.1.5
|
ENV MEMCACHED_PECL 3.2.0
|
||||||
ENV REDIS_PECL 5.3.4
|
ENV REDIS_PECL 5.3.7
|
||||||
|
|
||||||
RUN apk add -U --no-cache autoconf \
|
RUN apk add -U --no-cache autoconf \
|
||||||
aspell-dev \
|
aspell-dev \
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM debian:buster-slim
|
FROM debian:bullseye-slim
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|||||||
@@ -323,7 +323,19 @@ hosts = unix:/var/run/mysqld/mysqld.sock
|
|||||||
dbname = ${DBNAME}
|
dbname = ${DBNAME}
|
||||||
# First select queries domain and alias_domain to determine if domains are active.
|
# First select queries domain and alias_domain to determine if domains are active.
|
||||||
query = SELECT goto FROM alias
|
query = SELECT goto FROM alias
|
||||||
WHERE address='%s'
|
WHERE id IN (
|
||||||
|
SELECT COALESCE (
|
||||||
|
(
|
||||||
|
SELECT id FROM alias
|
||||||
|
WHERE address='%s'
|
||||||
|
AND (active='1' OR active='2')
|
||||||
|
), (
|
||||||
|
SELECT id FROM alias
|
||||||
|
WHERE address='@%d'
|
||||||
|
AND (active='1' OR active='2')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
AND active='1'
|
AND active='1'
|
||||||
AND (domain IN
|
AND (domain IN
|
||||||
(SELECT domain FROM domain
|
(SELECT domain FROM domain
|
||||||
@@ -354,7 +366,7 @@ query = SELECT goto FROM alias
|
|||||||
WHERE alias_domain.alias_domain = '%d'
|
WHERE alias_domain.alias_domain = '%d'
|
||||||
AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain)
|
AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain)
|
||||||
AND (mailbox.active = '1' OR mailbox.active ='2')
|
AND (mailbox.active = '1' OR mailbox.active ='2')
|
||||||
AND alias_domain.active='1'
|
AND alias_domain.active='1';
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# MX based routing
|
# MX based routing
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@version: 3.19
|
@version: 3.28
|
||||||
@include "scl.conf"
|
@include "scl.conf"
|
||||||
options {
|
options {
|
||||||
chain_hostnames(off);
|
chain_hostnames(off);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@version: 3.19
|
@version: 3.28
|
||||||
@include "scl.conf"
|
@include "scl.conf"
|
||||||
options {
|
options {
|
||||||
chain_hostnames(off);
|
chain_hostnames(off);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ FROM debian:bullseye-slim
|
|||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ARG SOGO_DEBIAN_REPOSITORY=http://packages.inverse.ca/SOGo/nightly/5/debian/
|
ARG SOGO_DEBIAN_REPOSITORY=http://packages.sogo.nu/nightly/5/debian/
|
||||||
ENV LC_ALL C
|
ENV LC_ALL C
|
||||||
ENV GOSU_VERSION 1.14
|
ENV GOSU_VERSION 1.14
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
|
|||||||
&& gosu nobody true \
|
&& gosu nobody true \
|
||||||
&& mkdir /usr/share/doc/sogo \
|
&& mkdir /usr/share/doc/sogo \
|
||||||
&& touch /usr/share/doc/sogo/empty.sh \
|
&& touch /usr/share/doc/sogo/empty.sh \
|
||||||
&& apt-key adv --keyserver keyserver.ubuntu.com --recv-key 0x810273C4 \
|
&& apt-key adv --keyserver keys.openpgp.org --recv-key 74FFC6D72B925A34B5D356BDF8A27B36A6E2EAE9 \
|
||||||
&& echo "deb ${SOGO_DEBIAN_REPOSITORY} bullseye bullseye" > /etc/apt/sources.list.d/sogo.list \
|
&& echo "deb ${SOGO_DEBIAN_REPOSITORY} bullseye bullseye" > /etc/apt/sources.list.d/sogo.list \
|
||||||
&& apt-get update && apt-get install -y --no-install-recommends \
|
&& apt-get update && apt-get install -y --no-install-recommends \
|
||||||
sogo \
|
sogo \
|
||||||
@@ -52,4 +52,4 @@ RUN chmod +x /bootstrap-sogo.sh \
|
|||||||
|
|
||||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
|
|
||||||
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||||
@@ -142,6 +142,10 @@ cat <<EOF > /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
|||||||
<string>mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_acl</string>
|
<string>mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_acl</string>
|
||||||
<key>SOGoIMAPServer</key>
|
<key>SOGoIMAPServer</key>
|
||||||
<string>imap://${IPV4_NETWORK}.250:143/?TLS=YES&tlsVerifyMode=none</string>
|
<string>imap://${IPV4_NETWORK}.250:143/?TLS=YES&tlsVerifyMode=none</string>
|
||||||
|
<key>SOGoSieveServer</key>
|
||||||
|
<string>sieve://${IPV4_NETWORK}.250:4190/?TLS=YES&tlsVerifyMode=none</string>
|
||||||
|
<key>SOGoSMTPServer</key>
|
||||||
|
<string>smtp://${IPV4_NETWORK}.253:588/?TLS=YES&tlsVerifyMode=none</string>
|
||||||
<key>SOGoTrustProxyAuthentication</key>
|
<key>SOGoTrustProxyAuthentication</key>
|
||||||
<string>YES</string>
|
<string>YES</string>
|
||||||
<key>SOGoEncryptionKey</key>
|
<key>SOGoEncryptionKey</key>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.15
|
FROM alpine:3.16
|
||||||
|
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.15
|
FROM alpine:3.16
|
||||||
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ symbols {
|
|||||||
"ENCRYPTED_CHAT" {
|
"ENCRYPTED_CHAT" {
|
||||||
score = -20.0;
|
score = -20.0;
|
||||||
}
|
}
|
||||||
|
"SOGO_CONTACT" {
|
||||||
|
score = -99.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group "MX" {
|
group "MX" {
|
||||||
|
|||||||
@@ -32,8 +32,6 @@
|
|||||||
// );
|
// );
|
||||||
|
|
||||||
// self-signed is not trusted anymore
|
// self-signed is not trusted anymore
|
||||||
SOGoSieveServer = "sieve://dovecot:4190/?TLS=YES&tlsVerifyMode=none";
|
|
||||||
SOGoSMTPServer = "smtp://postfix:588/?TLS=YES&tlsVerifyMode=none";
|
|
||||||
WOPort = "0.0.0.0:20000";
|
WOPort = "0.0.0.0:20000";
|
||||||
SOGoMemcachedHost = "memcached";
|
SOGoMemcachedHost = "memcached";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: -moz-scrollbars-vertical;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
+2
-43
@@ -5,56 +5,15 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Swagger UI</title>
|
<title>Swagger UI</title>
|
||||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="index.css" />
|
||||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||||
<style>
|
|
||||||
html
|
|
||||||
{
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: -moz-scrollbars-vertical;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
*,
|
|
||||||
*:before,
|
|
||||||
*:after
|
|
||||||
{
|
|
||||||
box-sizing: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
body
|
|
||||||
{
|
|
||||||
margin:0;
|
|
||||||
background: #fafafa;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="swagger-ui"></div>
|
<div id="swagger-ui"></div>
|
||||||
|
|
||||||
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
||||||
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
||||||
<script>
|
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
|
||||||
window.onload = function() {
|
|
||||||
// Begin Swagger UI call region
|
|
||||||
const ui = SwaggerUIBundle({
|
|
||||||
urls: [{url: "/api/openapi.yaml", name: "mailcow API"}],
|
|
||||||
dom_id: '#swagger-ui',
|
|
||||||
deepLinking: true,
|
|
||||||
presets: [
|
|
||||||
SwaggerUIBundle.presets.apis,
|
|
||||||
SwaggerUIStandalonePreset
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
SwaggerUIBundle.plugins.DownloadUrl
|
|
||||||
],
|
|
||||||
layout: "StandaloneLayout"
|
|
||||||
});
|
|
||||||
// End Swagger UI call region
|
|
||||||
|
|
||||||
window.ui = ui;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
var isValid, qp, arr;
|
var isValid, qp, arr;
|
||||||
|
|
||||||
if (/code|token|error/.test(window.location.hash)) {
|
if (/code|token|error/.test(window.location.hash)) {
|
||||||
qp = window.location.hash.substring(1);
|
qp = window.location.hash.substring(1).replace('?', '&');
|
||||||
} else {
|
} else {
|
||||||
qp = location.search.substring(1);
|
qp = location.search.substring(1);
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
authId: oauth2.auth.name,
|
authId: oauth2.auth.name,
|
||||||
source: "auth",
|
source: "auth",
|
||||||
level: "warning",
|
level: "warning",
|
||||||
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
|
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
authId: oauth2.auth.name,
|
authId: oauth2.auth.name,
|
||||||
source: "auth",
|
source: "auth",
|
||||||
level: "error",
|
level: "error",
|
||||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -67,9 +67,13 @@
|
|||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', function () {
|
if (document.readyState !== 'loading') {
|
||||||
run();
|
run();
|
||||||
});
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
run();
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+111
-53
@@ -209,10 +209,17 @@ paths:
|
|||||||
- app_passwd
|
- app_passwd
|
||||||
- add
|
- add
|
||||||
- active: "1"
|
- active: "1"
|
||||||
app_name: emclient
|
username: info@domain.tld
|
||||||
|
app_name: wordpress
|
||||||
app_passwd: keyleudecticidechothistishownsan31
|
app_passwd: keyleudecticidechothistishownsan31
|
||||||
app_passwd2: keyleudecticidechothistishownsan31
|
app_passwd2: keyleudecticidechothistishownsan31
|
||||||
username: hello@mailcow.email
|
protocols:
|
||||||
|
- imap_access
|
||||||
|
- dav_access
|
||||||
|
- smtp_access
|
||||||
|
- eas_access
|
||||||
|
- pop3_access
|
||||||
|
- sieve_access
|
||||||
msg: app_passwd_added
|
msg: app_passwd_added
|
||||||
type: success
|
type: success
|
||||||
schema:
|
schema:
|
||||||
@@ -249,6 +256,13 @@ paths:
|
|||||||
app_name: wordpress
|
app_name: wordpress
|
||||||
app_passwd: keyleudecticidechothistishownsan31
|
app_passwd: keyleudecticidechothistishownsan31
|
||||||
app_passwd2: keyleudecticidechothistishownsan31
|
app_passwd2: keyleudecticidechothistishownsan31
|
||||||
|
protocols:
|
||||||
|
- imap_access
|
||||||
|
- dav_access
|
||||||
|
- smtp_access
|
||||||
|
- eas_access
|
||||||
|
- pop3_access
|
||||||
|
- sieve_access
|
||||||
properties:
|
properties:
|
||||||
active:
|
active:
|
||||||
description: is alias active or not
|
description: is alias active or not
|
||||||
@@ -504,21 +518,23 @@ paths:
|
|||||||
- domain.tld
|
- domain.tld
|
||||||
type: success
|
type: success
|
||||||
schema:
|
schema:
|
||||||
properties:
|
type: array
|
||||||
log:
|
items:
|
||||||
description: contains request object
|
type: object
|
||||||
items: {}
|
properties:
|
||||||
type: array
|
log:
|
||||||
msg:
|
description: contains request object
|
||||||
items: {}
|
items: {}
|
||||||
type: array
|
type: array
|
||||||
type:
|
msg:
|
||||||
enum:
|
items: {}
|
||||||
- success
|
type: array
|
||||||
- danger
|
type:
|
||||||
- error
|
enum:
|
||||||
type: string
|
- success
|
||||||
type: object
|
- danger
|
||||||
|
- error
|
||||||
|
type: string
|
||||||
description: OK
|
description: OK
|
||||||
headers: {}
|
headers: {}
|
||||||
tags:
|
tags:
|
||||||
@@ -565,6 +581,11 @@ paths:
|
|||||||
domain:
|
domain:
|
||||||
description: Fully qualified domain name
|
description: Fully qualified domain name
|
||||||
type: string
|
type: string
|
||||||
|
gal:
|
||||||
|
description: >-
|
||||||
|
is domain global address list active or not, it enables
|
||||||
|
shared contacts accross domain in SOGo webmail
|
||||||
|
type: boolean
|
||||||
mailboxes:
|
mailboxes:
|
||||||
description: limit count of mailboxes associated with this domain
|
description: limit count of mailboxes associated with this domain
|
||||||
type: number
|
type: number
|
||||||
@@ -582,6 +603,9 @@ paths:
|
|||||||
if not, them you have to create "dummy" mailbox for each
|
if not, them you have to create "dummy" mailbox for each
|
||||||
address to relay
|
address to relay
|
||||||
type: boolean
|
type: boolean
|
||||||
|
relay_unknown_only:
|
||||||
|
description: Relay non-existing mailboxes only. Existing mailboxes will be delivered locally.
|
||||||
|
type: boolean
|
||||||
rl_frame:
|
rl_frame:
|
||||||
enum:
|
enum:
|
||||||
- s
|
- s
|
||||||
@@ -592,6 +616,11 @@ paths:
|
|||||||
rl_value:
|
rl_value:
|
||||||
description: rate limit value
|
description: rate limit value
|
||||||
type: number
|
type: number
|
||||||
|
tags:
|
||||||
|
description: tags for this Domain
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
summary: Create domain
|
summary: Create domain
|
||||||
/api/v1/add/domain-admin:
|
/api/v1/add/domain-admin:
|
||||||
@@ -1938,21 +1967,23 @@ paths:
|
|||||||
- domain2.tld
|
- domain2.tld
|
||||||
type: success
|
type: success
|
||||||
schema:
|
schema:
|
||||||
properties:
|
type: array
|
||||||
log:
|
items:
|
||||||
description: contains request object
|
type: object
|
||||||
items: {}
|
properties:
|
||||||
type: array
|
log:
|
||||||
msg:
|
description: contains request object
|
||||||
items: {}
|
items: {}
|
||||||
type: array
|
type: array
|
||||||
type:
|
msg:
|
||||||
enum:
|
items: {}
|
||||||
- success
|
type: array
|
||||||
- danger
|
type:
|
||||||
- error
|
enum:
|
||||||
type: string
|
- success
|
||||||
type: object
|
- danger
|
||||||
|
- error
|
||||||
|
type: string
|
||||||
description: OK
|
description: OK
|
||||||
headers: {}
|
headers: {}
|
||||||
tags:
|
tags:
|
||||||
@@ -1963,14 +1994,15 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
|
type: object
|
||||||
example:
|
example:
|
||||||
- domain.tld
|
- domain.tld
|
||||||
- domain2.tld
|
- domain2.tld
|
||||||
properties:
|
properties:
|
||||||
items:
|
items:
|
||||||
description: contains list of domains you want to delete
|
type: array
|
||||||
type: object
|
items:
|
||||||
type: object
|
type: string
|
||||||
summary: Delete domain
|
summary: Delete domain
|
||||||
/api/v1/delete/domain-admin:
|
/api/v1/delete/domain-admin:
|
||||||
post:
|
post:
|
||||||
@@ -2958,23 +2990,25 @@ paths:
|
|||||||
$ref: "#/components/responses/Unauthorized"
|
$ref: "#/components/responses/Unauthorized"
|
||||||
"200":
|
"200":
|
||||||
content:
|
content:
|
||||||
"*/*":
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
properties:
|
type: array
|
||||||
log:
|
items:
|
||||||
description: contains request object
|
type: object
|
||||||
items: {}
|
properties:
|
||||||
type: array
|
log:
|
||||||
msg:
|
type: array
|
||||||
items: {}
|
description: contains request object
|
||||||
type: array
|
items: {}
|
||||||
type:
|
msg:
|
||||||
enum:
|
type: array
|
||||||
- success
|
items: {}
|
||||||
- danger
|
type:
|
||||||
- error
|
enum:
|
||||||
type: string
|
- success
|
||||||
type: object
|
- danger
|
||||||
|
- error
|
||||||
|
type: string
|
||||||
description: OK
|
description: OK
|
||||||
headers: {}
|
headers: {}
|
||||||
tags:
|
tags:
|
||||||
@@ -3042,13 +3076,33 @@ paths:
|
|||||||
if not, them you have to create "dummy" mailbox for each
|
if not, them you have to create "dummy" mailbox for each
|
||||||
address to relay
|
address to relay
|
||||||
type: boolean
|
type: boolean
|
||||||
|
relay_unknown_only:
|
||||||
|
description: Relay non-existing mailboxes only. Existing mailboxes will be delivered locally.
|
||||||
|
type: boolean
|
||||||
relayhost:
|
relayhost:
|
||||||
description: id of relayhost
|
description: id of relayhost
|
||||||
type: number
|
type: number
|
||||||
|
rl_frame:
|
||||||
|
enum:
|
||||||
|
- s
|
||||||
|
- m
|
||||||
|
- h
|
||||||
|
- d
|
||||||
|
type: string
|
||||||
|
rl_value:
|
||||||
|
description: rate limit value
|
||||||
|
type: number
|
||||||
|
tags:
|
||||||
|
description: tags for this Domain
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
items:
|
items:
|
||||||
description: contains list of domain names you want update
|
description: contains list of domain names you want update
|
||||||
type: object
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
summary: Update domain
|
summary: Update domain
|
||||||
/api/v1/edit/fail2ban:
|
/api/v1/edit/fail2ban:
|
||||||
@@ -3939,6 +3993,8 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: tags
|
name: tags
|
||||||
required: false
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
- description: e.g. api-key-string
|
- description: e.g. api-key-string
|
||||||
example: api-key-string
|
example: api-key-string
|
||||||
in: header
|
in: header
|
||||||
@@ -4498,6 +4554,8 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: tags
|
name: tags
|
||||||
required: false
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
- description: e.g. api-key-string
|
- description: e.g. api-key-string
|
||||||
example: api-key-string
|
example: api-key-string
|
||||||
in: header
|
in: header
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
window.onload = function() {
|
||||||
|
// Begin Swagger UI call region
|
||||||
|
const ui = SwaggerUIBundle({
|
||||||
|
urls: [{url: "/api/openapi.yaml", name: "mailcow API"}],
|
||||||
|
dom_id: '#swagger-ui',
|
||||||
|
deepLinking: true,
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
SwaggerUIBundle.plugins.DownloadUrl
|
||||||
|
],
|
||||||
|
layout: "StandaloneLayout"
|
||||||
|
});
|
||||||
|
// End Swagger UI call region
|
||||||
|
|
||||||
|
window.ui = ui;
|
||||||
|
};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-2
@@ -128,10 +128,10 @@ table.footable > tbody > tr.footable-empty > th {
|
|||||||
content: "\f130";
|
content: "\f130";
|
||||||
}
|
}
|
||||||
.fooicon-plus:before {
|
.fooicon-plus:before {
|
||||||
content: "\f4fc";
|
content: "\f4fd";
|
||||||
}
|
}
|
||||||
.fooicon-minus:before {
|
.fooicon-minus:before {
|
||||||
content: "\f2e8";
|
content: "\f2e9";
|
||||||
}
|
}
|
||||||
.fooicon-search:before {
|
.fooicon-search:before {
|
||||||
content: "\f52a";
|
content: "\f52a";
|
||||||
|
|||||||
@@ -260,6 +260,18 @@ code {
|
|||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-group-item.webauthn-authenticator-selection,
|
||||||
|
.list-group-item.totp-authenticator-selection,
|
||||||
|
.list-group-item.yubi_otp-authenticator-selection {
|
||||||
|
border-radius: 0px !important;
|
||||||
|
}
|
||||||
|
.pending-tfa-collapse {
|
||||||
|
padding: 10px;
|
||||||
|
background: #fbfbfb;
|
||||||
|
border: 1px solid #ededed;
|
||||||
|
min-height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
.tag-box {
|
.tag-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -296,4 +308,3 @@ code {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
session_start();
|
session_start();
|
||||||
unset($_SESSION['pending_mailcow_cc_username']);
|
unset($_SESSION['pending_mailcow_cc_username']);
|
||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_method']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -23,14 +23,43 @@ if (is_array($alertbox_log_parser)) {
|
|||||||
unset($_SESSION['return']);
|
unset($_SESSION['return']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// map tfa details for twig
|
||||||
|
$pending_tfa_authmechs = [];
|
||||||
|
foreach($_SESSION['pending_tfa_methods'] as $authdata){
|
||||||
|
$pending_tfa_authmechs[$authdata['authmech']] = false;
|
||||||
|
}
|
||||||
|
if (isset($pending_tfa_authmechs['webauthn'])) {
|
||||||
|
$pending_tfa_authmechs['webauthn'] = true;
|
||||||
|
}
|
||||||
|
if (!isset($pending_tfa_authmechs['webauthn'])
|
||||||
|
&& isset($pending_tfa_authmechs['yubi_otp'])) {
|
||||||
|
$pending_tfa_authmechs['yubi_otp'] = true;
|
||||||
|
}
|
||||||
|
if (!isset($pending_tfa_authmechs['webauthn'])
|
||||||
|
&& !isset($pending_tfa_authmechs['yubi_otp'])
|
||||||
|
&& isset($pending_tfa_authmechs['totp'])) {
|
||||||
|
$pending_tfa_authmechs['totp'] = true;
|
||||||
|
}
|
||||||
|
if (isset($pending_tfa_authmechs['u2f'])) {
|
||||||
|
$pending_tfa_authmechs['u2f'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
// globals
|
// globals
|
||||||
$globalVariables = [
|
$globalVariables = [
|
||||||
'mailcow_info' => array(
|
'mailcow_info' => array(
|
||||||
'version_tag' => $GLOBALS['MAILCOW_GIT_VERSION'],
|
'version_tag' => $GLOBALS['MAILCOW_GIT_VERSION'],
|
||||||
'git_project_url' => $GLOBALS['MAILCOW_GIT_URL']
|
'last_version_tag' => $GLOBALS['MAILCOW_LAST_GIT_VERSION'],
|
||||||
|
'git_owner' => $GLOBALS['MAILCOW_GIT_OWNER'],
|
||||||
|
'git_repo' => $GLOBALS['MAILCOW_GIT_REPO'],
|
||||||
|
'git_project_url' => $GLOBALS['MAILCOW_GIT_URL'],
|
||||||
|
'git_commit' => $GLOBALS['MAILCOW_GIT_COMMIT'],
|
||||||
|
'git_commit_date' => $GLOBALS['MAILCOW_GIT_COMMIT_DATE'],
|
||||||
|
'mailcow_branch' => $GLOBALS['MAILCOW_BRANCH'],
|
||||||
|
'updated_at' => $GLOBALS['MAILCOW_UPDATEDAT']
|
||||||
),
|
),
|
||||||
'js_path' => '/cache/'.basename($JSPath),
|
'js_path' => '/cache/'.basename($JSPath),
|
||||||
'pending_tfa_method' => @$_SESSION['pending_tfa_method'],
|
'pending_tfa_methods' => @$_SESSION['pending_tfa_methods'],
|
||||||
|
'pending_tfa_authmechs' => $pending_tfa_authmechs,
|
||||||
'pending_mailcow_cc_username' => @$_SESSION['pending_mailcow_cc_username'],
|
'pending_mailcow_cc_username' => @$_SESSION['pending_mailcow_cc_username'],
|
||||||
'lang_footer' => json_encode($lang['footer']),
|
'lang_footer' => json_encode($lang['footer']),
|
||||||
'lang_acl' => json_encode($lang['acl']),
|
'lang_acl' => json_encode($lang['acl']),
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ function dkim($_action, $_data = null, $privkey = false) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
dkim('delete', (array)$domain);
|
dkim('delete', array('domains' => $domain));
|
||||||
$redis->hSet('DKIM_PUB_KEYS', $domain, $pem_public_key);
|
$redis->hSet('DKIM_PUB_KEYS', $domain, $pem_public_key);
|
||||||
$redis->hSet('DKIM_SELECTORS', $domain, $dkim_selector);
|
$redis->hSet('DKIM_SELECTORS', $domain, $dkim_selector);
|
||||||
$redis->hSet('DKIM_PRIV_KEYS', $dkim_selector . '.' . $domain, $private_key_normalized);
|
$redis->hSet('DKIM_PRIV_KEYS', $dkim_selector . '.' . $domain, $private_key_normalized);
|
||||||
|
|||||||
+249
-179
@@ -830,11 +830,15 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|||||||
$stmt->execute(array(':user' => $user));
|
$stmt->execute(array(':user' => $user));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
|
// verify password
|
||||||
if (verify_hash($row['password'], $pass)) {
|
if (verify_hash($row['password'], $pass)) {
|
||||||
if (get_tfa($user)['name'] != "none") {
|
// check for tfa authenticators
|
||||||
|
$authenticators = get_tfa($user);
|
||||||
|
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) {
|
||||||
|
// active tfa authenticators found, set pending user login
|
||||||
$_SESSION['pending_mailcow_cc_username'] = $user;
|
$_SESSION['pending_mailcow_cc_username'] = $user;
|
||||||
$_SESSION['pending_mailcow_cc_role'] = "admin";
|
$_SESSION['pending_mailcow_cc_role'] = "admin";
|
||||||
$_SESSION['pending_tfa_method'] = get_tfa($user)['name'];
|
$_SESSION['pending_tfa_methods'] = $authenticators['additional'];
|
||||||
unset($_SESSION['ldelay']);
|
unset($_SESSION['ldelay']);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'info',
|
'type' => 'info',
|
||||||
@@ -842,8 +846,7 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|||||||
'msg' => 'awaiting_tfa_confirmation'
|
'msg' => 'awaiting_tfa_confirmation'
|
||||||
);
|
);
|
||||||
return "pending";
|
return "pending";
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
unset($_SESSION['ldelay']);
|
unset($_SESSION['ldelay']);
|
||||||
// Reactivate TFA if it was set to "deactivate TFA for next login"
|
// Reactivate TFA if it was set to "deactivate TFA for next login"
|
||||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
|
||||||
@@ -866,11 +869,14 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|||||||
$stmt->execute(array(':user' => $user));
|
$stmt->execute(array(':user' => $user));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
|
// verify password
|
||||||
if (verify_hash($row['password'], $pass) !== false) {
|
if (verify_hash($row['password'], $pass) !== false) {
|
||||||
if (get_tfa($user)['name'] != "none") {
|
// check for tfa authenticators
|
||||||
|
$authenticators = get_tfa($user);
|
||||||
|
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) {
|
||||||
$_SESSION['pending_mailcow_cc_username'] = $user;
|
$_SESSION['pending_mailcow_cc_username'] = $user;
|
||||||
$_SESSION['pending_mailcow_cc_role'] = "domainadmin";
|
$_SESSION['pending_mailcow_cc_role'] = "domainadmin";
|
||||||
$_SESSION['pending_tfa_method'] = get_tfa($user)['name'];
|
$_SESSION['pending_tfa_methods'] = $authenticators['additional'];
|
||||||
unset($_SESSION['ldelay']);
|
unset($_SESSION['ldelay']);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'info',
|
'type' => 'info',
|
||||||
@@ -929,15 +935,37 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|||||||
$stmt->execute(array(':user' => $user));
|
$stmt->execute(array(':user' => $user));
|
||||||
$rows = array_merge($rows, $stmt->fetchAll(PDO::FETCH_ASSOC));
|
$rows = array_merge($rows, $stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
}
|
}
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
|
// verify password
|
||||||
if (verify_hash($row['password'], $pass) !== false) {
|
if (verify_hash($row['password'], $pass) !== false) {
|
||||||
unset($_SESSION['ldelay']);
|
if (!array_key_exists("app_passwd_id", $row)){
|
||||||
$_SESSION['return'][] = array(
|
// password is not a app password
|
||||||
'type' => 'success',
|
// check for tfa authenticators
|
||||||
'log' => array(__FUNCTION__, $user, '*'),
|
$authenticators = get_tfa($user);
|
||||||
'msg' => array('logged_in_as', $user)
|
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 &&
|
||||||
);
|
$app_passwd_data['eas'] !== true && $app_passwd_data['dav'] !== true) {
|
||||||
if ($app_passwd_data['eas'] === true || $app_passwd_data['dav'] === true) {
|
// authenticators found, init TFA flow
|
||||||
|
$_SESSION['pending_mailcow_cc_username'] = $user;
|
||||||
|
$_SESSION['pending_mailcow_cc_role'] = "user";
|
||||||
|
$_SESSION['pending_tfa_methods'] = $authenticators['additional'];
|
||||||
|
unset($_SESSION['ldelay']);
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'log' => array(__FUNCTION__, $user, '*'),
|
||||||
|
'msg' => array('logged_in_as', $user)
|
||||||
|
);
|
||||||
|
return "pending";
|
||||||
|
} else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
|
||||||
|
// no authenticators found, login successfull
|
||||||
|
// Reactivate TFA if it was set to "deactivate TFA for next login"
|
||||||
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
|
||||||
|
$stmt->execute(array(':user' => $user));
|
||||||
|
|
||||||
|
unset($_SESSION['ldelay']);
|
||||||
|
return "user";
|
||||||
|
}
|
||||||
|
} elseif ($app_passwd_data['eas'] === true || $app_passwd_data['dav'] === true) {
|
||||||
|
// password is a app password
|
||||||
$service = ($app_passwd_data['eas'] === true) ? 'EAS' : 'DAV';
|
$service = ($app_passwd_data['eas'] === true) ? 'EAS' : 'DAV';
|
||||||
$stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES (:service, :app_id, :username, :remote_addr)");
|
$stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES (:service, :app_id, :username, :remote_addr)");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
@@ -946,8 +974,10 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|||||||
':username' => $user,
|
':username' => $user,
|
||||||
':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
|
':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
|
||||||
));
|
));
|
||||||
|
|
||||||
|
unset($_SESSION['ldelay']);
|
||||||
|
return "user";
|
||||||
}
|
}
|
||||||
return "user";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1142,47 +1172,46 @@ function set_tfa($_data) {
|
|||||||
global $yubi;
|
global $yubi;
|
||||||
global $tfa;
|
global $tfa;
|
||||||
$_data_log = $_data;
|
$_data_log = $_data;
|
||||||
|
$access_denied = null;
|
||||||
!isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*';
|
!isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*';
|
||||||
$username = $_SESSION['mailcow_cc_username'];
|
$username = $_SESSION['mailcow_cc_username'];
|
||||||
if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
|
|
||||||
$_SESSION['return'][] = array(
|
// check for empty user and role
|
||||||
'type' => 'danger',
|
if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) $access_denied = true;
|
||||||
'log' => array(__FUNCTION__, $_data_log),
|
|
||||||
'msg' => 'access_denied'
|
// check admin confirm password
|
||||||
);
|
if ($access_denied === null) {
|
||||||
return false;
|
$stmt = $pdo->prepare("SELECT `password` FROM `admin`
|
||||||
}
|
WHERE `username` = :username");
|
||||||
$stmt = $pdo->prepare("SELECT `password` FROM `admin`
|
$stmt->execute(array(':username' => $username));
|
||||||
WHERE `username` = :username");
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
$stmt->execute(array(':username' => $username));
|
if ($row) {
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true;
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
else $access_denied = false;
|
||||||
if (!empty($num_results)) {
|
|
||||||
if (!verify_hash($row['password'], $_data["confirm_password"])) {
|
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $_data_log),
|
|
||||||
'msg' => 'access_denied'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
|
|
||||||
WHERE `username` = :username");
|
|
||||||
$stmt->execute(array(':username' => $username));
|
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
|
||||||
if (!empty($num_results)) {
|
|
||||||
if (!verify_hash($row['password'], $_data["confirm_password"])) {
|
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $_data_log),
|
|
||||||
'msg' => 'access_denied'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check mailbox confirm password
|
||||||
|
if ($access_denied === null) {
|
||||||
|
$stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
|
||||||
|
WHERE `username` = :username");
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($row) {
|
||||||
|
if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true;
|
||||||
|
else $access_denied = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set access_denied error
|
||||||
|
if ($access_denied){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_data_log),
|
||||||
|
'msg' => 'access_denied'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
switch ($_data["tfa_method"]) {
|
switch ($_data["tfa_method"]) {
|
||||||
case "yubi_otp":
|
case "yubi_otp":
|
||||||
@@ -1220,8 +1249,7 @@ function set_tfa($_data) {
|
|||||||
$yubico_modhex_id = substr($_data["otp_token"], 0, 12);
|
$yubico_modhex_id = substr($_data["otp_token"], 0, 12);
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa`
|
$stmt = $pdo->prepare("DELETE FROM `tfa`
|
||||||
WHERE `username` = :username
|
WHERE `username` = :username
|
||||||
AND (`authmech` != 'yubi_otp')
|
AND (`authmech` = 'yubi_otp' AND `secret` LIKE :modhex)");
|
||||||
OR (`authmech` = 'yubi_otp' AND `secret` LIKE :modhex)");
|
|
||||||
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
||||||
$stmt = $pdo->prepare("INSERT INTO `tfa` (`key_id`, `username`, `authmech`, `active`, `secret`) VALUES
|
$stmt = $pdo->prepare("INSERT INTO `tfa` (`key_id`, `username`, `authmech`, `active`, `secret`) VALUES
|
||||||
(:key_id, :username, 'yubi_otp', '1', :secret)");
|
(:key_id, :username, 'yubi_otp', '1', :secret)");
|
||||||
@@ -1265,9 +1293,6 @@ function set_tfa($_data) {
|
|||||||
case "webauthn":
|
case "webauthn":
|
||||||
$key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
|
$key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
|
||||||
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `authmech` != 'webauthn'");
|
|
||||||
$stmt->execute(array(':username' => $username));
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`)
|
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`)
|
||||||
VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')");
|
VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
@@ -1439,25 +1464,27 @@ function unset_tfa_key($_data) {
|
|||||||
global $pdo;
|
global $pdo;
|
||||||
global $lang;
|
global $lang;
|
||||||
$_data_log = $_data;
|
$_data_log = $_data;
|
||||||
|
$access_denied = null;
|
||||||
$id = intval($_data['unset_tfa_key']);
|
$id = intval($_data['unset_tfa_key']);
|
||||||
$username = $_SESSION['mailcow_cc_username'];
|
$username = $_SESSION['mailcow_cc_username'];
|
||||||
if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
|
|
||||||
$_SESSION['return'][] = array(
|
// check for empty user and role
|
||||||
'type' => 'danger',
|
if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) $access_denied = true;
|
||||||
'log' => array(__FUNCTION__, $_data_log),
|
|
||||||
'msg' => 'access_denied'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
if (!is_numeric($id)) {
|
if (!is_numeric($id)) $access_denied = true;
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
|
// set access_denied error
|
||||||
|
if ($access_denied){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_data_log),
|
'log' => array(__FUNCTION__, $_data_log),
|
||||||
'msg' => 'access_denied'
|
'msg' => 'access_denied'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if it's last key
|
||||||
$stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
|
$stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
|
||||||
WHERE `username` = :username AND `active` = '1'");
|
WHERE `username` = :username AND `active` = '1'");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
@@ -1470,6 +1497,8 @@ function unset_tfa_key($_data) {
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete key
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `id` = :id");
|
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `id` = :id");
|
||||||
$stmt->execute(array(':username' => $username, ':id' => $id));
|
$stmt->execute(array(':username' => $username, ':id' => $id));
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
@@ -1487,7 +1516,7 @@ function unset_tfa_key($_data) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function get_tfa($username = null) {
|
function get_tfa($username = null, $id = null) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
if (isset($_SESSION['mailcow_cc_username'])) {
|
if (isset($_SESSION['mailcow_cc_username'])) {
|
||||||
$username = $_SESSION['mailcow_cc_username'];
|
$username = $_SESSION['mailcow_cc_username'];
|
||||||
@@ -1495,95 +1524,119 @@ function get_tfa($username = null) {
|
|||||||
elseif (empty($username)) {
|
elseif (empty($username)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare("SELECT * FROM `tfa`
|
|
||||||
WHERE `username` = :username AND `active` = '1'");
|
|
||||||
$stmt->execute(array(':username' => $username));
|
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (isset($row["authmech"])) {
|
if (!isset($id)){
|
||||||
switch ($row["authmech"]) {
|
// fetch all tfa methods - just get information about possible authenticators
|
||||||
case "yubi_otp":
|
$stmt = $pdo->prepare("SELECT `id`, `key_id`, `authmech` FROM `tfa`
|
||||||
$data['name'] = "yubi_otp";
|
WHERE `username` = :username AND `active` = '1'");
|
||||||
$data['pretty'] = "Yubico OTP";
|
$stmt->execute(array(':username' => $username));
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `key_id`, RIGHT(`secret`, 12) AS 'modhex' FROM `tfa` WHERE `authmech` = 'yubi_otp' AND `username` = :username");
|
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
$stmt->execute(array(
|
|
||||||
':username' => $username,
|
// no tfa methods found
|
||||||
));
|
if (count($results) == 0) {
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$data['name'] = 'none';
|
||||||
while($row = array_shift($rows)) {
|
$data['pretty'] = "-";
|
||||||
$data['additional'][] = $row;
|
$data['additional'] = array();
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['additional'] = $results;
|
||||||
|
return $data;
|
||||||
|
} else {
|
||||||
|
// fetch specific authenticator details by id
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM `tfa`
|
||||||
|
WHERE `username` = :username AND `id` = :id AND `active` = '1'");
|
||||||
|
$stmt->execute(array(':username' => $username, ':id' => $id));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (isset($row["authmech"])) {
|
||||||
|
switch ($row["authmech"]) {
|
||||||
|
case "yubi_otp":
|
||||||
|
$data['name'] = "yubi_otp";
|
||||||
|
$data['pretty'] = "Yubico OTP";
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`, `key_id`, RIGHT(`secret`, 12) AS 'modhex' FROM `tfa` WHERE `authmech` = 'yubi_otp' AND `username` = :username AND `id` = :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$data['additional'][] = $row;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
|
// u2f - deprecated, should be removed
|
||||||
|
case "u2f":
|
||||||
|
$data['name'] = "u2f";
|
||||||
|
$data['pretty'] = "Fido U2F";
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username AND `id` = :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$data['additional'][] = $row;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
|
case "hotp":
|
||||||
|
$data['name'] = "hotp";
|
||||||
|
$data['pretty'] = "HMAC-based OTP";
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
|
case "totp":
|
||||||
|
$data['name'] = "totp";
|
||||||
|
$data['pretty'] = "Time-based OTP";
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`, `key_id`, `secret` FROM `tfa` WHERE `authmech` = 'totp' AND `username` = :username AND `id` = :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$data['additional'][] = $row;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
|
case "webauthn":
|
||||||
|
$data['name'] = "webauthn";
|
||||||
|
$data['pretty'] = "WebAuthn";
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'webauthn' AND `username` = :username AND `id` = :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$data['additional'][] = $row;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$data['name'] = 'none';
|
||||||
|
$data['pretty'] = "-";
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return $data;
|
}
|
||||||
break;
|
else {
|
||||||
// u2f - deprecated, should be removed
|
|
||||||
case "u2f":
|
|
||||||
$data['name'] = "u2f";
|
|
||||||
$data['pretty'] = "Fido U2F";
|
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username");
|
|
||||||
$stmt->execute(array(
|
|
||||||
':username' => $username,
|
|
||||||
));
|
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
while($row = array_shift($rows)) {
|
|
||||||
$data['additional'][] = $row;
|
|
||||||
}
|
|
||||||
return $data;
|
|
||||||
break;
|
|
||||||
case "hotp":
|
|
||||||
$data['name'] = "hotp";
|
|
||||||
$data['pretty'] = "HMAC-based OTP";
|
|
||||||
return $data;
|
|
||||||
break;
|
|
||||||
case "totp":
|
|
||||||
$data['name'] = "totp";
|
|
||||||
$data['pretty'] = "Time-based OTP";
|
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `key_id`, `secret` FROM `tfa` WHERE `authmech` = 'totp' AND `username` = :username");
|
|
||||||
$stmt->execute(array(
|
|
||||||
':username' => $username,
|
|
||||||
));
|
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
while($row = array_shift($rows)) {
|
|
||||||
$data['additional'][] = $row;
|
|
||||||
}
|
|
||||||
return $data;
|
|
||||||
break;
|
|
||||||
case "webauthn":
|
|
||||||
$data['name'] = "webauthn";
|
|
||||||
$data['pretty'] = "WebAuthn";
|
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'webauthn' AND `username` = :username");
|
|
||||||
$stmt->execute(array(
|
|
||||||
':username' => $username,
|
|
||||||
));
|
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
while($row = array_shift($rows)) {
|
|
||||||
$data['additional'][] = $row;
|
|
||||||
}
|
|
||||||
return $data;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$data['name'] = 'none';
|
$data['name'] = 'none';
|
||||||
$data['pretty'] = "-";
|
$data['pretty'] = "-";
|
||||||
return $data;
|
return $data;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
$data['name'] = 'none';
|
|
||||||
$data['pretty'] = "-";
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
function verify_tfa_login($username, $_data, $WebAuthn) {
|
function verify_tfa_login($username, $_data) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
global $yubi;
|
global $yubi;
|
||||||
global $u2f;
|
global $u2f;
|
||||||
global $tfa;
|
global $tfa;
|
||||||
$stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
|
global $WebAuthn;
|
||||||
WHERE `username` = :username AND `active` = '1'");
|
|
||||||
$stmt->execute(array(':username' => $username));
|
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
switch ($row["authmech"]) {
|
if ($_data['tfa_method'] != 'u2f'){
|
||||||
|
|
||||||
|
switch ($_data["tfa_method"]) {
|
||||||
case "yubi_otp":
|
case "yubi_otp":
|
||||||
if (!ctype_alnum($_data['token']) || strlen($_data['token']) != 44) {
|
if (!ctype_alnum($_data['token']) || strlen($_data['token']) != 44) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
@@ -1597,7 +1650,7 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
||||||
WHERE `username` = :username
|
WHERE `username` = :username
|
||||||
AND `authmech` = 'yubi_otp'
|
AND `authmech` = 'yubi_otp'
|
||||||
AND `active`='1'
|
AND `active` = '1'
|
||||||
AND `secret` LIKE :modhex");
|
AND `secret` LIKE :modhex");
|
||||||
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
@@ -1632,15 +1685,16 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case "totp":
|
case "totp":
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
||||||
WHERE `username` = :username
|
WHERE `username` = :username
|
||||||
AND `authmech` = 'totp'
|
AND `authmech` = 'totp'
|
||||||
|
AND `id` = :id
|
||||||
AND `active`='1'");
|
AND `active`='1'");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username, ':id' => $_data['id']));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
if ($tfa->verifyCode($row['secret'], $_data['token']) === true) {
|
if ($tfa->verifyCode($row['secret'], $_data['token']) === true) {
|
||||||
$_SESSION['tfa_id'] = $row['id'];
|
$_SESSION['tfa_id'] = $row['id'];
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
@@ -1648,7 +1702,7 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
'msg' => 'verified_totp_login'
|
'msg' => 'verified_totp_login'
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
@@ -1656,23 +1710,16 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
'msg' => 'totp_verification_failed'
|
'msg' => 'totp_verification_failed'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (PDOException $e) {
|
catch (PDOException $e) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
'msg' => array('mysql_error', $e)
|
'msg' => array('mysql_error', $e)
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// u2f - deprecated, should be removed
|
|
||||||
case "u2f":
|
|
||||||
// delete old keys that used u2f
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `authmech` = :authmech AND `username` = :username");
|
|
||||||
$stmt->execute(array(':authmech' => 'u2f', ':username' => $username));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
case "webauthn":
|
case "webauthn":
|
||||||
$tokenData = json_decode($_data['token']);
|
$tokenData = json_decode($_data['token']);
|
||||||
$clientDataJSON = base64_decode($tokenData->clientDataJSON);
|
$clientDataJSON = base64_decode($tokenData->clientDataJSON);
|
||||||
@@ -1681,13 +1728,20 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
$id = base64_decode($tokenData->id);
|
$id = base64_decode($tokenData->id);
|
||||||
$challenge = $_SESSION['challenge'];
|
$challenge = $_SESSION['challenge'];
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT `key_id`, `keyHandle`, `username`, `publicKey` FROM `tfa` WHERE `keyHandle` = :tokenId");
|
$stmt = $pdo->prepare("SELECT `id`, `key_id`, `keyHandle`, `username`, `publicKey` FROM `tfa` WHERE `id` = :id AND `active`='1'");
|
||||||
$stmt->execute(array(':tokenId' => $tokenData->id));
|
$stmt->execute(array(':id' => $_data['id']));
|
||||||
$process_webauthn = $stmt->fetch(PDO::FETCH_ASSOC);
|
$process_webauthn = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if (empty($process_webauthn) || empty($process_webauthn['publicKey']) || empty($process_webauthn['username'])) return false;
|
if (empty($process_webauthn)){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
|
'msg' => array('webauthn_verification_failed', 'authenticator not found')
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ($process_webauthn['publicKey'] === false) {
|
if (empty($process_webauthn['publicKey']) || $process_webauthn['publicKey'] === false) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
@@ -1695,6 +1749,7 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);
|
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);
|
||||||
}
|
}
|
||||||
@@ -1707,26 +1762,31 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
|
$stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $process_webauthn['username']));
|
$stmt->execute(array(':username' => $process_webauthn['username']));
|
||||||
$obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
|
$obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
if ($obj_props['superadmin'] === 1) {
|
if ($obj_props['superadmin'] === 1) {
|
||||||
$_SESSION["mailcow_cc_role"] = "admin";
|
$_SESSION["mailcow_cc_role"] = "admin";
|
||||||
}
|
}
|
||||||
elseif ($obj_props['superadmin'] === 0) {
|
elseif ($obj_props['superadmin'] === 0) {
|
||||||
$_SESSION["mailcow_cc_role"] = "domainadmin";
|
$_SESSION["mailcow_cc_role"] = "domainadmin";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
|
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $process_webauthn['username']));
|
$stmt->execute(array(':username' => $process_webauthn['username']));
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
if ($row['username'] == $process_webauthn['username']) {
|
if (!empty($row['username'])) {
|
||||||
$_SESSION["mailcow_cc_role"] = "user";
|
$_SESSION["mailcow_cc_role"] = "user";
|
||||||
}
|
} else {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
|
'msg' => array('webauthn_verification_failed', 'could not determine user role')
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
|
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
@@ -1736,9 +1796,8 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
|
$_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
|
||||||
$_SESSION['tfa_id'] = $process_webauthn['key_id'];
|
$_SESSION['tfa_id'] = $process_webauthn['id'];
|
||||||
$_SESSION['authReq'] = null;
|
$_SESSION['authReq'] = null;
|
||||||
unset($_SESSION["challenge"]);
|
unset($_SESSION["challenge"]);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
@@ -1759,6 +1818,17 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
// delete old keys that used u2f
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username");
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
if (count($rows) == 0) return false;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username");
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function admin_api($access, $action, $data = null) {
|
function admin_api($access, $action, $data = null) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
|||||||
@@ -336,9 +336,37 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$mins_interval = $_data['mins_interval'];
|
$mins_interval = $_data['mins_interval'];
|
||||||
$enc1 = $_data['enc1'];
|
$enc1 = $_data['enc1'];
|
||||||
$custom_params = (empty(trim($_data['custom_params']))) ? '' : trim($_data['custom_params']);
|
$custom_params = (empty(trim($_data['custom_params']))) ? '' : trim($_data['custom_params']);
|
||||||
// Workaround, fixme
|
|
||||||
if (stripos($custom_params, 'pipemess')) {
|
// validate custom params
|
||||||
$custom_params = '';
|
foreach (explode('-', $custom_params) as $param){
|
||||||
|
if(empty($param)) continue;
|
||||||
|
|
||||||
|
// extract option
|
||||||
|
if (str_contains($param, '=')) $param = explode('=', $param)[0];
|
||||||
|
else $param = rtrim($param, ' ');
|
||||||
|
// remove first char if first char is -
|
||||||
|
if ($param[0] == '-') $param = ltrim($param, $param[0]);
|
||||||
|
|
||||||
|
if (str_contains($param, ' ')) {
|
||||||
|
// bad char
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => 'bad character SPACE'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if param is whitelisted
|
||||||
|
if (!in_array(strtolower($param), $GLOBALS["IMAPSYNC_OPTIONS"]["whitelist"])){
|
||||||
|
// bad option
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => 'bad option '. $param
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (empty($subfolder2)) {
|
if (empty($subfolder2)) {
|
||||||
$subfolder2 = "";
|
$subfolder2 = "";
|
||||||
@@ -599,7 +627,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
ratelimit('edit', 'domain', array('rl_value' => $_data['rl_value'], 'rl_frame' => $_data['rl_frame'], 'object' => $domain));
|
ratelimit('edit', 'domain', array('rl_value' => $_data['rl_value'], 'rl_frame' => $_data['rl_frame'], 'object' => $domain));
|
||||||
}
|
}
|
||||||
if (!empty($_data['key_size']) && !empty($_data['dkim_selector'])) {
|
if (!empty($_data['key_size']) && !empty($_data['dkim_selector'])) {
|
||||||
dkim('add', array('key_size' => $_data['key_size'], 'dkim_selector' => $_data['dkim_selector'], 'domains' => $domain));
|
if (!empty($redis->hGet('DKIM_SELECTORS', $domain))) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => 'domain_add_dkim_available'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dkim('add', array('key_size' => $_data['key_size'], 'dkim_selector' => $_data['dkim_selector'], 'domains' => $domain));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!empty($restart_sogo)) {
|
if (!empty($restart_sogo)) {
|
||||||
$restart_response = json_decode(docker('post', 'sogo-mailcow', 'restart'), true);
|
$restart_response = json_decode(docker('post', 'sogo-mailcow', 'restart'), true);
|
||||||
@@ -929,7 +966,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
ratelimit('edit', 'domain', array('rl_value' => $_data['rl_value'], 'rl_frame' => $_data['rl_frame'], 'object' => $alias_domain));
|
ratelimit('edit', 'domain', array('rl_value' => $_data['rl_value'], 'rl_frame' => $_data['rl_frame'], 'object' => $alias_domain));
|
||||||
}
|
}
|
||||||
if (!empty($_data['key_size']) && !empty($_data['dkim_selector'])) {
|
if (!empty($_data['key_size']) && !empty($_data['dkim_selector'])) {
|
||||||
dkim('add', array('key_size' => $_data['key_size'], 'dkim_selector' => $_data['dkim_selector'], 'domains' => $alias_domain));
|
if (!empty($redis->hGet('DKIM_SELECTORS', $alias_domain))) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => 'domain_add_dkim_available'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dkim('add', array('key_size' => $_data['key_size'], 'dkim_selector' => $_data['dkim_selector'], 'domains' => $alias_domain));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
@@ -1746,8 +1792,37 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (stripos($custom_params, 'pipemess')) {
|
|
||||||
$custom_params = '';
|
// validate custom params
|
||||||
|
foreach (explode('-', $custom_params) as $param){
|
||||||
|
if(empty($param)) continue;
|
||||||
|
|
||||||
|
// extract option
|
||||||
|
if (str_contains($param, '=')) $param = explode('=', $param)[0];
|
||||||
|
else $param = rtrim($param, ' ');
|
||||||
|
// remove first char if first char is -
|
||||||
|
if ($param[0] == '-') $param = ltrim($param, $param[0]);
|
||||||
|
|
||||||
|
if (str_contains($param, ' ')) {
|
||||||
|
// bad char
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => 'bad character SPACE'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if param is whitelisted
|
||||||
|
if (!in_array(strtolower($param), $GLOBALS["IMAPSYNC_OPTIONS"]["whitelist"])){
|
||||||
|
// bad option
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => 'bad option '. $param
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (empty($subfolder2)) {
|
if (empty($subfolder2)) {
|
||||||
$subfolder2 = "";
|
$subfolder2 = "";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ function init_db_schema() {
|
|||||||
try {
|
try {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
|
||||||
$db_version = "19052022_1541";
|
$db_version = "25072022_2300";
|
||||||
|
|
||||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
@@ -440,7 +440,7 @@ function init_db_schema() {
|
|||||||
"spam_score" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"spam_score" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
"spam_policy" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"spam_policy" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
"delimiter_action" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"delimiter_action" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
"syncjobs" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"syncjobs" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||||
"eas_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"eas_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
"sogo_profile_reset" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
"sogo_profile_reset" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||||
"pushover" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"pushover" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
@@ -738,8 +738,8 @@ function init_db_schema() {
|
|||||||
"username" => "VARCHAR(255) NOT NULL",
|
"username" => "VARCHAR(255) NOT NULL",
|
||||||
"authmech" => "ENUM('yubi_otp', 'u2f', 'hotp', 'totp', 'webauthn')",
|
"authmech" => "ENUM('yubi_otp', 'u2f', 'hotp', 'totp', 'webauthn')",
|
||||||
"secret" => "VARCHAR(255) DEFAULT NULL",
|
"secret" => "VARCHAR(255) DEFAULT NULL",
|
||||||
"keyHandle" => "VARCHAR(255) DEFAULT NULL",
|
"keyHandle" => "VARCHAR(1023) DEFAULT NULL",
|
||||||
"publicKey" => "VARCHAR(255) DEFAULT NULL",
|
"publicKey" => "VARCHAR(4096) DEFAULT NULL",
|
||||||
"counter" => "INT NOT NULL DEFAULT '0'",
|
"counter" => "INT NOT NULL DEFAULT '0'",
|
||||||
"certificate" => "TEXT",
|
"certificate" => "TEXT",
|
||||||
"active" => "TINYINT(1) NOT NULL DEFAULT '0'"
|
"active" => "TINYINT(1) NOT NULL DEFAULT '0'"
|
||||||
@@ -1227,8 +1227,16 @@ function init_db_schema() {
|
|||||||
$pdo->query($create);
|
$pdo->query($create);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mitigate imapsync pipemess issue
|
// Mitigate imapsync argument injection issue
|
||||||
$pdo->query("UPDATE `imapsync` SET `custom_params` = '' WHERE `custom_params` LIKE '%pipemess%';");
|
$pdo->query("UPDATE `imapsync` SET `custom_params` = ''
|
||||||
|
WHERE `custom_params` LIKE '%pipemess%'
|
||||||
|
OR custom_params LIKE '%skipmess%'
|
||||||
|
OR custom_params LIKE '%delete2foldersonly%'
|
||||||
|
OR custom_params LIKE '%delete2foldersbutnot%'
|
||||||
|
OR custom_params LIKE '%regexflag%'
|
||||||
|
OR custom_params LIKE '%pipemess%'
|
||||||
|
OR custom_params LIKE '%regextrans2%'
|
||||||
|
OR custom_params LIKE '%maxlinelengthcmd%';");
|
||||||
|
|
||||||
// Migrate webauthn tfa
|
// Migrate webauthn tfa
|
||||||
$stmt = $pdo->query("ALTER TABLE `tfa` MODIFY COLUMN `authmech` ENUM('yubi_otp', 'u2f', 'hotp', 'totp', 'webauthn')");
|
$stmt = $pdo->query("ALTER TABLE `tfa` MODIFY COLUMN `authmech` ENUM('yubi_otp', 'u2f', 'hotp', 'totp', 'webauthn')");
|
||||||
|
|||||||
Generated
+8
-8
@@ -1604,16 +1604,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/twig",
|
"name": "twig/twig",
|
||||||
"version": "v3.3.8",
|
"version": "v3.4.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/twigphp/Twig.git",
|
"url": "https://github.com/twigphp/Twig.git",
|
||||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
|
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
|
"url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
|
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1628,7 +1628,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.3-dev"
|
"dev-master": "3.4-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -1664,7 +1664,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/twigphp/Twig/issues",
|
"issues": "https://github.com/twigphp/Twig/issues",
|
||||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
|
"source": "https://github.com/twigphp/Twig/tree/v3.4.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1676,7 +1676,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-02-04T06:59:48+00:00"
|
"time": "2022-09-28T08:42:51+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "yubico/u2flib-server",
|
"name": "yubico/u2flib-server",
|
||||||
@@ -1728,5 +1728,5 @@
|
|||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": [],
|
"platform": [],
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"plugin-api-version": "2.2.0"
|
"plugin-api-version": "2.3.0"
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+5
@@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
// autoload.php @generated by Composer
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
require_once __DIR__ . '/composer/autoload_real.php';
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
return ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b::getLoader();
|
return ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b::getLoader();
|
||||||
|
|||||||
+9
-7
@@ -21,12 +21,14 @@ use Composer\Semver\VersionParser;
|
|||||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||||
*
|
*
|
||||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||||
|
*
|
||||||
|
* @final
|
||||||
*/
|
*/
|
||||||
class InstalledVersions
|
class InstalledVersions
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var mixed[]|null
|
* @var mixed[]|null
|
||||||
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
*/
|
*/
|
||||||
private static $installed;
|
private static $installed;
|
||||||
|
|
||||||
@@ -37,7 +39,7 @@ class InstalledVersions
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array[]
|
* @var array[]
|
||||||
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
*/
|
*/
|
||||||
private static $installedByVendor = array();
|
private static $installedByVendor = array();
|
||||||
|
|
||||||
@@ -241,7 +243,7 @@ class InstalledVersions
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
|
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||||
*/
|
*/
|
||||||
public static function getRootPackage()
|
public static function getRootPackage()
|
||||||
{
|
{
|
||||||
@@ -255,7 +257,7 @@ class InstalledVersions
|
|||||||
*
|
*
|
||||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||||
* @return array[]
|
* @return array[]
|
||||||
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
|
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||||
*/
|
*/
|
||||||
public static function getRawData()
|
public static function getRawData()
|
||||||
{
|
{
|
||||||
@@ -278,7 +280,7 @@ class InstalledVersions
|
|||||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||||
*
|
*
|
||||||
* @return array[]
|
* @return array[]
|
||||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
*/
|
*/
|
||||||
public static function getAllRawData()
|
public static function getAllRawData()
|
||||||
{
|
{
|
||||||
@@ -301,7 +303,7 @@ class InstalledVersions
|
|||||||
* @param array[] $data A vendor/composer/installed.php data set
|
* @param array[] $data A vendor/composer/installed.php data set
|
||||||
* @return void
|
* @return void
|
||||||
*
|
*
|
||||||
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
|
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||||
*/
|
*/
|
||||||
public static function reload($data)
|
public static function reload($data)
|
||||||
{
|
{
|
||||||
@@ -311,7 +313,7 @@ class InstalledVersions
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array[]
|
||||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
*/
|
*/
|
||||||
private static function getInstalled()
|
private static function getInstalled()
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// autoload_classmap.php @generated by Composer
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
$vendorDir = dirname(__DIR__);
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
+2
-2
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
// autoload_files.php @generated by Composer
|
// autoload_files.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
$vendorDir = dirname(__DIR__);
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
|
||||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||||
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
|
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
|
||||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||||
|
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||||
'fe62ba7e10580d903cc46d808b5961a4' => $vendorDir . '/tightenco/collect/src/Collect/Support/helpers.php',
|
'fe62ba7e10580d903cc46d808b5961a4' => $vendorDir . '/tightenco/collect/src/Collect/Support/helpers.php',
|
||||||
'caf31cc6ec7cf2241cb6f12c226c3846' => $vendorDir . '/tightenco/collect/src/Collect/Support/alias.php',
|
'caf31cc6ec7cf2241cb6f12c226c3846' => $vendorDir . '/tightenco/collect/src/Collect/Support/alias.php',
|
||||||
'04c6c5c2f7095ccf6c481d3e53e1776f' => $vendorDir . '/mustangostang/spyc/Spyc.php',
|
'04c6c5c2f7095ccf6c481d3e53e1776f' => $vendorDir . '/mustangostang/spyc/Spyc.php',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// autoload_namespaces.php @generated by Composer
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
$vendorDir = dirname(__DIR__);
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// autoload_psr4.php @generated by Composer
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
$vendorDir = dirname(__DIR__);
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
+4
-27
@@ -25,38 +25,15 @@ class ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b
|
|||||||
require __DIR__ . '/platform_check.php';
|
require __DIR__ . '/platform_check.php';
|
||||||
|
|
||||||
spl_autoload_register(array('ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b', 'loadClassLoader'), true, true);
|
spl_autoload_register(array('ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b', 'loadClassLoader'), true, true);
|
||||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||||
spl_autoload_unregister(array('ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b', 'loadClassLoader'));
|
spl_autoload_unregister(array('ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b', 'loadClassLoader'));
|
||||||
|
|
||||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
require __DIR__ . '/autoload_static.php';
|
||||||
if ($useStaticLoader) {
|
call_user_func(\Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::getInitializer($loader));
|
||||||
require __DIR__ . '/autoload_static.php';
|
|
||||||
|
|
||||||
call_user_func(\Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::getInitializer($loader));
|
|
||||||
} else {
|
|
||||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
|
||||||
foreach ($map as $namespace => $path) {
|
|
||||||
$loader->set($namespace, $path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$map = require __DIR__ . '/autoload_psr4.php';
|
|
||||||
foreach ($map as $namespace => $path) {
|
|
||||||
$loader->setPsr4($namespace, $path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
|
||||||
if ($classMap) {
|
|
||||||
$loader->addClassMap($classMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$loader->register(true);
|
$loader->register(true);
|
||||||
|
|
||||||
if ($useStaticLoader) {
|
$includeFiles = \Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::$files;
|
||||||
$includeFiles = Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::$files;
|
|
||||||
} else {
|
|
||||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
|
||||||
}
|
|
||||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||||
composerRequire873464e4bd965a3168f133248b1b218b($fileIdentifier, $file);
|
composerRequire873464e4bd965a3168f133248b1b218b($fileIdentifier, $file);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -8,10 +8,10 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
|
|||||||
{
|
{
|
||||||
public static $files = array (
|
public static $files = array (
|
||||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
|
||||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||||
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
|
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
|
||||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||||
|
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||||
'fe62ba7e10580d903cc46d808b5961a4' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/helpers.php',
|
'fe62ba7e10580d903cc46d808b5961a4' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/helpers.php',
|
||||||
'caf31cc6ec7cf2241cb6f12c226c3846' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/alias.php',
|
'caf31cc6ec7cf2241cb6f12c226c3846' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/alias.php',
|
||||||
'04c6c5c2f7095ccf6c481d3e53e1776f' => __DIR__ . '/..' . '/mustangostang/spyc/Spyc.php',
|
'04c6c5c2f7095ccf6c481d3e53e1776f' => __DIR__ . '/..' . '/mustangostang/spyc/Spyc.php',
|
||||||
|
|||||||
+8
-8
@@ -1654,17 +1654,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/twig",
|
"name": "twig/twig",
|
||||||
"version": "v3.3.8",
|
"version": "v3.4.3",
|
||||||
"version_normalized": "3.3.8.0",
|
"version_normalized": "3.4.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/twigphp/Twig.git",
|
"url": "https://github.com/twigphp/Twig.git",
|
||||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
|
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
|
"url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
|
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1676,11 +1676,11 @@
|
|||||||
"psr/container": "^1.0",
|
"psr/container": "^1.0",
|
||||||
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
|
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
|
||||||
},
|
},
|
||||||
"time": "2022-02-04T06:59:48+00:00",
|
"time": "2022-09-28T08:42:51+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.3-dev"
|
"dev-master": "3.4-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"installation-source": "dist",
|
"installation-source": "dist",
|
||||||
@@ -1717,7 +1717,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/twigphp/Twig/issues",
|
"issues": "https://github.com/twigphp/Twig/issues",
|
||||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
|
"source": "https://github.com/twigphp/Twig/tree/v3.4.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|||||||
+34
-34
@@ -1,49 +1,49 @@
|
|||||||
<?php return array(
|
<?php return array(
|
||||||
'root' => array(
|
'root' => array(
|
||||||
'pretty_version' => '1.0.0+no-version-set',
|
'name' => '__root__',
|
||||||
'version' => '1.0.0.0',
|
'pretty_version' => 'dev-master',
|
||||||
|
'version' => 'dev-master',
|
||||||
|
'reference' => '8e0b1d8aee4af02311692cb031695cc2ac3850fd',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => NULL,
|
|
||||||
'name' => '__root__',
|
|
||||||
'dev' => true,
|
'dev' => true,
|
||||||
),
|
),
|
||||||
'versions' => array(
|
'versions' => array(
|
||||||
'__root__' => array(
|
'__root__' => array(
|
||||||
'pretty_version' => '1.0.0+no-version-set',
|
'pretty_version' => 'dev-master',
|
||||||
'version' => '1.0.0.0',
|
'version' => 'dev-master',
|
||||||
|
'reference' => '8e0b1d8aee4af02311692cb031695cc2ac3850fd',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => NULL,
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'bshaffer/oauth2-server-php' => array(
|
'bshaffer/oauth2-server-php' => array(
|
||||||
'pretty_version' => 'v1.11.1',
|
'pretty_version' => 'v1.11.1',
|
||||||
'version' => '1.11.1.0',
|
'version' => '1.11.1.0',
|
||||||
|
'reference' => '5a0c8000d4763b276919e2106f54eddda6bc50fa',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../bshaffer/oauth2-server-php',
|
'install_path' => __DIR__ . '/../bshaffer/oauth2-server-php',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '5a0c8000d4763b276919e2106f54eddda6bc50fa',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'ddeboer/imap' => array(
|
'ddeboer/imap' => array(
|
||||||
'pretty_version' => '1.13.1',
|
'pretty_version' => '1.13.1',
|
||||||
'version' => '1.13.1.0',
|
'version' => '1.13.1.0',
|
||||||
|
'reference' => '8b772d04b1deadb5df13782fb78c4b648f77496e',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../ddeboer/imap',
|
'install_path' => __DIR__ . '/../ddeboer/imap',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '8b772d04b1deadb5df13782fb78c4b648f77496e',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'directorytree/ldaprecord' => array(
|
'directorytree/ldaprecord' => array(
|
||||||
'pretty_version' => 'v2.10.1',
|
'pretty_version' => 'v2.10.1',
|
||||||
'version' => '2.10.1.0',
|
'version' => '2.10.1.0',
|
||||||
|
'reference' => 'bf512d9af7a7b0e2ed7a666ab29cefdd027bee88',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../directorytree/ldaprecord',
|
'install_path' => __DIR__ . '/../directorytree/ldaprecord',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'bf512d9af7a7b0e2ed7a666ab29cefdd027bee88',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'exorus/php-mime-mail-parser' => array(
|
'exorus/php-mime-mail-parser' => array(
|
||||||
@@ -55,28 +55,28 @@
|
|||||||
'illuminate/contracts' => array(
|
'illuminate/contracts' => array(
|
||||||
'pretty_version' => 'v9.3.0',
|
'pretty_version' => 'v9.3.0',
|
||||||
'version' => '9.3.0.0',
|
'version' => '9.3.0.0',
|
||||||
|
'reference' => 'bf4b3c254c49d28157645d01e4883b5951b1e1d0',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../illuminate/contracts',
|
'install_path' => __DIR__ . '/../illuminate/contracts',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'bf4b3c254c49d28157645d01e4883b5951b1e1d0',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'matthiasmullie/minify' => array(
|
'matthiasmullie/minify' => array(
|
||||||
'pretty_version' => '1.3.66',
|
'pretty_version' => '1.3.66',
|
||||||
'version' => '1.3.66.0',
|
'version' => '1.3.66.0',
|
||||||
|
'reference' => '45fd3b0f1dfa2c965857c6d4a470bea52adc31a6',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../matthiasmullie/minify',
|
'install_path' => __DIR__ . '/../matthiasmullie/minify',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '45fd3b0f1dfa2c965857c6d4a470bea52adc31a6',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'matthiasmullie/path-converter' => array(
|
'matthiasmullie/path-converter' => array(
|
||||||
'pretty_version' => '1.1.3',
|
'pretty_version' => '1.1.3',
|
||||||
'version' => '1.1.3.0',
|
'version' => '1.1.3.0',
|
||||||
|
'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../matthiasmullie/path-converter',
|
'install_path' => __DIR__ . '/../matthiasmullie/path-converter',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'messaged/php-mime-mail-parser' => array(
|
'messaged/php-mime-mail-parser' => array(
|
||||||
@@ -88,136 +88,136 @@
|
|||||||
'mustangostang/spyc' => array(
|
'mustangostang/spyc' => array(
|
||||||
'pretty_version' => '0.6.3',
|
'pretty_version' => '0.6.3',
|
||||||
'version' => '0.6.3.0',
|
'version' => '0.6.3.0',
|
||||||
|
'reference' => '4627c838b16550b666d15aeae1e5289dd5b77da0',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../mustangostang/spyc',
|
'install_path' => __DIR__ . '/../mustangostang/spyc',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '4627c838b16550b666d15aeae1e5289dd5b77da0',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'nesbot/carbon' => array(
|
'nesbot/carbon' => array(
|
||||||
'pretty_version' => '2.57.0',
|
'pretty_version' => '2.57.0',
|
||||||
'version' => '2.57.0.0',
|
'version' => '2.57.0.0',
|
||||||
|
'reference' => '4a54375c21eea4811dbd1149fe6b246517554e78',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../nesbot/carbon',
|
'install_path' => __DIR__ . '/../nesbot/carbon',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '4a54375c21eea4811dbd1149fe6b246517554e78',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'paragonie/random_compat' => array(
|
'paragonie/random_compat' => array(
|
||||||
'pretty_version' => 'v9.99.100',
|
'pretty_version' => 'v9.99.100',
|
||||||
'version' => '9.99.100.0',
|
'version' => '9.99.100.0',
|
||||||
|
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../paragonie/random_compat',
|
'install_path' => __DIR__ . '/../paragonie/random_compat',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'php-mime-mail-parser/php-mime-mail-parser' => array(
|
'php-mime-mail-parser/php-mime-mail-parser' => array(
|
||||||
'pretty_version' => '7.0.0',
|
'pretty_version' => '7.0.0',
|
||||||
'version' => '7.0.0.0',
|
'version' => '7.0.0.0',
|
||||||
|
'reference' => '9d09a017f3f103fec8456211a4a538b80e0eca0d',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../php-mime-mail-parser/php-mime-mail-parser',
|
'install_path' => __DIR__ . '/../php-mime-mail-parser/php-mime-mail-parser',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '9d09a017f3f103fec8456211a4a538b80e0eca0d',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'phpmailer/phpmailer' => array(
|
'phpmailer/phpmailer' => array(
|
||||||
'pretty_version' => 'v6.6.0',
|
'pretty_version' => 'v6.6.0',
|
||||||
'version' => '6.6.0.0',
|
'version' => '6.6.0.0',
|
||||||
|
'reference' => 'e43bac82edc26ca04b36143a48bde1c051cfd5b1',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../phpmailer/phpmailer',
|
'install_path' => __DIR__ . '/../phpmailer/phpmailer',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'e43bac82edc26ca04b36143a48bde1c051cfd5b1',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'psr/container' => array(
|
'psr/container' => array(
|
||||||
'pretty_version' => '2.0.2',
|
'pretty_version' => '2.0.2',
|
||||||
'version' => '2.0.2.0',
|
'version' => '2.0.2.0',
|
||||||
|
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../psr/container',
|
'install_path' => __DIR__ . '/../psr/container',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'psr/log' => array(
|
'psr/log' => array(
|
||||||
'pretty_version' => '3.0.0',
|
'pretty_version' => '3.0.0',
|
||||||
'version' => '3.0.0.0',
|
'version' => '3.0.0.0',
|
||||||
|
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../psr/log',
|
'install_path' => __DIR__ . '/../psr/log',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'psr/simple-cache' => array(
|
'psr/simple-cache' => array(
|
||||||
'pretty_version' => '2.0.0',
|
'pretty_version' => '2.0.0',
|
||||||
'version' => '2.0.0.0',
|
'version' => '2.0.0.0',
|
||||||
|
'reference' => '8707bf3cea6f710bf6ef05491234e3ab06f6432a',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../psr/simple-cache',
|
'install_path' => __DIR__ . '/../psr/simple-cache',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '8707bf3cea6f710bf6ef05491234e3ab06f6432a',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'robthree/twofactorauth' => array(
|
'robthree/twofactorauth' => array(
|
||||||
'pretty_version' => '1.8.1',
|
'pretty_version' => '1.8.1',
|
||||||
'version' => '1.8.1.0',
|
'version' => '1.8.1.0',
|
||||||
|
'reference' => '5afcb45282f1c75562a48d479ecd1732c9bdb11b',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../robthree/twofactorauth',
|
'install_path' => __DIR__ . '/../robthree/twofactorauth',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '5afcb45282f1c75562a48d479ecd1732c9bdb11b',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'soundasleep/html2text' => array(
|
'soundasleep/html2text' => array(
|
||||||
'pretty_version' => '0.5.0',
|
'pretty_version' => '0.5.0',
|
||||||
'version' => '0.5.0.0',
|
'version' => '0.5.0.0',
|
||||||
|
'reference' => 'cdb89f6ffa2c4cc78f8ed9ea6ee0594a9133ccad',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../soundasleep/html2text',
|
'install_path' => __DIR__ . '/../soundasleep/html2text',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'cdb89f6ffa2c4cc78f8ed9ea6ee0594a9133ccad',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-ctype' => array(
|
'symfony/polyfill-ctype' => array(
|
||||||
'pretty_version' => 'v1.24.0',
|
'pretty_version' => 'v1.24.0',
|
||||||
'version' => '1.24.0.0',
|
'version' => '1.24.0.0',
|
||||||
|
'reference' => '30885182c981ab175d4d034db0f6f469898070ab',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '30885182c981ab175d4d034db0f6f469898070ab',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-mbstring' => array(
|
'symfony/polyfill-mbstring' => array(
|
||||||
'pretty_version' => 'v1.24.0',
|
'pretty_version' => 'v1.24.0',
|
||||||
'version' => '1.24.0.0',
|
'version' => '1.24.0.0',
|
||||||
|
'reference' => '0abb51d2f102e00a4eefcf46ba7fec406d245825',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '0abb51d2f102e00a4eefcf46ba7fec406d245825',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-php80' => array(
|
'symfony/polyfill-php80' => array(
|
||||||
'pretty_version' => 'v1.24.0',
|
'pretty_version' => 'v1.24.0',
|
||||||
'version' => '1.24.0.0',
|
'version' => '1.24.0.0',
|
||||||
|
'reference' => '57b712b08eddb97c762a8caa32c84e037892d2e9',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '57b712b08eddb97c762a8caa32c84e037892d2e9',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/translation' => array(
|
'symfony/translation' => array(
|
||||||
'pretty_version' => 'v6.0.5',
|
'pretty_version' => 'v6.0.5',
|
||||||
'version' => '6.0.5.0',
|
'version' => '6.0.5.0',
|
||||||
|
'reference' => 'e69501c71107cc3146b32aaa45f4edd0c3427875',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/translation',
|
'install_path' => __DIR__ . '/../symfony/translation',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'e69501c71107cc3146b32aaa45f4edd0c3427875',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/translation-contracts' => array(
|
'symfony/translation-contracts' => array(
|
||||||
'pretty_version' => 'v3.0.0',
|
'pretty_version' => 'v3.0.0',
|
||||||
'version' => '3.0.0.0',
|
'version' => '3.0.0.0',
|
||||||
|
'reference' => '1b6ea5a7442af5a12dba3dbd6d71034b5b234e77',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/translation-contracts',
|
'install_path' => __DIR__ . '/../symfony/translation-contracts',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '1b6ea5a7442af5a12dba3dbd6d71034b5b234e77',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/translation-implementation' => array(
|
'symfony/translation-implementation' => array(
|
||||||
@@ -229,37 +229,37 @@
|
|||||||
'symfony/var-dumper' => array(
|
'symfony/var-dumper' => array(
|
||||||
'pretty_version' => 'v6.0.5',
|
'pretty_version' => 'v6.0.5',
|
||||||
'version' => '6.0.5.0',
|
'version' => '6.0.5.0',
|
||||||
|
'reference' => '60d6a756d5f485df5e6e40b337334848f79f61ce',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/var-dumper',
|
'install_path' => __DIR__ . '/../symfony/var-dumper',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '60d6a756d5f485df5e6e40b337334848f79f61ce',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'tightenco/collect' => array(
|
'tightenco/collect' => array(
|
||||||
'pretty_version' => 'v8.83.2',
|
'pretty_version' => 'v8.83.2',
|
||||||
'version' => '8.83.2.0',
|
'version' => '8.83.2.0',
|
||||||
|
'reference' => 'd9c66d586ec2d216d8a31283d73f8df1400cc722',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../tightenco/collect',
|
'install_path' => __DIR__ . '/../tightenco/collect',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'd9c66d586ec2d216d8a31283d73f8df1400cc722',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'twig/twig' => array(
|
'twig/twig' => array(
|
||||||
'pretty_version' => 'v3.3.8',
|
'pretty_version' => 'v3.4.3',
|
||||||
'version' => '3.3.8.0',
|
'version' => '3.4.3.0',
|
||||||
|
'reference' => 'c38fd6b0b7f370c198db91ffd02e23b517426b58',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../twig/twig',
|
'install_path' => __DIR__ . '/../twig/twig',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '972d8604a92b7054828b539f2febb0211dd5945c',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'yubico/u2flib-server' => array(
|
'yubico/u2flib-server' => array(
|
||||||
'pretty_version' => '1.0.2',
|
'pretty_version' => '1.0.2',
|
||||||
'version' => '1.0.2.0',
|
'version' => '1.0.2.0',
|
||||||
|
'reference' => '55d813acf68212ad2cadecde07551600d6971939',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../yubico/u2flib-server',
|
'install_path' => __DIR__ . '/../yubico/u2flib-server',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '55d813acf68212ad2cadecde07551600d6971939',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/doc/** export-ignore
|
/doc/ export-ignore
|
||||||
/extra/** export-ignore
|
/extra/ export-ignore
|
||||||
/tests export-ignore
|
/tests/ export-ignore
|
||||||
/phpunit.xml.dist export-ignore
|
/phpunit.xml.dist export-ignore
|
||||||
|
|||||||
+11
-37
@@ -9,6 +9,9 @@ on:
|
|||||||
env:
|
env:
|
||||||
SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE: 1
|
SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE: 1
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
name: "PHP ${{ matrix.php-version }}"
|
name: "PHP ${{ matrix.php-version }}"
|
||||||
@@ -25,36 +28,23 @@ jobs:
|
|||||||
- '7.4'
|
- '7.4'
|
||||||
- '8.0'
|
- '8.0'
|
||||||
- '8.1'
|
- '8.1'
|
||||||
composer-options: ['']
|
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Install PHP with extensions"
|
- name: "Install PHP with extensions"
|
||||||
uses: shivammathur/setup-php@2.7.0
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
coverage: "none"
|
coverage: "none"
|
||||||
php-version: ${{ matrix.php-version }}
|
php-version: ${{ matrix.php-version }}
|
||||||
ini-values: memory_limit=-1
|
ini-values: memory_limit=-1
|
||||||
tools: composer:v2
|
|
||||||
|
|
||||||
- name: "Add PHPUnit matcher"
|
- name: "Add PHPUnit matcher"
|
||||||
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||||
|
|
||||||
- name: "Set composer cache directory"
|
- run: composer install
|
||||||
id: composer-cache
|
|
||||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
|
||||||
|
|
||||||
- name: "Cache composer"
|
|
||||||
uses: actions/cache@v2.1.2
|
|
||||||
with:
|
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('composer.json') }}
|
|
||||||
restore-keys: ${{ runner.os }}-${{ matrix.php-version }}-composer-
|
|
||||||
|
|
||||||
- run: composer install ${{ matrix.composer-options }}
|
|
||||||
|
|
||||||
- name: "Install PHPUnit"
|
- name: "Install PHPUnit"
|
||||||
run: vendor/bin/simple-phpunit install
|
run: vendor/bin/simple-phpunit install
|
||||||
@@ -92,35 +82,22 @@ jobs:
|
|||||||
- 'extra/markdown-extra'
|
- 'extra/markdown-extra'
|
||||||
- 'extra/string-extra'
|
- 'extra/string-extra'
|
||||||
- 'extra/twig-extra-bundle'
|
- 'extra/twig-extra-bundle'
|
||||||
composer-options: ['']
|
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Install PHP with extensions"
|
- name: "Install PHP with extensions"
|
||||||
uses: shivammathur/setup-php@2.7.0
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
coverage: "none"
|
coverage: "none"
|
||||||
php-version: ${{ matrix.php-version }}
|
php-version: ${{ matrix.php-version }}
|
||||||
ini-values: memory_limit=-1
|
ini-values: memory_limit=-1
|
||||||
tools: composer:v2
|
|
||||||
|
|
||||||
- name: "Add PHPUnit matcher"
|
- name: "Add PHPUnit matcher"
|
||||||
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||||
|
|
||||||
- name: "Set composer cache directory"
|
|
||||||
id: composer-cache
|
|
||||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
|
||||||
|
|
||||||
- name: "Cache composer"
|
|
||||||
uses: actions/cache@v2.1.2
|
|
||||||
with:
|
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-${{ matrix.php-version }}-${{ matrix.extension }}-${{ hashFiles('composer.json') }}
|
|
||||||
restore-keys: ${{ runner.os }}-${{ matrix.php-version }}-${{ matrix.extension }}-
|
|
||||||
|
|
||||||
- run: composer install
|
- run: composer install
|
||||||
|
|
||||||
- name: "Install PHPUnit"
|
- name: "Install PHPUnit"
|
||||||
@@ -129,10 +106,6 @@ jobs:
|
|||||||
- name: "PHPUnit version"
|
- name: "PHPUnit version"
|
||||||
run: vendor/bin/simple-phpunit --version
|
run: vendor/bin/simple-phpunit --version
|
||||||
|
|
||||||
- if: matrix.extension == 'extra/markdown-extra' && matrix.php-version == '8.0'
|
|
||||||
working-directory: ${{ matrix.extension}}
|
|
||||||
run: composer config platform.php 7.4.99
|
|
||||||
|
|
||||||
- name: "Composer install"
|
- name: "Composer install"
|
||||||
working-directory: ${{ matrix.extension}}
|
working-directory: ${{ matrix.extension}}
|
||||||
run: composer install
|
run: composer install
|
||||||
@@ -140,6 +113,7 @@ jobs:
|
|||||||
- name: "Run tests"
|
- name: "Run tests"
|
||||||
working-directory: ${{ matrix.extension}}
|
working-directory: ${{ matrix.extension}}
|
||||||
run: ../../vendor/bin/simple-phpunit
|
run: ../../vendor/bin/simple-phpunit
|
||||||
|
|
||||||
#
|
#
|
||||||
# Drupal does not support Twig 3 now!
|
# Drupal does not support Twig 3 now!
|
||||||
#
|
#
|
||||||
@@ -160,10 +134,10 @@ jobs:
|
|||||||
#
|
#
|
||||||
# steps:
|
# steps:
|
||||||
# - name: "Checkout code"
|
# - name: "Checkout code"
|
||||||
# uses: actions/checkout@v2.3.3
|
# uses: actions/checkout@v2
|
||||||
#
|
#
|
||||||
# - name: "Install PHP with extensions"
|
# - name: "Install PHP with extensions"
|
||||||
# uses: shivammathur/setup-php@2.7.0
|
# uses: shivammathur/setup-php@2
|
||||||
# with:
|
# with:
|
||||||
# coverage: "none"
|
# coverage: "none"
|
||||||
# extensions: "gd, pdo_sqlite"
|
# extensions: "gd, pdo_sqlite"
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
|
- '2.x'
|
||||||
- '3.x'
|
- '3.x'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: "Build"
|
name: "Build"
|
||||||
@@ -16,32 +20,32 @@ jobs:
|
|||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Set up Python 3.7"
|
- name: "Set-up PHP"
|
||||||
uses: actions/setup-python@v1
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
python-version: '3.7' # Semantic version range syntax or exact version of a Python version
|
php-version: 8.1
|
||||||
|
coverage: none
|
||||||
|
tools: "composer:v2"
|
||||||
|
|
||||||
- name: "Display Python version"
|
- name: Get composer cache directory
|
||||||
run: python -c "import sys; print(sys.version)"
|
id: composercache
|
||||||
|
working-directory: doc/_build
|
||||||
|
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||||
|
|
||||||
- name: "Install Sphinx dependencies"
|
- name: Cache dependencies
|
||||||
run: sudo apt-get install python-dev build-essential
|
|
||||||
|
|
||||||
- name: "Cache pip"
|
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ${{ steps.composercache.outputs.dir }}
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('_build/.requirements.txt') }}
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||||
restore-keys: |
|
restore-keys: ${{ runner.os }}-composer-
|
||||||
${{ runner.os }}-pip-
|
|
||||||
|
|
||||||
- name: "Install Sphinx + requirements via pip"
|
- name: "Install dependencies"
|
||||||
working-directory: "doc"
|
working-directory: doc/_build
|
||||||
run: pip install -r _build/.requirements.txt
|
run: composer install --prefer-dist --no-progress
|
||||||
|
|
||||||
- name: "Build documentation"
|
- name: "Build the docs"
|
||||||
working-directory: "doc"
|
working-directory: doc/_build
|
||||||
run: make -C _build SPHINXOPTS="-nqW -j auto" html
|
run: php build.php --disable-cache
|
||||||
|
|
||||||
doctor-rst:
|
doctor-rst:
|
||||||
name: "DOCtor-RST"
|
name: "DOCtor-RST"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
/doc/_build/vendor
|
||||||
|
/doc/_build/output
|
||||||
/composer.lock
|
/composer.lock
|
||||||
/phpunit.xml
|
/phpunit.xml
|
||||||
/vendor
|
/vendor
|
||||||
|
|||||||
+30
@@ -1,6 +1,36 @@
|
|||||||
|
# 3.4.3 (2022-09-28)
|
||||||
|
|
||||||
|
* Fix a security issue on filesystem loader (possibility to load a template outside a configured directory)
|
||||||
|
|
||||||
|
# 3.4.2 (2022-08-12)
|
||||||
|
|
||||||
|
* Allow inherited magic method to still run with calling class
|
||||||
|
* Fix CallExpression::reflectCallable() throwing TypeError
|
||||||
|
* Fix typo in naming (currency_code)
|
||||||
|
|
||||||
|
# 3.4.1 (2022-05-17)
|
||||||
|
|
||||||
|
* Fix optimizing non-public named closures
|
||||||
|
|
||||||
|
# 3.4.0 (2022-05-22)
|
||||||
|
|
||||||
|
* Add support for named closures
|
||||||
|
|
||||||
|
# 3.3.10 (2022-04-06)
|
||||||
|
|
||||||
|
* Enable bytecode invalidation when auto_reload is enabled
|
||||||
|
|
||||||
|
# 3.3.9 (2022-03-25)
|
||||||
|
|
||||||
|
* Fix custom escapers when using multiple Twig environments
|
||||||
|
* Add support for "constant('class', object)"
|
||||||
|
* Do not reuse internally generated variable names during parsing
|
||||||
|
|
||||||
# 3.3.8 (2022-02-04)
|
# 3.3.8 (2022-02-04)
|
||||||
|
|
||||||
* Fix a security issue when in a sandbox: the `sort` filter must require a Closure for the `arrow` parameter
|
* Fix a security issue when in a sandbox: the `sort` filter must require a Closure for the `arrow` parameter
|
||||||
|
* Fix deprecation notice on `round`
|
||||||
|
* Fix call to deprecated `convertToHtml` method
|
||||||
|
|
||||||
# 3.3.7 (2022-01-03)
|
# 3.3.7 (2022-01-03)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@
|
|||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.3-dev"
|
"dev-master": "3.4-dev"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -38,11 +38,11 @@ use Twig\TokenParser\TokenParserInterface;
|
|||||||
*/
|
*/
|
||||||
class Environment
|
class Environment
|
||||||
{
|
{
|
||||||
public const VERSION = '3.3.8';
|
public const VERSION = '3.4.3';
|
||||||
public const VERSION_ID = 30308;
|
public const VERSION_ID = 30403;
|
||||||
public const MAJOR_VERSION = 3;
|
public const MAJOR_VERSION = 3;
|
||||||
public const MINOR_VERSION = 3;
|
public const MINOR_VERSION = 4;
|
||||||
public const RELEASE_VERSION = 8;
|
public const RELEASE_VERSION = 3;
|
||||||
public const EXTRA_VERSION = '';
|
public const EXTRA_VERSION = '';
|
||||||
|
|
||||||
private $charset;
|
private $charset;
|
||||||
@@ -228,7 +228,7 @@ class Environment
|
|||||||
{
|
{
|
||||||
if (\is_string($cache)) {
|
if (\is_string($cache)) {
|
||||||
$this->originalCache = $cache;
|
$this->originalCache = $cache;
|
||||||
$this->cache = new FilesystemCache($cache);
|
$this->cache = new FilesystemCache($cache, $this->autoReload ? FilesystemCache::FORCE_BYTECODE_INVALIDATION : 0);
|
||||||
} elseif (false === $cache) {
|
} elseif (false === $cache) {
|
||||||
$this->originalCache = $cache;
|
$this->originalCache = $cache;
|
||||||
$this->cache = new NullCache();
|
$this->cache = new NullCache();
|
||||||
|
|||||||
@@ -485,7 +485,7 @@ class ExpressionParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new SyntaxError('Expected name or number.', $lineno, $stream->getSourceContext());
|
throw new SyntaxError(sprintf('Expected name or number, got value "%s" of type %s.', $token->getValue(), Token::typeToEnglish($token->getType())), $lineno, $stream->getSourceContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
|
if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
|
||||||
|
|||||||
@@ -1359,6 +1359,10 @@ function twig_source(Environment $env, $name, $ignoreMissing = false)
|
|||||||
function twig_constant($constant, $object = null)
|
function twig_constant($constant, $object = null)
|
||||||
{
|
{
|
||||||
if (null !== $object) {
|
if (null !== $object) {
|
||||||
|
if ('class' === $constant) {
|
||||||
|
return \get_class($object);
|
||||||
|
}
|
||||||
|
|
||||||
$constant = \get_class($object).'::'.$constant;
|
$constant = \get_class($object).'::'.$constant;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1376,6 +1380,10 @@ function twig_constant($constant, $object = null)
|
|||||||
function twig_constant_is_defined($constant, $object = null)
|
function twig_constant_is_defined($constant, $object = null)
|
||||||
{
|
{
|
||||||
if (null !== $object) {
|
if (null !== $object) {
|
||||||
|
if ('class' === $constant) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$constant = \get_class($object).'::'.$constant;
|
$constant = \get_class($object).'::'.$constant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -387,13 +387,8 @@ function twig_escape_filter(Environment $env, $string, $strategy = 'html', $char
|
|||||||
return rawurlencode($string);
|
return rawurlencode($string);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
static $escapers;
|
$escapers = $env->getExtension(EscaperExtension::class)->getEscapers();
|
||||||
|
if (array_key_exists($strategy, $escapers)) {
|
||||||
if (null === $escapers) {
|
|
||||||
$escapers = $env->getExtension(EscaperExtension::class)->getEscapers();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($escapers[$strategy])) {
|
|
||||||
return $escapers[$strategy]($env, $string, $charset);
|
return $escapers[$strategy]($env, $string, $charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,11 +91,11 @@ final class SandboxExtension extends AbstractExtension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkPropertyAllowed($obj, $method, int $lineno = -1, Source $source = null): void
|
public function checkPropertyAllowed($obj, $property, int $lineno = -1, Source $source = null): void
|
||||||
{
|
{
|
||||||
if ($this->isSandboxed()) {
|
if ($this->isSandboxed()) {
|
||||||
try {
|
try {
|
||||||
$this->policy->checkPropertyAllowed($obj, $method);
|
$this->policy->checkPropertyAllowed($obj, $property);
|
||||||
} catch (SecurityNotAllowedPropertyError $e) {
|
} catch (SecurityNotAllowedPropertyError $e) {
|
||||||
$e->setSourceContext($source);
|
$e->setSourceContext($source);
|
||||||
$e->setTemplateLine($lineno);
|
$e->setTemplateLine($lineno);
|
||||||
|
|||||||
@@ -183,9 +183,9 @@ class FilesystemLoader implements LoaderInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->validateName($name);
|
|
||||||
|
|
||||||
list($namespace, $shortname) = $this->parseName($name);
|
list($namespace, $shortname) = $this->parseName($name);
|
||||||
|
|
||||||
|
$this->validateName($shortname);
|
||||||
} catch (LoaderError $e) {
|
} catch (LoaderError $e) {
|
||||||
if (!$throw) {
|
if (!$throw) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -24,19 +24,20 @@ abstract class CallExpression extends AbstractExpression
|
|||||||
{
|
{
|
||||||
$callable = $this->getAttribute('callable');
|
$callable = $this->getAttribute('callable');
|
||||||
|
|
||||||
$closingParenthesis = false;
|
|
||||||
$isArray = false;
|
|
||||||
if (\is_string($callable) && false === strpos($callable, '::')) {
|
if (\is_string($callable) && false === strpos($callable, '::')) {
|
||||||
$compiler->raw($callable);
|
$compiler->raw($callable);
|
||||||
} else {
|
} else {
|
||||||
list($r, $callable) = $this->reflectCallable($callable);
|
[$r, $callable] = $this->reflectCallable($callable);
|
||||||
if ($r instanceof \ReflectionMethod && \is_string($callable[0])) {
|
|
||||||
if ($r->isStatic()) {
|
if (\is_string($callable)) {
|
||||||
|
$compiler->raw($callable);
|
||||||
|
} elseif (\is_array($callable) && \is_string($callable[0])) {
|
||||||
|
if (!$r instanceof \ReflectionMethod || $r->isStatic()) {
|
||||||
$compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
|
$compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
|
||||||
} else {
|
} else {
|
||||||
$compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
|
$compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
|
||||||
}
|
}
|
||||||
} elseif ($r instanceof \ReflectionMethod && $callable[0] instanceof ExtensionInterface) {
|
} elseif (\is_array($callable) && $callable[0] instanceof ExtensionInterface) {
|
||||||
$class = \get_class($callable[0]);
|
$class = \get_class($callable[0]);
|
||||||
if (!$compiler->getEnvironment()->hasExtension($class)) {
|
if (!$compiler->getEnvironment()->hasExtension($class)) {
|
||||||
// Compile a non-optimized call to trigger a \Twig\Error\RuntimeError, which cannot be a compile-time error
|
// Compile a non-optimized call to trigger a \Twig\Error\RuntimeError, which cannot be a compile-time error
|
||||||
@@ -47,17 +48,11 @@ abstract class CallExpression extends AbstractExpression
|
|||||||
|
|
||||||
$compiler->raw(sprintf('->%s', $callable[1]));
|
$compiler->raw(sprintf('->%s', $callable[1]));
|
||||||
} else {
|
} else {
|
||||||
$closingParenthesis = true;
|
$compiler->raw(sprintf('$this->env->get%s(\'%s\')->getCallable()', ucfirst($this->getAttribute('type')), $this->getAttribute('name')));
|
||||||
$isArray = true;
|
|
||||||
$compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), ', ucfirst($this->getAttribute('type')), $this->getAttribute('name')));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->compileArguments($compiler, $isArray);
|
$this->compileArguments($compiler);
|
||||||
|
|
||||||
if ($closingParenthesis) {
|
|
||||||
$compiler->raw(')');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function compileArguments(Compiler $compiler, $isArray = false): void
|
protected function compileArguments(Compiler $compiler, $isArray = false): void
|
||||||
@@ -244,10 +239,7 @@ abstract class CallExpression extends AbstractExpression
|
|||||||
|
|
||||||
private function getCallableParameters($callable, bool $isVariadic): array
|
private function getCallableParameters($callable, bool $isVariadic): array
|
||||||
{
|
{
|
||||||
list($r) = $this->reflectCallable($callable);
|
[$r, , $callableName] = $this->reflectCallable($callable);
|
||||||
if (null === $r) {
|
|
||||||
return [[], false];
|
|
||||||
}
|
|
||||||
|
|
||||||
$parameters = $r->getParameters();
|
$parameters = $r->getParameters();
|
||||||
if ($this->hasNode('node')) {
|
if ($this->hasNode('node')) {
|
||||||
@@ -274,11 +266,6 @@ abstract class CallExpression extends AbstractExpression
|
|||||||
array_pop($parameters);
|
array_pop($parameters);
|
||||||
$isPhpVariadic = true;
|
$isPhpVariadic = true;
|
||||||
} else {
|
} else {
|
||||||
$callableName = $r->name;
|
|
||||||
if ($r instanceof \ReflectionMethod) {
|
|
||||||
$callableName = $r->getDeclaringClass()->name.'::'.$callableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
|
throw new \LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,29 +279,41 @@ abstract class CallExpression extends AbstractExpression
|
|||||||
return $this->reflector;
|
return $this->reflector;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\is_array($callable)) {
|
if (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
|
||||||
if (!method_exists($callable[0], $callable[1])) {
|
$callable = [substr($callable, 0, $pos), substr($callable, 2 + $pos)];
|
||||||
// __call()
|
|
||||||
return [null, []];
|
|
||||||
}
|
|
||||||
$r = new \ReflectionMethod($callable[0], $callable[1]);
|
|
||||||
} elseif (\is_object($callable) && !$callable instanceof \Closure) {
|
|
||||||
$r = new \ReflectionObject($callable);
|
|
||||||
$r = $r->getMethod('__invoke');
|
|
||||||
$callable = [$callable, '__invoke'];
|
|
||||||
} elseif (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
|
|
||||||
$class = substr($callable, 0, $pos);
|
|
||||||
$method = substr($callable, $pos + 2);
|
|
||||||
if (!method_exists($class, $method)) {
|
|
||||||
// __staticCall()
|
|
||||||
return [null, []];
|
|
||||||
}
|
|
||||||
$r = new \ReflectionMethod($callable);
|
|
||||||
$callable = [$class, $method];
|
|
||||||
} else {
|
|
||||||
$r = new \ReflectionFunction($callable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->reflector = [$r, $callable];
|
if (\is_array($callable) && method_exists($callable[0], $callable[1])) {
|
||||||
|
$r = new \ReflectionMethod($callable[0], $callable[1]);
|
||||||
|
|
||||||
|
return $this->reflector = [$r, $callable, $r->class.'::'.$r->name];
|
||||||
|
}
|
||||||
|
|
||||||
|
$checkVisibility = $callable instanceof \Closure;
|
||||||
|
try {
|
||||||
|
$closure = \Closure::fromCallable($callable);
|
||||||
|
} catch (\TypeError $e) {
|
||||||
|
throw new \LogicException(sprintf('Callback for %s "%s" is not callable in the current scope.', $this->getAttribute('type'), $this->getAttribute('name')), 0, $e);
|
||||||
|
}
|
||||||
|
$r = new \ReflectionFunction($closure);
|
||||||
|
|
||||||
|
if (false !== strpos($r->name, '{closure}')) {
|
||||||
|
return $this->reflector = [$r, $callable, 'Closure'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($object = $r->getClosureThis()) {
|
||||||
|
$callable = [$object, $r->name];
|
||||||
|
$callableName = (\function_exists('get_debug_type') ? get_debug_type($object) : \get_class($object)).'::'.$r->name;
|
||||||
|
} elseif ($class = $r->getClosureScopeClass()) {
|
||||||
|
$callableName = (\is_array($callable) ? $callable[0] : $class->name).'::'.$r->name;
|
||||||
|
} else {
|
||||||
|
$callable = $callableName = $r->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($checkVisibility && \is_array($callable) && method_exists(...$callable) && !(new \ReflectionMethod(...$callable))->isPublic()) {
|
||||||
|
$callable = $r->getClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->reflector = [$r, $callable, $callableName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -58,7 +58,7 @@ class Parser
|
|||||||
public function parse(TokenStream $stream, $test = null, bool $dropNeedle = false): ModuleNode
|
public function parse(TokenStream $stream, $test = null, bool $dropNeedle = false): ModuleNode
|
||||||
{
|
{
|
||||||
$vars = get_object_vars($this);
|
$vars = get_object_vars($this);
|
||||||
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']);
|
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames'], $vars['varNameSalt']);
|
||||||
$this->stack[] = $vars;
|
$this->stack[] = $vars;
|
||||||
|
|
||||||
// node visitors
|
// node visitors
|
||||||
@@ -78,7 +78,6 @@ class Parser
|
|||||||
$this->blockStack = [];
|
$this->blockStack = [];
|
||||||
$this->importedSymbols = [[]];
|
$this->importedSymbols = [[]];
|
||||||
$this->embeddedTemplates = [];
|
$this->embeddedTemplates = [];
|
||||||
$this->varNameSalt = 0;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$body = $this->subparse($test, $dropNeedle);
|
$body = $this->subparse($test, $dropNeedle);
|
||||||
|
|||||||
@@ -19,17 +19,27 @@ namespace Twig\Sandbox;
|
|||||||
interface SecurityPolicyInterface
|
interface SecurityPolicyInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
* @param string[] $tags
|
||||||
|
* @param string[] $filters
|
||||||
|
* @param string[] $functions
|
||||||
|
*
|
||||||
* @throws SecurityError
|
* @throws SecurityError
|
||||||
*/
|
*/
|
||||||
public function checkSecurity($tags, $filters, $functions): void;
|
public function checkSecurity($tags, $filters, $functions): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param object $obj
|
||||||
|
* @param string $method
|
||||||
|
*
|
||||||
* @throws SecurityNotAllowedMethodError
|
* @throws SecurityNotAllowedMethodError
|
||||||
*/
|
*/
|
||||||
public function checkMethodAllowed($obj, $method): void;
|
public function checkMethodAllowed($obj, $method): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param object $obj
|
||||||
|
* @param string $property
|
||||||
|
*
|
||||||
* @throws SecurityNotAllowedPropertyError
|
* @throws SecurityNotAllowedPropertyError
|
||||||
*/
|
*/
|
||||||
public function checkPropertyAllowed($obj, $method): void;
|
public function checkPropertyAllowed($obj, $property): void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,9 @@ $qrprovider = new RobThree\Auth\Providers\Qr\QRServerProvider();
|
|||||||
$tfa = new RobThree\Auth\TwoFactorAuth($OTP_LABEL, 6, 30, 'sha1', $qrprovider);
|
$tfa = new RobThree\Auth\TwoFactorAuth($OTP_LABEL, 6, 30, 'sha1', $qrprovider);
|
||||||
|
|
||||||
// FIDO2
|
// FIDO2
|
||||||
|
$server_name = parse_url('https://' . $_SERVER['HTTP_HOST'], PHP_URL_HOST);
|
||||||
$formats = $GLOBALS['FIDO2_FORMATS'];
|
$formats = $GLOBALS['FIDO2_FORMATS'];
|
||||||
$WebAuthn = new lbuchs\WebAuthn\WebAuthn('WebAuthn Library', $_SERVER['HTTP_HOST'], $formats);
|
$WebAuthn = new lbuchs\WebAuthn\WebAuthn('WebAuthn Library', $server_name, $formats);
|
||||||
// only include root ca's when needed
|
// only include root ca's when needed
|
||||||
if (getenv('WEBAUTHN_ONLY_TRUSTED_VENDORS') == 'y') $WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates');
|
if (getenv('WEBAUTHN_ONLY_TRUSTED_VENDORS') == 'y') $WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates');
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
if (isset($_POST["verify_tfa_login"])) {
|
if (isset($_POST["verify_tfa_login"])) {
|
||||||
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST, $WebAuthn)) {
|
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) {
|
||||||
$_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
|
$_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
|
||||||
$_SESSION['mailcow_cc_role'] = $_SESSION['pending_mailcow_cc_role'];
|
$_SESSION['mailcow_cc_role'] = $_SESSION['pending_mailcow_cc_role'];
|
||||||
unset($_SESSION['pending_mailcow_cc_username']);
|
unset($_SESSION['pending_mailcow_cc_username']);
|
||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_method']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
|
|
||||||
header("Location: /user");
|
header("Location: /user");
|
||||||
} else {
|
} else {
|
||||||
unset($_SESSION['pending_mailcow_cc_username']);
|
unset($_SESSION['pending_mailcow_cc_username']);
|
||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_method']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_GET["cancel_tfa_login"])) {
|
if (isset($_GET["cancel_tfa_login"])) {
|
||||||
unset($_SESSION['pending_mailcow_cc_username']);
|
unset($_SESSION['pending_mailcow_cc_username']);
|
||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_method']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
|
|
||||||
header("Location: /");
|
header("Location: /");
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,7 @@ if (isset($_POST["quick_delete"])) {
|
|||||||
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
||||||
$login_user = strtolower(trim($_POST["login_user"]));
|
$login_user = strtolower(trim($_POST["login_user"]));
|
||||||
$as = check_login($login_user, $_POST["pass_user"]);
|
$as = check_login($login_user, $_POST["pass_user"]);
|
||||||
|
|
||||||
if ($as == "admin") {
|
if ($as == "admin") {
|
||||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||||
$_SESSION['mailcow_cc_role'] = "admin";
|
$_SESSION['mailcow_cc_role'] = "admin";
|
||||||
@@ -47,22 +48,22 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
|||||||
elseif ($as == "user") {
|
elseif ($as == "user") {
|
||||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||||
$_SESSION['mailcow_cc_role'] = "user";
|
$_SESSION['mailcow_cc_role'] = "user";
|
||||||
$http_parameters = explode('&', $_SESSION['index_query_string']);
|
$http_parameters = explode('&', $_SESSION['index_query_string']);
|
||||||
unset($_SESSION['index_query_string']);
|
unset($_SESSION['index_query_string']);
|
||||||
if (in_array('mobileconfig', $http_parameters)) {
|
if (in_array('mobileconfig', $http_parameters)) {
|
||||||
if (in_array('only_email', $http_parameters)) {
|
if (in_array('only_email', $http_parameters)) {
|
||||||
header("Location: /mobileconfig.php?email_only");
|
header("Location: /mobileconfig.php?email_only");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
header("Location: /mobileconfig.php");
|
header("Location: /mobileconfig.php");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
header("Location: /user");
|
header("Location: /user");
|
||||||
}
|
}
|
||||||
elseif ($as != "pending") {
|
elseif ($as != "pending") {
|
||||||
unset($_SESSION['pending_mailcow_cc_username']);
|
unset($_SESSION['pending_mailcow_cc_username']);
|
||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_method']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
unset($_SESSION['mailcow_cc_username']);
|
unset($_SESSION['mailcow_cc_username']);
|
||||||
unset($_SESSION['mailcow_cc_role']);
|
unset($_SESSION['mailcow_cc_role']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ $AVAILABLE_LANGUAGES = array(
|
|||||||
'ru' => 'Pусский (Russian)',
|
'ru' => 'Pусский (Russian)',
|
||||||
'sk' => 'Slovenčina (Slovak)',
|
'sk' => 'Slovenčina (Slovak)',
|
||||||
'sv' => 'Svenska (Swedish)',
|
'sv' => 'Svenska (Swedish)',
|
||||||
|
'tr' => 'Türkçe (Turkish)',
|
||||||
|
'uk' => 'Українська (Ukrainian)',
|
||||||
'zh' => '中文 (Chinese)'
|
'zh' => '中文 (Chinese)'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -227,3 +229,131 @@ $RSPAMD_MAPS = array(
|
|||||||
'Monitoring Hosts' => 'monitoring_nolog.map'
|
'Monitoring Hosts' => 'monitoring_nolog.map'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
$IMAPSYNC_OPTIONS = array(
|
||||||
|
'whitelist' => array(
|
||||||
|
'authmech1',
|
||||||
|
'authmech2',
|
||||||
|
'authuser1',
|
||||||
|
'authuser2',
|
||||||
|
'debugcontent',
|
||||||
|
'disarmreadreceipts',
|
||||||
|
'logdir',
|
||||||
|
'debugcrossduplicates',
|
||||||
|
'maxsize',
|
||||||
|
'minsize',
|
||||||
|
'minage',
|
||||||
|
'search',
|
||||||
|
'noabletosearch',
|
||||||
|
'pidfile',
|
||||||
|
'pidfilelocking',
|
||||||
|
'search1',
|
||||||
|
'search2',
|
||||||
|
'sslargs1',
|
||||||
|
'sslargs2',
|
||||||
|
'syncduplicates',
|
||||||
|
'usecache',
|
||||||
|
'synclabels',
|
||||||
|
'truncmess',
|
||||||
|
'domino2',
|
||||||
|
'expunge1',
|
||||||
|
'filterbuggyflags',
|
||||||
|
'justconnect',
|
||||||
|
'justfolders',
|
||||||
|
'maxlinelength',
|
||||||
|
'useheader',
|
||||||
|
'noabletosearch1',
|
||||||
|
'nolog',
|
||||||
|
'prefix1',
|
||||||
|
'prefix2',
|
||||||
|
'sep1',
|
||||||
|
'sep2',
|
||||||
|
'nofoldersizesatend',
|
||||||
|
'justfoldersizes',
|
||||||
|
'proxyauth1',
|
||||||
|
'skipemptyfolders',
|
||||||
|
'include',
|
||||||
|
'subfolder1',
|
||||||
|
'subscribed',
|
||||||
|
'subscribe',
|
||||||
|
'debug',
|
||||||
|
'debugimap2',
|
||||||
|
'domino1',
|
||||||
|
'exchange1',
|
||||||
|
'exchange2',
|
||||||
|
'justlogin',
|
||||||
|
'keepalive1',
|
||||||
|
'keepalive2',
|
||||||
|
'noabletosearch2',
|
||||||
|
'noexpunge2',
|
||||||
|
'noresyncflags',
|
||||||
|
'nossl1',
|
||||||
|
'nouidexpunge2',
|
||||||
|
'syncinternaldates',
|
||||||
|
'idatefromheader',
|
||||||
|
'useuid',
|
||||||
|
'debugflags',
|
||||||
|
'debugimap',
|
||||||
|
'delete1emptyfolders',
|
||||||
|
'delete2folders',
|
||||||
|
'gmail2',
|
||||||
|
'office1',
|
||||||
|
'testslive6',
|
||||||
|
'debugimap1',
|
||||||
|
'errorsmax',
|
||||||
|
'tests',
|
||||||
|
'gmail1',
|
||||||
|
'maxmessagespersecond',
|
||||||
|
'maxbytesafter',
|
||||||
|
'maxsleep',
|
||||||
|
'abort',
|
||||||
|
'resyncflags',
|
||||||
|
'resynclabels',
|
||||||
|
'syncacls',
|
||||||
|
'nosyncacls',
|
||||||
|
'nousecache',
|
||||||
|
'office2',
|
||||||
|
'testslive',
|
||||||
|
'debugmemory',
|
||||||
|
'exitwhenover',
|
||||||
|
'noid',
|
||||||
|
'noexpunge1',
|
||||||
|
'authmd51',
|
||||||
|
'logfile',
|
||||||
|
'proxyauth2',
|
||||||
|
'domain1',
|
||||||
|
'domain2',
|
||||||
|
'oauthaccesstoken1',
|
||||||
|
'oauthaccesstoken2',
|
||||||
|
'oauthdirect1',
|
||||||
|
'oauthdirect2',
|
||||||
|
'folder',
|
||||||
|
'folderrec',
|
||||||
|
'folderfirst',
|
||||||
|
'folderlast',
|
||||||
|
'nomixfolders',
|
||||||
|
'authmd52',
|
||||||
|
'debugfolders',
|
||||||
|
'nossl2',
|
||||||
|
'ssl2',
|
||||||
|
'tls2',
|
||||||
|
'notls2',
|
||||||
|
'debugssl',
|
||||||
|
'notls1',
|
||||||
|
'inet4',
|
||||||
|
'inet6',
|
||||||
|
'log',
|
||||||
|
'showpasswords'
|
||||||
|
),
|
||||||
|
'blacklist' => array(
|
||||||
|
'skipmess',
|
||||||
|
'delete2foldersonly',
|
||||||
|
'delete2foldersbutnot',
|
||||||
|
'regexflag',
|
||||||
|
'regexmess',
|
||||||
|
'pipemess',
|
||||||
|
'regextrans2',
|
||||||
|
'maxlinelengthcmd'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|||||||
+61
-42
@@ -99,37 +99,6 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
auto_fill_quota($('#addSelectDomain').val());
|
auto_fill_quota($('#addSelectDomain').val());
|
||||||
|
|
||||||
// Read bcc local dests
|
|
||||||
// Using ajax to not be a blocking moo
|
|
||||||
$.get("/api/v1/get/bcc-destination-options", function(data){
|
|
||||||
// Domains
|
|
||||||
var optgroup = "<optgroup label='" + lang.domains + "'>";
|
|
||||||
$.each(data.domains, function(index, domain){
|
|
||||||
optgroup += "<option value='" + domain + "'>" + domain + "</option>"
|
|
||||||
});
|
|
||||||
optgroup += "</optgroup>"
|
|
||||||
$('#bcc-local-dest').append(optgroup);
|
|
||||||
// Alias domains
|
|
||||||
var optgroup = "<optgroup label='" + lang.domain_aliases + "'>";
|
|
||||||
$.each(data.alias_domains, function(index, alias_domain){
|
|
||||||
optgroup += "<option value='" + alias_domain + "'>" + alias_domain + "</option>"
|
|
||||||
});
|
|
||||||
optgroup += "</optgroup>"
|
|
||||||
$('#bcc-local-dest').append(optgroup);
|
|
||||||
// Mailboxes and aliases
|
|
||||||
$.each(data.mailboxes, function(mailbox, aliases){
|
|
||||||
var optgroup = "<optgroup label='" + mailbox + "'>";
|
|
||||||
$.each(aliases, function(index, alias){
|
|
||||||
optgroup += "<option value='" + alias + "'>" + alias + "</option>"
|
|
||||||
});
|
|
||||||
optgroup += "</optgroup>"
|
|
||||||
$('#bcc-local-dest').append(optgroup);
|
|
||||||
});
|
|
||||||
// Finish
|
|
||||||
$('#bcc-local-dest').find('option:selected').remove();
|
|
||||||
$('#bcc-local-dest').selectpicker('refresh');
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".goto_checkbox").click(function( event ) {
|
$(".goto_checkbox").click(function( event ) {
|
||||||
$("form[data-id='add_alias'] .goto_checkbox").not(this).prop('checked', false);
|
$("form[data-id='add_alias'] .goto_checkbox").not(this).prop('checked', false);
|
||||||
if ($("form[data-id='add_alias'] .goto_checkbox:checked").length > 0) {
|
if ($("form[data-id='add_alias'] .goto_checkbox:checked").length > 0) {
|
||||||
@@ -584,6 +553,7 @@ jQuery(function($){
|
|||||||
'</div>';
|
'</div>';
|
||||||
item.chkbox = '<input type="checkbox" data-id="resource" name="multi_select" value="' + encodeURIComponent(item.name) + '" />';
|
item.chkbox = '<input type="checkbox" data-id="resource" name="multi_select" value="' + encodeURIComponent(item.name) + '" />';
|
||||||
item.name = escapeHtml(item.name);
|
item.name = escapeHtml(item.name);
|
||||||
|
item.description = escapeHtml(item.description);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -623,6 +593,37 @@ jQuery(function($){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
function draw_bcc_table() {
|
function draw_bcc_table() {
|
||||||
|
// Read bcc local dests
|
||||||
|
// Using ajax to not be a blocking moo
|
||||||
|
$.get("/api/v1/get/bcc-destination-options", function(data){
|
||||||
|
// Domains
|
||||||
|
var optgroup = "<optgroup label='" + lang.domains + "'>";
|
||||||
|
$.each(data.domains, function(index, domain){
|
||||||
|
optgroup += "<option value='" + domain + "'>" + domain + "</option>"
|
||||||
|
});
|
||||||
|
optgroup += "</optgroup>"
|
||||||
|
$('#bcc-local-dest').append(optgroup);
|
||||||
|
// Alias domains
|
||||||
|
var optgroup = "<optgroup label='" + lang.domain_aliases + "'>";
|
||||||
|
$.each(data.alias_domains, function(index, alias_domain){
|
||||||
|
optgroup += "<option value='" + alias_domain + "'>" + alias_domain + "</option>"
|
||||||
|
});
|
||||||
|
optgroup += "</optgroup>"
|
||||||
|
$('#bcc-local-dest').append(optgroup);
|
||||||
|
// Mailboxes and aliases
|
||||||
|
$.each(data.mailboxes, function(mailbox, aliases){
|
||||||
|
var optgroup = "<optgroup label='" + mailbox + "'>";
|
||||||
|
$.each(aliases, function(index, alias){
|
||||||
|
optgroup += "<option value='" + alias + "'>" + alias + "</option>"
|
||||||
|
});
|
||||||
|
optgroup += "</optgroup>"
|
||||||
|
$('#bcc-local-dest').append(optgroup);
|
||||||
|
});
|
||||||
|
// Finish
|
||||||
|
$('#bcc-local-dest').find('option:selected').remove();
|
||||||
|
$('#bcc-local-dest').selectpicker('refresh');
|
||||||
|
});
|
||||||
|
|
||||||
ft_bcc_table = FooTable.init('#bcc_table', {
|
ft_bcc_table = FooTable.init('#bcc_table', {
|
||||||
"columns": [
|
"columns": [
|
||||||
{"name":"chkbox","title":"","style":{"min-width":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
|
{"name":"chkbox","title":"","style":{"min-width":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
|
||||||
@@ -1022,7 +1023,7 @@ jQuery(function($){
|
|||||||
if (!item.exclude > 0) {
|
if (!item.exclude > 0) {
|
||||||
item.exclude = '-';
|
item.exclude = '-';
|
||||||
} else {
|
} else {
|
||||||
item.exclude = '<code>' + item.exclude + '</code>';
|
item.exclude = '<code>' + escapeHtml(item.exclude) + '</code>';
|
||||||
}
|
}
|
||||||
item.server_w_port = escapeHtml(item.user1) + '@' + item.host1 + ':' + item.port1;
|
item.server_w_port = escapeHtml(item.user1) + '@' + item.host1 + ':' + item.port1;
|
||||||
item.action = '<div class="btn-group footable-actions">' +
|
item.action = '<div class="btn-group footable-actions">' +
|
||||||
@@ -1160,15 +1161,33 @@ jQuery(function($){
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
})
|
})
|
||||||
|
|
||||||
draw_domain_table();
|
// detect element visibility changes
|
||||||
draw_mailbox_table();
|
function onVisible(element, callback) {
|
||||||
draw_resource_table();
|
$(element).ready(function() {
|
||||||
draw_alias_table();
|
element_object = document.querySelector(element)
|
||||||
draw_aliasdomain_table();
|
new IntersectionObserver((entries, observer) => {
|
||||||
draw_sync_job_table();
|
entries.forEach(entry => {
|
||||||
draw_filter_table();
|
if(entry.intersectionRatio > 0) {
|
||||||
draw_bcc_table();
|
callback(element_object);
|
||||||
draw_recipient_map_table();
|
observer.disconnect();
|
||||||
draw_tls_policy_table();
|
}
|
||||||
|
});
|
||||||
|
}).observe(element_object);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load only if the tab is visible
|
||||||
|
onVisible("[id^=tab-domains]", () => draw_domain_table());
|
||||||
|
onVisible("[id^=tab-mailboxes]", () => draw_mailbox_table());
|
||||||
|
onVisible("[id^=tab-resources]", () => draw_resource_table());
|
||||||
|
onVisible("[id^=tab-mbox-aliases]", () => draw_alias_table());
|
||||||
|
onVisible("[id^=tab-domain-aliases]", () => draw_aliasdomain_table());
|
||||||
|
onVisible("[id^=tab-syncjobs]", () => draw_sync_job_table());
|
||||||
|
onVisible("[id^=tab-filters]", () => draw_filter_table());
|
||||||
|
onVisible("[id^=tab-bcc]", () => {
|
||||||
|
draw_bcc_table();
|
||||||
|
draw_recipient_map_table();
|
||||||
|
});
|
||||||
|
onVisible("[id^=tab-tls-policy]", () => draw_tls_policy_table());
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
+28
-19
@@ -178,15 +178,22 @@ if (isset($_GET['query'])) {
|
|||||||
// parse post data
|
// parse post data
|
||||||
$post = trim(file_get_contents('php://input'));
|
$post = trim(file_get_contents('php://input'));
|
||||||
if ($post) $post = json_decode($post);
|
if ($post) $post = json_decode($post);
|
||||||
|
|
||||||
// decode base64 strings
|
|
||||||
$clientDataJSON = base64_decode($post->clientDataJSON);
|
|
||||||
$attestationObject = base64_decode($post->attestationObject);
|
|
||||||
|
|
||||||
// process registration data from authenticator
|
// process registration data from authenticator
|
||||||
try {
|
try {
|
||||||
|
// decode base64 strings
|
||||||
|
$clientDataJSON = base64_decode($post->clientDataJSON);
|
||||||
|
$attestationObject = base64_decode($post->attestationObject);
|
||||||
|
|
||||||
// processCreate($clientDataJSON, $attestationObject, $challenge, $requireUserVerification=false, $requireUserPresent=true, $failIfRootMismatch=true)
|
// processCreate($clientDataJSON, $attestationObject, $challenge, $requireUserVerification=false, $requireUserPresent=true, $failIfRootMismatch=true)
|
||||||
$data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $_SESSION['challenge'], false, true);
|
$data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $_SESSION['challenge'], false, true);
|
||||||
|
|
||||||
|
// safe authenticator in mysql `tfa` table
|
||||||
|
$_data['tfa_method'] = $post->tfa_method;
|
||||||
|
$_data['key_id'] = $post->key_id;
|
||||||
|
$_data['confirm_password'] = $post->confirm_password;
|
||||||
|
$_data['registration'] = $data;
|
||||||
|
set_tfa($_data);
|
||||||
}
|
}
|
||||||
catch (Throwable $ex) {
|
catch (Throwable $ex) {
|
||||||
// err
|
// err
|
||||||
@@ -197,11 +204,6 @@ if (isset($_GET['query'])) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// safe authenticator in mysql `tfa` table
|
|
||||||
$_data['tfa_method'] = $post->tfa_method;
|
|
||||||
$_data['key_id'] = $post->key_id;
|
|
||||||
$_data['registration'] = $data;
|
|
||||||
set_tfa($_data);
|
|
||||||
|
|
||||||
// send response
|
// send response
|
||||||
$return = new stdClass();
|
$return = new stdClass();
|
||||||
@@ -419,7 +421,7 @@ if (isset($_GET['query'])) {
|
|||||||
// }
|
// }
|
||||||
$ids = NULL;
|
$ids = NULL;
|
||||||
|
|
||||||
$getArgs = $WebAuthn->getGetArgs($ids, 30, true, true, true, true, $GLOBALS['FIDO2_UV_FLAG_LOGIN']);
|
$getArgs = $WebAuthn->getGetArgs($ids, 30, false, false, false, false, $GLOBALS['FIDO2_UV_FLAG_LOGIN']);
|
||||||
print(json_encode($getArgs));
|
print(json_encode($getArgs));
|
||||||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||||
return;
|
return;
|
||||||
@@ -428,8 +430,11 @@ if (isset($_GET['query'])) {
|
|||||||
case "webauthn-tfa-registration":
|
case "webauthn-tfa-registration":
|
||||||
if (isset($_SESSION["mailcow_cc_role"])) {
|
if (isset($_SESSION["mailcow_cc_role"])) {
|
||||||
// Exclude existing CredentialIds, if any
|
// Exclude existing CredentialIds, if any
|
||||||
$stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username");
|
$stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username AND authmech = :authmech");
|
||||||
$stmt->execute(array(':username' => $_SESSION['mailcow_cc_username']));
|
$stmt->execute(array(
|
||||||
|
':username' => $_SESSION['mailcow_cc_username'],
|
||||||
|
':authmech' => 'webauthn'
|
||||||
|
));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
while($row = array_shift($rows)) {
|
while($row = array_shift($rows)) {
|
||||||
$excludeCredentialIds[] = base64_decode($row['keyHandle']);
|
$excludeCredentialIds[] = base64_decode($row['keyHandle']);
|
||||||
@@ -450,20 +455,24 @@ if (isset($_GET['query'])) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "webauthn-tfa-get-args":
|
case "webauthn-tfa-get-args":
|
||||||
$stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username");
|
$stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username AND authmech = :authmech");
|
||||||
$stmt->execute(array(':username' => $_SESSION['pending_mailcow_cc_username']));
|
$stmt->execute(array(
|
||||||
|
':username' => $_SESSION['pending_mailcow_cc_username'],
|
||||||
|
':authmech' => 'webauthn'
|
||||||
|
));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
while($row = array_shift($rows)) {
|
if (count($rows) == 0) {
|
||||||
$cids[] = base64_decode($row['keyHandle']);
|
|
||||||
}
|
|
||||||
if (count($cids) == 0) {
|
|
||||||
print(json_encode(array(
|
print(json_encode(array(
|
||||||
'type' => 'error',
|
'type' => 'error',
|
||||||
'msg' => 'Cannot find matching credentialIds'
|
'msg' => 'Cannot find matching credentialIds'
|
||||||
)));
|
)));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$cids[] = base64_decode($row['keyHandle']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$getArgs = $WebAuthn->getGetArgs($cids, 30, true, true, true, true, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN']);
|
$getArgs = $WebAuthn->getGetArgs($cids, 30, false, false, false, false, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN']);
|
||||||
$getArgs->publicKey->extensions = array('appid' => "https://".$getArgs->publicKey->rpId);
|
$getArgs->publicKey->extensions = array('appid' => "https://".$getArgs->publicKey->rpId);
|
||||||
print(json_encode($getArgs));
|
print(json_encode($getArgs));
|
||||||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||||
|
|||||||
@@ -839,7 +839,7 @@
|
|||||||
"confirm_delete": "Potvrdit smazání prvku.",
|
"confirm_delete": "Potvrdit smazání prvku.",
|
||||||
"danger": "Nebezpečí",
|
"danger": "Nebezpečí",
|
||||||
"deliver_inbox": "Doručit do schránky",
|
"deliver_inbox": "Doručit do schránky",
|
||||||
"disabled_by_config": "Funkce karanténa je momentálně vypnuta v nastavení systému.",
|
"disabled_by_config": "Funkce karanténa je momentálně vypnuta v nastavení systému. Nastavte, prosím, prvkům karantény hodnoty \"počet zadržených zpráv\" a \"maximální velikost\".",
|
||||||
"download_eml": "Stáhnout (.eml)",
|
"download_eml": "Stáhnout (.eml)",
|
||||||
"empty": "Žádné výsledky",
|
"empty": "Žádné výsledky",
|
||||||
"high_danger": "Vysoké nebezpečí",
|
"high_danger": "Vysoké nebezpečí",
|
||||||
|
|||||||
@@ -106,7 +106,8 @@
|
|||||||
"timeout2": "Timeout für Verbindung zum lokalen Host",
|
"timeout2": "Timeout für Verbindung zum lokalen Host",
|
||||||
"username": "Benutzername",
|
"username": "Benutzername",
|
||||||
"validate": "Validieren",
|
"validate": "Validieren",
|
||||||
"validation_success": "Erfolgreich validiert"
|
"validation_success": "Erfolgreich validiert",
|
||||||
|
"tags": "Tags"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Zugang",
|
"access": "Zugang",
|
||||||
@@ -920,6 +921,7 @@
|
|||||||
"deleted_syncjob": "Sync-Jobs-ID %s gelöscht",
|
"deleted_syncjob": "Sync-Jobs-ID %s gelöscht",
|
||||||
"deleted_syncjobs": "Sync-Jobs gelöscht: %s",
|
"deleted_syncjobs": "Sync-Jobs gelöscht: %s",
|
||||||
"dkim_added": "DKIM-Key %s wurde hinzugefügt",
|
"dkim_added": "DKIM-Key %s wurde hinzugefügt",
|
||||||
|
"domain_add_dkim_available": "Ein DKIM-Key existierte bereits",
|
||||||
"dkim_duplicated": "DKIM-Key der Domain %s wurde auf Domain %s kopiert",
|
"dkim_duplicated": "DKIM-Key der Domain %s wurde auf Domain %s kopiert",
|
||||||
"dkim_removed": "DKIM-Key %s wurde entfernt",
|
"dkim_removed": "DKIM-Key %s wurde entfernt",
|
||||||
"domain_added": "Domain %s wurde angelegt",
|
"domain_added": "Domain %s wurde angelegt",
|
||||||
|
|||||||
@@ -928,6 +928,7 @@
|
|||||||
"deleted_syncjob": "Deleted syncjob ID %s",
|
"deleted_syncjob": "Deleted syncjob ID %s",
|
||||||
"deleted_syncjobs": "Deleted syncjobs: %s",
|
"deleted_syncjobs": "Deleted syncjobs: %s",
|
||||||
"dkim_added": "DKIM key %s has been saved",
|
"dkim_added": "DKIM key %s has been saved",
|
||||||
|
"domain_add_dkim_available": "A DKIM key did already exist",
|
||||||
"dkim_duplicated": "DKIM key for domain %s has been copied to %s",
|
"dkim_duplicated": "DKIM key for domain %s has been copied to %s",
|
||||||
"dkim_removed": "DKIM key %s has been removed",
|
"dkim_removed": "DKIM key %s has been removed",
|
||||||
"domain_added": "Added domain %s",
|
"domain_added": "Added domain %s",
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
"syncjobs": "Trabajos de sincronización",
|
"syncjobs": "Trabajos de sincronización",
|
||||||
"tls_policy": "Póliza de TLS",
|
"tls_policy": "Póliza de TLS",
|
||||||
"unlimited_quota": "Cuota ilimitada para buzones",
|
"unlimited_quota": "Cuota ilimitada para buzones",
|
||||||
"app_passwds": "Gestionar las contraseñas de aplicaciones"
|
"app_passwds": "Gestionar las contraseñas de aplicaciones",
|
||||||
|
"domain_desc": "Cambiar descripción del dominio"
|
||||||
},
|
},
|
||||||
"add": {
|
"add": {
|
||||||
"activate_filter_warn": "Todos los demás filtros se desactivarán cuando este filtro se active.",
|
"activate_filter_warn": "Todos los demás filtros se desactivarán cuando este filtro se active.",
|
||||||
|
|||||||
@@ -102,7 +102,8 @@
|
|||||||
"timeout2": "Délai d'expiration pour la connexion à l'hôte local",
|
"timeout2": "Délai d'expiration pour la connexion à l'hôte local",
|
||||||
"username": "Nom d'utilisateur",
|
"username": "Nom d'utilisateur",
|
||||||
"validate": "Valider",
|
"validate": "Valider",
|
||||||
"validation_success": "Validation réussie"
|
"validation_success": "Validation réussie",
|
||||||
|
"bcc_dest_format": "La destination Cci doit être une seule adresse e-mail valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici."
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Accès",
|
"access": "Accès",
|
||||||
@@ -322,7 +323,9 @@
|
|||||||
"yes": "✓",
|
"yes": "✓",
|
||||||
"api_read_write": "Accès Lecture-Écriture",
|
"api_read_write": "Accès Lecture-Écriture",
|
||||||
"oauth2_add_client": "Ajouter un client OAuth2",
|
"oauth2_add_client": "Ajouter un client OAuth2",
|
||||||
"password_policy": "Politique de mots de passe"
|
"password_policy": "Politique de mots de passe",
|
||||||
|
"admins": "Administrateurs",
|
||||||
|
"api_read_only": "Accès lecture-seule"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Accès refusé ou données de formulaire non valides",
|
"access_denied": "Accès refusé ou données de formulaire non valides",
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"acl": {
|
"acl": {
|
||||||
"alias_domains": "Aggiungi alias di dominio",
|
"alias_domains": "Aggiungi alias di dominio",
|
||||||
"app_passwds": "Gestisci le password delle app",
|
"app_passwds": "Gestisci le password delle app",
|
||||||
"bcc_maps": "BCC maps",
|
"bcc_maps": "Mappe CCN",
|
||||||
"delimiter_action": "Delimiter action",
|
"delimiter_action": "Azione delimitatrice",
|
||||||
"domain_desc": "Modifica la descrizione del dominio",
|
"domain_desc": "Modifica la descrizione del dominio",
|
||||||
"domain_relayhost": "Modifica relayhost per un dominio",
|
"domain_relayhost": "Modifica relayhost per un dominio",
|
||||||
"eas_reset": "Ripristina i dispositivi EAS",
|
"eas_reset": "Ripristina i dispositivi EAS",
|
||||||
@@ -106,7 +106,8 @@
|
|||||||
"validate": "Convalida",
|
"validate": "Convalida",
|
||||||
"validation_success": "Convalidato con successo",
|
"validation_success": "Convalidato con successo",
|
||||||
"bcc_dest_format": "Il destinatario in copia nascosta deve essere un singolo indirizzo email.<br>Se si vuole spedire una copia del messaggio a più destinatari, bisogna creare un alias ed utilizzarlo per questa opzione.",
|
"bcc_dest_format": "Il destinatario in copia nascosta deve essere un singolo indirizzo email.<br>Se si vuole spedire una copia del messaggio a più destinatari, bisogna creare un alias ed utilizzarlo per questa opzione.",
|
||||||
"app_passwd_protocols": "Protocolli consentiti per la password dell'app"
|
"app_passwd_protocols": "Protocolli consentiti per la password dell'app",
|
||||||
|
"tags": "Tag"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Accedi",
|
"access": "Accedi",
|
||||||
@@ -972,7 +973,8 @@
|
|||||||
"verified_fido2_login": "Verified FIDO2 login",
|
"verified_fido2_login": "Verified FIDO2 login",
|
||||||
"verified_totp_login": "Verified TOTP login",
|
"verified_totp_login": "Verified TOTP login",
|
||||||
"verified_webauthn_login": "Verified WebAuthn login",
|
"verified_webauthn_login": "Verified WebAuthn login",
|
||||||
"verified_yotp_login": "Verified Yubico OTP login"
|
"verified_yotp_login": "Verified Yubico OTP login",
|
||||||
|
"domain_add_dkim_available": "Esisteva già una chiave DKIM"
|
||||||
},
|
},
|
||||||
"tfa": {
|
"tfa": {
|
||||||
"api_register": "%s usa le API Yubico Cloud. Richiedi una chiave API <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">qui</a>",
|
"api_register": "%s usa le API Yubico Cloud. Richiedi una chiave API <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">qui</a>",
|
||||||
@@ -983,7 +985,7 @@
|
|||||||
"enter_qr_code": "Il codice TOTP se il tuo dispositivo non è in grado di acquisire i codici QR",
|
"enter_qr_code": "Il codice TOTP se il tuo dispositivo non è in grado di acquisire i codici QR",
|
||||||
"error_code": "Codice di errore",
|
"error_code": "Codice di errore",
|
||||||
"init_webauthn": "Inizializzazione, attendere prego...",
|
"init_webauthn": "Inizializzazione, attendere prego...",
|
||||||
"key_id": "Identificatore per il tuo YubiKey",
|
"key_id": "Identificatore per il tuo dispositivo",
|
||||||
"key_id_totp": "Identificatore per la tua chiave",
|
"key_id_totp": "Identificatore per la tua chiave",
|
||||||
"none": "Disattivato",
|
"none": "Disattivato",
|
||||||
"reload_retry": "- (ricaricare la pagina se l'errore persiste)",
|
"reload_retry": "- (ricaricare la pagina se l'errore persiste)",
|
||||||
@@ -997,7 +999,9 @@
|
|||||||
"waiting_usb_auth": "<i>In attesa del device USB...</i><br /><br />Tocca ora il pulsante sul dispositivo WebAuthn USB.",
|
"waiting_usb_auth": "<i>In attesa del device USB...</i><br /><br />Tocca ora il pulsante sul dispositivo WebAuthn USB.",
|
||||||
"waiting_usb_register": "<i>In attesa del device USB...</i><br /><br />Inserisci la tua password qui sopra e conferma la tua registrazione WebAuthn toccando il pulsante del dispositivo WebAuthn USB.",
|
"waiting_usb_register": "<i>In attesa del device USB...</i><br /><br />Inserisci la tua password qui sopra e conferma la tua registrazione WebAuthn toccando il pulsante del dispositivo WebAuthn USB.",
|
||||||
"yubi_otp": "Autenticazione Yubico OTP",
|
"yubi_otp": "Autenticazione Yubico OTP",
|
||||||
"tfa_token_invalid": "Token TFA non valido"
|
"tfa_token_invalid": "Token TFA non valido",
|
||||||
|
"u2f_deprecated": "Sembra che la tua chiave sia stata registrata utilizzando il metodo U2F deprecato. Disattiveremo Two-Factor-Authenticaiton per te e cancelleremo la tua chiave.",
|
||||||
|
"u2f_deprecated_important": "Registra la tua chiave nel pannello di amministrazione con il nuovo metodo WebAuthn."
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"action": "Azione",
|
"action": "Azione",
|
||||||
|
|||||||
@@ -979,7 +979,8 @@
|
|||||||
"verified_totp_login": "Autentificarea TOTP verificată",
|
"verified_totp_login": "Autentificarea TOTP verificată",
|
||||||
"verified_webauthn_login": "Autentificarea WebAuthn verificată",
|
"verified_webauthn_login": "Autentificarea WebAuthn verificată",
|
||||||
"verified_fido2_login": "Conectare FIDO2 verificată",
|
"verified_fido2_login": "Conectare FIDO2 verificată",
|
||||||
"verified_yotp_login": "Autentificarea Yubico OTP verificată"
|
"verified_yotp_login": "Autentificarea Yubico OTP verificată",
|
||||||
|
"domain_add_dkim_available": "O cheie DKIM deja a existat"
|
||||||
},
|
},
|
||||||
"tfa": {
|
"tfa": {
|
||||||
"api_register": "%s utilizează API-ul Yubico Cloud. Obțineți o cheie API pentru cheia dvs. de <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">aici</a>",
|
"api_register": "%s utilizează API-ul Yubico Cloud. Obțineți o cheie API pentru cheia dvs. de <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">aici</a>",
|
||||||
@@ -990,7 +991,7 @@
|
|||||||
"enter_qr_code": "Codul tău TOTP dacă dispozitivul tău nu poate scana codurile QR",
|
"enter_qr_code": "Codul tău TOTP dacă dispozitivul tău nu poate scana codurile QR",
|
||||||
"error_code": "Cod de eroare",
|
"error_code": "Cod de eroare",
|
||||||
"init_webauthn": "Inițializare, vă rugăm așteptați...",
|
"init_webauthn": "Inițializare, vă rugăm așteptați...",
|
||||||
"key_id": "Un identificator pentru YubiKey",
|
"key_id": "Un identificator pentru dispozitiv",
|
||||||
"key_id_totp": "Un identificator pentru cheia ta",
|
"key_id_totp": "Un identificator pentru cheia ta",
|
||||||
"none": "Dezactivează",
|
"none": "Dezactivează",
|
||||||
"reload_retry": "- (reîncărcați browserul dacă eroarea persistă)",
|
"reload_retry": "- (reîncărcați browserul dacă eroarea persistă)",
|
||||||
|
|||||||
+14
-10
@@ -105,7 +105,9 @@
|
|||||||
"timeout2": "Тайм-аут для подключения к локальному хосту",
|
"timeout2": "Тайм-аут для подключения к локальному хосту",
|
||||||
"username": "Имя пользователя",
|
"username": "Имя пользователя",
|
||||||
"validate": "Проверить",
|
"validate": "Проверить",
|
||||||
"validation_success": "Проверка прошла успешно"
|
"validation_success": "Проверка прошла успешно",
|
||||||
|
"tags": "Теги",
|
||||||
|
"app_passwd_protocols": "Разрешенные протоколы для пароля приложения"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Настройки доступа",
|
"access": "Настройки доступа",
|
||||||
@@ -190,7 +192,7 @@
|
|||||||
"flush_queue": "Отправить все сообщения",
|
"flush_queue": "Отправить все сообщения",
|
||||||
"forwarding_hosts": "Переадресация хостов",
|
"forwarding_hosts": "Переадресация хостов",
|
||||||
"forwarding_hosts_add_hint": "Можно указывать: IPv4/IPv6 подсети в нотации CIDR, имена хостов (которые будут разрешаться в IP-адреса) или доменные имена (которые будут решаться с IP-адресами путем запроса SPF записей или, в случае их отсутствия - запросом MX записей).",
|
"forwarding_hosts_add_hint": "Можно указывать: IPv4/IPv6 подсети в нотации CIDR, имена хостов (которые будут разрешаться в IP-адреса) или доменные имена (которые будут решаться с IP-адресами путем запроса SPF записей или, в случае их отсутствия - запросом MX записей).",
|
||||||
"forwarding_hosts_hint": "Входящие сообщения безоговорочно принимаются от любых хостов, перечисленных здесь. Эти хосты не проходят проверку DNSBL и graylisting. Спам, полученный от них, никогда не отклоняется, но при желании можно включить спам фильтр и письма с плохим рейтингом будут попадать в Junk. Наиболее распространенное использование - указать почтовые серверы, на которых вы установили правило, которое перенаправляет входящие электронные письма на ваш почтовый сервер.",
|
"forwarding_hosts_hint": "Входящие сообщения безоговорочно принимаются от любых хостов, перечисленных здесь. Эти хосты не проходят проверку DNSBL и graylisting. Спам, полученный от них, никогда не отклоняется, но при желании можно включить спам фильтр и письма с плохим рейтингом будут попадать в Junk. Наиболее распространенное использование - указать почтовые серверы, на которых вы установили правило, которое перенаправляет входящие электронные письма на ваш почтовый сервер mailcow.",
|
||||||
"from": "От",
|
"from": "От",
|
||||||
"generate": "сгенерировать",
|
"generate": "сгенерировать",
|
||||||
"guid": "GUID - уникальный ID",
|
"guid": "GUID - уникальный ID",
|
||||||
@@ -460,7 +462,8 @@
|
|||||||
"unlimited_quota_acl": "Неограниченная квота запрещена политикой доступа",
|
"unlimited_quota_acl": "Неограниченная квота запрещена политикой доступа",
|
||||||
"username_invalid": "Имя пользователя %s нельзя использовать",
|
"username_invalid": "Имя пользователя %s нельзя использовать",
|
||||||
"validity_missing": "Пожалуйста, назначьте срок действия",
|
"validity_missing": "Пожалуйста, назначьте срок действия",
|
||||||
"value_missing": "Пожалуйста заполните все поля"
|
"value_missing": "Пожалуйста заполните все поля",
|
||||||
|
"yotp_verification_failed": "Ошибка валидации Yubico OTP: %s"
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"chart_this_server": "Диаграмма (текущий сервер)",
|
"chart_this_server": "Диаграмма (текущий сервер)",
|
||||||
@@ -886,11 +889,11 @@
|
|||||||
"type": "Тип"
|
"type": "Тип"
|
||||||
},
|
},
|
||||||
"ratelimit": {
|
"ratelimit": {
|
||||||
"disabled": "Отключен",
|
"disabled": "Отключен",
|
||||||
"second": "сообщений / секунду",
|
"second": "сообщений / секунду",
|
||||||
"minute": "сообщений / минуту",
|
"minute": "сообщений / минуту",
|
||||||
"hour": "сообщений / час",
|
"hour": "сообщений / час",
|
||||||
"day": "сообщений / день"
|
"day": "сообщений / день"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
"help": "Справка",
|
"help": "Справка",
|
||||||
@@ -985,7 +988,7 @@
|
|||||||
"enter_qr_code": "Ваш код TOTP, если устройство не может отсканировать QR-код",
|
"enter_qr_code": "Ваш код TOTP, если устройство не может отсканировать QR-код",
|
||||||
"error_code": "Код ошибки",
|
"error_code": "Код ошибки",
|
||||||
"init_webauthn": "Инициализация, пожалуйста, подождите...",
|
"init_webauthn": "Инициализация, пожалуйста, подождите...",
|
||||||
"key_id": "Идентификатор YubiKey ключа",
|
"key_id": "Идентификатор вашего устройства",
|
||||||
"key_id_totp": "Идентификатор TOTP ключа",
|
"key_id_totp": "Идентификатор TOTP ключа",
|
||||||
"none": "Отключить",
|
"none": "Отключить",
|
||||||
"reload_retry": "- (перезагрузить страницу браузера или почистите кеш/cookies, если ошибка повторяется)",
|
"reload_retry": "- (перезагрузить страницу браузера или почистите кеш/cookies, если ошибка повторяется)",
|
||||||
@@ -999,7 +1002,8 @@
|
|||||||
"webauthn": "WebAuthn аутентификация",
|
"webauthn": "WebAuthn аутентификация",
|
||||||
"waiting_usb_auth": "<i>Ожидание устройства USB...</i><br><br>Пожалуйста, нажмите кнопку на USB устройстве сейчас.",
|
"waiting_usb_auth": "<i>Ожидание устройства USB...</i><br><br>Пожалуйста, нажмите кнопку на USB устройстве сейчас.",
|
||||||
"waiting_usb_register": "<i>Ожидание устройства USB...</i><br><br>Пожалуйста, введите пароль выше и подтвердите регистрацию, нажав кнопку на USB устройстве.",
|
"waiting_usb_register": "<i>Ожидание устройства USB...</i><br><br>Пожалуйста, введите пароль выше и подтвердите регистрацию, нажав кнопку на USB устройстве.",
|
||||||
"yubi_otp": "Yubico OTP аутентификация"
|
"yubi_otp": "Yubico OTP аутентификация",
|
||||||
|
"u2f_deprecated": "Похоже, что ваш ключ был зарегистрирован с использованием устаревшего метода U2F. Мы деактивируем для вас двухфакторную аутентификацию и удалим ваш ключ."
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"action": "Действия",
|
"action": "Действия",
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
{
|
||||||
|
"acl": {
|
||||||
|
"alias_domains": "Takma alan adı ekle",
|
||||||
|
"app_passwds": "Uygulama şifrelerini yönet",
|
||||||
|
"delimiter_action": "Sınırlama işlemi",
|
||||||
|
"domain_relayhost": "Bir alan adı için relayhost sunucusunu değiştir",
|
||||||
|
"eas_reset": "EAS cihazlarını sıfırla",
|
||||||
|
"mailbox_relayhost": "Bir posta kutusunun relayhost sunucularını değiştir",
|
||||||
|
"pushover": "Bildirim",
|
||||||
|
"quarantine": "Karantina işlemleri",
|
||||||
|
"quarantine_attachments": "Ekleri karantinaya al",
|
||||||
|
"quarantine_notification": "Karantina bildirimlerini değiştir",
|
||||||
|
"smtp_ip_access": "SMTP sunucularının değiştirilmesine izin ver",
|
||||||
|
"sogo_access": "SOGo erişiminin yönetilmesine izin ver",
|
||||||
|
"domain_desc": "Alan adı açıklamasını değiştir",
|
||||||
|
"extend_sender_acl": "Gönderenin acl'sini harici adreslere göre genişletmeye izin ver",
|
||||||
|
"spam_policy": "Engellenenler / İzin verilenler",
|
||||||
|
"filters": "Fitreler"
|
||||||
|
},
|
||||||
|
"add": {
|
||||||
|
"activate_filter_warn": "Aktif edilirse diğer tüm filtreler devre dışı bırakılacak.",
|
||||||
|
"add_domain_only": "Sadece alan adı ekle",
|
||||||
|
"alias_address": "Takma ad adres(leri)",
|
||||||
|
"alias_domain": "Takma alan adı",
|
||||||
|
"alias_domain_info": "<small>Sadece geçerli alan adları (virgülle ayırın).</small>",
|
||||||
|
"backup_mx_options": "İletme ayarları",
|
||||||
|
"delete2": "Kaynakta olmayan hedefteki mesajları sil",
|
||||||
|
"delete2duplicates": "Hedefteki kopyaları sil",
|
||||||
|
"disable_login": "Giriş yapmaya izin verme ( Gelen mailler yine de kabul edilir)",
|
||||||
|
"domain": "Alan adı",
|
||||||
|
"domain_matches_hostname": "Alan adı %s ana bilgisayar adıyla eşleşiyor",
|
||||||
|
"add_domain_restart": "Alan adı ekleyin ve SOGo'yu yeniden başlatın",
|
||||||
|
"alias_address_info": "<small>Bir alan adına ilişkin tüm iletileri yakalamak için tam e-posta adresi veya @example.com olacak şeklinde girin (virgülle ayırın).<b>sadece mailcow alan adları</b>.</small>",
|
||||||
|
"domain_quota_m": "Toplam alan adı kotası (MiB)",
|
||||||
|
"generate": "oluştur",
|
||||||
|
"goto_ham": "Ham olarak<span class=\"text-success\"><b>işaretle</b></span>",
|
||||||
|
"goto_null": "Postaları sessizce çöpe at",
|
||||||
|
"goto_spam": "Spam olarak<span class=\"text-danger\"><b>işaretle</b></span>",
|
||||||
|
"hostname": "Ana sunucu",
|
||||||
|
"kind": "Tür",
|
||||||
|
"mailbox_quota_m": "Posta kutusu başına maksimum kota (MiB)",
|
||||||
|
"max_aliases": "Maksimum olası takma adı",
|
||||||
|
"max_mailboxes": "Maksimum olası posta kutusu",
|
||||||
|
"nexthop": "Sonraki atlama",
|
||||||
|
"port": "Port",
|
||||||
|
"public_comment": "Genel yorum",
|
||||||
|
"relay_all": "Tüm alıcılara ilet",
|
||||||
|
"relay_all_info": "Eğer <b>hiçbir</b> alıcıya iletilmemesini seçerseniz, aktarılması gereken her alıcı için bir (\"kör\") posta kutusu eklemeniz gerekecektir.",
|
||||||
|
"relay_domain": "Bu alan adını ilet",
|
||||||
|
"relay_transport_info": "<div class=\"label label-info\">Bilgi</div> Bu etki alanı için özel bir hedef için aktarım eşlemeleri tanımlayabilirsiniz. Ayarlanmazsa, bir MX araması yapılacaktır.",
|
||||||
|
"relay_unknown_only": "Yalnızca mevcut olmayan posta kutularını ilet. Mevcut posta kutuları yerel olarak teslim edilecektir.",
|
||||||
|
"relayhost_wrapped_tls_info": "Lütfen TLS ile örtülmüş portları <b> kullanmayın</b> (çoğu 465 portunda çalışır).<br>\nÖrtülmemiş port kullan ve STARTTLS üzerinden yayınla. TLS'yi zorlamak için bir TLS ilkesi \"TLS ilke eşlemeleri\" sayfası içinde oluşturulabilir.",
|
||||||
|
"skipcrossduplicates": "Klasörler arasında yinelenen mesajları atlayın (ilk mesaj seçilir)",
|
||||||
|
"target_address": "Adreslere git",
|
||||||
|
"target_address_info": "<small>Tam e-posta adres(leri) girin ( virgülle ayırın).</small>",
|
||||||
|
"target_domain": "Hedef alan adı",
|
||||||
|
"timeout1": "Uzak ana bilgisayara bağlantısı zaman aşımına uğradı",
|
||||||
|
"timeout2": "Yerel ana bilgisayara bağlantı zaman aşımına uğradı"
|
||||||
|
},
|
||||||
|
"admin": {
|
||||||
|
"action": "İşlem",
|
||||||
|
"add_forwarding_host": "Yönlendirme sunucusu ekle",
|
||||||
|
"add_transport": "İletim ekle",
|
||||||
|
"admin_details": "Yönetici detaylarını düzenle",
|
||||||
|
"admin_domains": "Alan adı atamaları",
|
||||||
|
"add_domain_admin": "Alan adı yöneticisi ekle",
|
||||||
|
"api_info": "API üzerinde çalışmalar devam etmektedir. Belgeler <a href=\"/api\">/api</a>adresinde bulunabilir",
|
||||||
|
"apps_name": "\"mailcow Uygulamaları\" adı",
|
||||||
|
"authed_user": "Yetkili kullanıcı",
|
||||||
|
"ban_list_info": "Aşağıdaki yasaklı IP'lerin listesine bakın: <b>ağ (kalan yasak süresi) - [işlemler]</b>.<br />Yasağı kaldırılmak üzere sıraya alınan IP'ler birkaç saniye içinde aktif yasak listesinden kaldırılacaktır.<br />Kırmızı etiketler, kara listeye alınarak aktif kalıcı yasakları gösterir.",
|
||||||
|
"configuration": "Yapılandırma",
|
||||||
|
"dkim_from_title": "Verilerin kopyalanacağı kaynak alan adı",
|
||||||
|
"dkim_to": "Kime",
|
||||||
|
"dkim_to_title": "Hedef alan ad(ları) üzerinde yazılacak",
|
||||||
|
"dkim_domains_wo_keys": "Eksik anahtarları olan alan adlarını seçin",
|
||||||
|
"domain": "Alan adı",
|
||||||
|
"domain_admin": "Alan adı yöneticisi",
|
||||||
|
"domain_admins": "Alan adı yöneticileri",
|
||||||
|
"domain_s": "Alan ad(ları)",
|
||||||
|
"duplicate": "Çift",
|
||||||
|
"duplicate_dkim": "Çift DKIM kayıtları",
|
||||||
|
"f2b_ban_time": "Yasaklama süresi (saniye)",
|
||||||
|
"f2b_max_attempts": "Maksimum giriş denemesi",
|
||||||
|
"f2b_retry_window": "Maksimum girişim için deneme pencere(leri)"
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+101
-27
@@ -176,15 +176,75 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
// Confirm TFA modal
|
// Confirm TFA modal
|
||||||
{% if pending_tfa_method %}
|
{% if pending_tfa_methods %}
|
||||||
$('#ConfirmTFAModal').modal({
|
$('#ConfirmTFAModal').modal({
|
||||||
backdrop: 'static',
|
backdrop: 'static',
|
||||||
keyboard: false
|
keyboard: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// validate Time based OTP tfa
|
||||||
|
$("#pending_tfa_tab_totp").click(function(){
|
||||||
|
$(".webauthn-authenticator-selection").removeClass("active");
|
||||||
|
$("#collapseWebAuthnTFA").collapse('hide');
|
||||||
|
|
||||||
|
// select default if only one authenticator exists
|
||||||
|
if ($('.totp-authenticator-selection').length == 1){
|
||||||
|
$('.totp-authenticator-selection').addClass("active");
|
||||||
|
var id = $('.totp-authenticator-selection').children('input').first().val();
|
||||||
|
$("#totp_selected_id").val(id);
|
||||||
|
$("#collapseTotpTFA").collapse('show');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$(".totp-authenticator-selection").click(function(){
|
||||||
|
$(".totp-authenticator-selection").removeClass("active");
|
||||||
|
$(this).addClass("active");
|
||||||
|
|
||||||
|
var id = $(this).children('input').first().val();
|
||||||
|
$("#totp_selected_id").val(id);
|
||||||
|
|
||||||
|
$("#collapseTotpTFA").collapse('show');
|
||||||
|
});
|
||||||
|
if ($('.totp-authenticator-selection').length == 1 &&
|
||||||
|
$('#pending_tfa_tab_yubi_otp').length == 0 &&
|
||||||
|
$('.webauthn-authenticator-selection').length == 0){
|
||||||
|
|
||||||
|
// select default if only one authenticator exists
|
||||||
|
$('.totp-authenticator-selection').addClass("active");
|
||||||
|
|
||||||
|
var id = $('.totp-authenticator-selection').children('input').first().val();
|
||||||
|
$("#totp_selected_id").val(id);
|
||||||
|
|
||||||
|
$("#collapseTotpTFA").collapse('show');
|
||||||
|
setTimeout(function() { $("#collapseTotpTFA").find('input[name="token"]').focus(); }, 1000);
|
||||||
|
}
|
||||||
|
$('#pending_tfa_tab_totp').on('shown.bs.tab', function() {
|
||||||
|
// autofocus
|
||||||
|
setTimeout(function() { $("#collapseTotpTFA").find('input[name="token"]').focus(); }, 200);
|
||||||
|
});
|
||||||
|
// validate Yubi OTP tfa
|
||||||
|
if ($('.webauthn-authenticator-selection').length == 0){
|
||||||
|
// autofocus
|
||||||
|
setTimeout(function() { $("#collapseYubiTFA").find('input[name="token"]').focus(); }, 1000);
|
||||||
|
}
|
||||||
|
$('#pending_tfa_tab_yubi_otp').on('shown.bs.tab', function() {
|
||||||
|
// autofocus
|
||||||
|
$("#collapseYubiTFA").find('input[name="token"]').focus();
|
||||||
|
});
|
||||||
// validate WebAuthn tfa
|
// validate WebAuthn tfa
|
||||||
$('#start_webauthn_confirmation').click(function(){
|
$("#pending_tfa_tab_webauthn").click(function(){
|
||||||
$('#webauthn_status_auth').html('<p><i class="bi bi-arrow-repeat icon-spin"></i> ' + lang_tfa.init_webauthn + '</p>');
|
$(".totp-authenticator-selection").removeClass("active");
|
||||||
|
|
||||||
|
$("#collapseTotpTFA").collapse('hide');
|
||||||
|
});
|
||||||
|
$(".webauthn-authenticator-selection").click(function(){
|
||||||
|
$(".webauthn-authenticator-selection").removeClass("active");
|
||||||
|
$(this).addClass("active");
|
||||||
|
|
||||||
|
var id = $(this).children('input').first().val();
|
||||||
|
$("#webauthn_selected_id").val(id);
|
||||||
|
|
||||||
|
$("#collapseWebAuthnTFA").collapse('show');
|
||||||
|
|
||||||
$(this).find('input[name=token]').focus();
|
$(this).find('input[name=token]').focus();
|
||||||
if(document.getElementById("webauthn_auth_data") !== null) {
|
if(document.getElementById("webauthn_auth_data") !== null) {
|
||||||
@@ -198,30 +258,32 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||||||
window.fetch("/api/v1/get/webauthn-tfa-get-args", {method:'GET',cache:'no-cache'}).then(response => {
|
window.fetch("/api/v1/get/webauthn-tfa-get-args", {method:'GET',cache:'no-cache'}).then(response => {
|
||||||
return response.json();
|
return response.json();
|
||||||
}).then(json => {
|
}).then(json => {
|
||||||
if (json.success === false) throw new Error();
|
console.log(json);
|
||||||
|
if (json.success === false) throw new Error();
|
||||||
|
if (json.type === "error") throw new Error(json.msg);
|
||||||
|
|
||||||
recursiveBase64StrToArrayBuffer(json);
|
recursiveBase64StrToArrayBuffer(json);
|
||||||
return json;
|
return json;
|
||||||
}).then(getCredentialArgs => {
|
}).then(getCredentialArgs => {
|
||||||
// get credentials
|
// get credentials
|
||||||
return navigator.credentials.get(getCredentialArgs);
|
return navigator.credentials.get(getCredentialArgs);
|
||||||
}).then(cred => {
|
}).then(cred => {
|
||||||
return {
|
return {
|
||||||
id: cred.rawId ? arrayBufferToBase64(cred.rawId) : null,
|
id: cred.rawId ? arrayBufferToBase64(cred.rawId) : null,
|
||||||
clientDataJSON: cred.response.clientDataJSON ? arrayBufferToBase64(cred.response.clientDataJSON) : null,
|
clientDataJSON: cred.response.clientDataJSON ? arrayBufferToBase64(cred.response.clientDataJSON) : null,
|
||||||
authenticatorData: cred.response.authenticatorData ? arrayBufferToBase64(cred.response.authenticatorData) : null,
|
authenticatorData: cred.response.authenticatorData ? arrayBufferToBase64(cred.response.authenticatorData) : null,
|
||||||
signature : cred.response.signature ? arrayBufferToBase64(cred.response.signature) : null
|
signature : cred.response.signature ? arrayBufferToBase64(cred.response.signature) : null
|
||||||
};
|
};
|
||||||
}).then(JSON.stringify).then(function(AuthenticatorAttestationResponse) {
|
}).then(JSON.stringify).then(function(AuthenticatorAttestationResponse) {
|
||||||
// send request by submit
|
// send request by submit
|
||||||
var form = document.getElementById('webauthn_auth_form');
|
var form = document.getElementById('webauthn_auth_form');
|
||||||
var auth = document.getElementById('webauthn_auth_data');
|
var auth = document.getElementById('webauthn_auth_data');
|
||||||
auth.value = AuthenticatorAttestationResponse;
|
auth.value = AuthenticatorAttestationResponse;
|
||||||
form.submit();
|
form.submit();
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
var webauthn_return_code = document.getElementById('webauthn_return_code');
|
var webauthn_return_code = document.getElementById('webauthn_return_code');
|
||||||
webauthn_return_code.style.display = webauthn_return_code.style.display === 'none' ? '' : null;
|
webauthn_return_code.style.display = webauthn_return_code.style.display === 'none' ? '' : null;
|
||||||
webauthn_return_code.innerHTML = lang_tfa.error_code + ': ' + err + ' ' + lang_tfa.reload_retry;
|
webauthn_return_code.innerHTML = lang_tfa.error_code + ': ' + err + ' ' + lang_tfa.reload_retry;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -237,7 +299,9 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
// Validate FIDO2
|
// Validate FIDO2
|
||||||
$("#fido2-login").click(function(){
|
$("#fido2-login").click(function(){
|
||||||
$('#fido2-alerts').html();
|
$('#fido2-alerts').html();
|
||||||
@@ -358,11 +422,13 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||||||
|
|
||||||
$("#start_webauthn_register").click(() => {
|
$("#start_webauthn_register").click(() => {
|
||||||
var key_id = document.getElementsByName('key_id')[1].value;
|
var key_id = document.getElementsByName('key_id')[1].value;
|
||||||
|
var confirm_password = document.getElementsByName('confirm_password')[1].value;
|
||||||
|
|
||||||
// fetch WebAuthn create args
|
// fetch WebAuthn create args
|
||||||
window.fetch("/api/v1/get/webauthn-tfa-registration/{{ mailcow_cc_username|url_encode(true)|default('null') }}", {method:'GET',cache:'no-cache'}).then(response => {
|
window.fetch("/api/v1/get/webauthn-tfa-registration/{{ mailcow_cc_username|url_encode(true)|default('null') }}", {method:'GET',cache:'no-cache'}).then(response => {
|
||||||
return response.json();
|
return response.json();
|
||||||
}).then(json => {
|
}).then(json => {
|
||||||
|
console.log(json);
|
||||||
if (json.success === false) throw new Error(json.msg);
|
if (json.success === false) throw new Error(json.msg);
|
||||||
recursiveBase64StrToArrayBuffer(json);
|
recursiveBase64StrToArrayBuffer(json);
|
||||||
|
|
||||||
@@ -375,7 +441,8 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||||||
clientDataJSON: cred.response.clientDataJSON ? arrayBufferToBase64(cred.response.clientDataJSON) : null,
|
clientDataJSON: cred.response.clientDataJSON ? arrayBufferToBase64(cred.response.clientDataJSON) : null,
|
||||||
attestationObject: cred.response.attestationObject ? arrayBufferToBase64(cred.response.attestationObject) : null,
|
attestationObject: cred.response.attestationObject ? arrayBufferToBase64(cred.response.attestationObject) : null,
|
||||||
key_id: key_id,
|
key_id: key_id,
|
||||||
tfa_method: "webauthn"
|
tfa_method: "webauthn",
|
||||||
|
confirm_password: confirm_password
|
||||||
};
|
};
|
||||||
}).then(JSON.stringify).then(AuthenticatorAttestationResponse => {
|
}).then(JSON.stringify).then(AuthenticatorAttestationResponse => {
|
||||||
// send request
|
// send request
|
||||||
@@ -423,13 +490,20 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||||||
{% if ui_texts.ui_footer %}
|
{% if ui_texts.ui_footer %}
|
||||||
<hr><span class="rot-enc">{{ ui_texts.ui_footer|rot13|raw }}</span>
|
<hr><span class="rot-enc">{{ ui_texts.ui_footer|rot13|raw }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if mailcow_cc_username and mailcow_info.version_tag|default %}
|
{% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "master" and mailcow_info.version_tag|default %}
|
||||||
<span class="version">
|
<span class="version">
|
||||||
🐮 + 🐋 = 💕
|
🐮 + 🐋 = 💕
|
||||||
<a href="{{ mailcow_info.git_project_url }}/releases/tag/{{ mailcow_info.version_tag }}" target="_blank">
|
Version: <a href="{{ mailcow_info.git_project_url }}/releases/tag/{{ mailcow_info.version_tag }}" target="_blank">{{ mailcow_info.version_tag }}
|
||||||
Version: {{ mailcow_info.version_tag }}
|
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
{% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "nightly" and mailcow_info.version_tag|default %}
|
||||||
|
<span class="version">
|
||||||
|
🛠️🐮 + 🐋 = 💕
|
||||||
|
Nightly: <a href="{{ mailcow_info.git_project_url }}/commit/{{ mailcow_info.git_commit }}" target="_blank">{{ mailcow_info.version_tag }}
|
||||||
|
</a><br>
|
||||||
|
<span style="text-align:right;display:block;">Build: {{ mailcow_info.git_commit_date }}</span>
|
||||||
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<div class="col-sm-9 col-xs-7">
|
<div class="col-sm-9 col-xs-7">
|
||||||
<select id="selectTFA" class="selectpicker" title="{{ lang.tfa.select }}">
|
<select id="selectTFA" class="selectpicker" title="{{ lang.tfa.select }}">
|
||||||
<option value="yubi_otp">{{ lang.tfa.yubi_otp }}</option>
|
<option value="yubi_otp">{{ lang.tfa.yubi_otp }}</option>
|
||||||
<option value="u2f">{{ lang.tfa.u2f }}</option>
|
<option value="webauthn">{{ lang.tfa.webauthn }}</option>
|
||||||
<option value="totp">{{ lang.tfa.totp }}</option>
|
<option value="totp">{{ lang.tfa.totp }}</option>
|
||||||
<option value="none">{{ lang.tfa.none }}</option>
|
<option value="none">{{ lang.tfa.none }}</option>
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -133,73 +133,163 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if pending_tfa_method %}
|
{% if pending_tfa_methods %}
|
||||||
<div class="modal fade" id="ConfirmTFAModal" tabindex="-1" role="dialog" aria-labelledby="ConfirmTFAModalLabel">
|
<div class="modal fade" id="ConfirmTFAModal" tabindex="-1" role="dialog" aria-labelledby="ConfirmTFAModalLabel">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||||||
<h3 class="modal-title">{{ lang.tfa[pending_tfa_method] }}</h3>
|
<h3 class="modal-title">{{ lang.tfa.tfa }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
|
||||||
{% if pending_tfa_method == 'yubi_otp' %}
|
<ul class="nav nav-tabs" id="tabContent">
|
||||||
<form role="form" method="post">
|
{% if pending_tfa_authmechs["webauthn"] is defined and pending_tfa_authmechs["u2f"] is not defined %}
|
||||||
<div class="form-group">
|
<li class="active"><a href="#tfa_tab_webauthn" data-toggle="tab" id="pending_tfa_tab_webauthn"><i class="bi bi-fingerprint"></i> WebAuthn</a></li>
|
||||||
<div class="input-group">
|
{% endif %}
|
||||||
<span class="input-group-addon" id="yubi-addon"><img alt="Yubicon Icon" src="/img/yubi.ico"></span>
|
|
||||||
<input type="text" name="token" class="form-control" autocomplete="off" placeholder="Touch Yubikey" aria-describedby="yubi-addon">
|
|
||||||
<input type="hidden" name="tfa_method" value="yubi_otp">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-sm visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline btn-sm btn-default" type="submit" name="verify_tfa_login">{{ lang.login.login }}</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% if pending_tfa_method == 'totp' %}
|
|
||||||
<form role="form" method="post">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="input-group">
|
|
||||||
<span class="input-group-addon" id="tfa-addon"><i class="bi bi-shield-lock-fill"></i></span>
|
|
||||||
<input type="number" min="000000" max="999999" name="token" class="form-control" placeholder="123456" autocomplete="one-time-code" aria-describedby="tfa-addon">
|
|
||||||
<input type="hidden" name="tfa_method" value="totp">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-sm visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline btn-default" type="submit" name="verify_tfa_login">{{ lang.login.login }}</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% if pending_tfa_method == 'hotp' %}
|
|
||||||
<div class="empty"></div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if pending_tfa_method == 'webauthn' %}
|
{% if pending_tfa_authmechs["yubi_otp"] is defined and pending_tfa_authmechs["u2f"] is not defined %}
|
||||||
<form role="form" method="post" id="webauthn_auth_form">
|
<li class="tab-pane {% if pending_tfa_authmechs["yubi_otp"] %}active{% endif %}">
|
||||||
<center>
|
<a href="#tfa_tab_yubi_otp" data-toggle="tab" id="pending_tfa_tab_yubi_otp"><i class="bi bi-usb-drive"></i> Yubi OTP</a>
|
||||||
<div style="cursor:pointer" id="start_webauthn_confirmation">
|
</li>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24">
|
{% endif %}
|
||||||
<path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5 9.72c-.1 0-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07c-.13 0-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96 0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"></path>
|
|
||||||
</svg>
|
{% if pending_tfa_authmechs["totp"] is defined and pending_tfa_authmechs["u2f"] is not defined %}
|
||||||
<p>{{ lang.tfa.start_webauthn_validation }}</p>
|
<li class="tab-pane {% if pending_tfa_authmechs["totp"] %}active{% endif %}">
|
||||||
<hr>
|
<a href="#tfa_tab_totp" data-toggle="tab" id="pending_tfa_tab_totp"><i class="bi bi-clock-history"></i> Time based OTP</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- <li><a href="#tfa_tab_hotp" data-toggle="tab">HOTP</a></li> -->
|
||||||
|
{% if pending_tfa_authmechs["u2f"] is defined %}
|
||||||
|
<li class="active"><a href="#tfa_tab_u2f" data-toggle="tab"><i class="bi bi-x-octagon"></i> U2F</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tab-content">
|
||||||
|
{% if pending_tfa_authmechs["webauthn"] is defined and pending_tfa_authmechs["u2f"] is not defined %}
|
||||||
|
<div role="tabpanel" class="tab-pane active" id="tfa_tab_webauthn">
|
||||||
|
<div class="panel panel-default" style="margin-bottom: 0px;">
|
||||||
|
<div class="panel-body">
|
||||||
|
<form role="form" method="post" id="webauthn_auth_form">
|
||||||
|
<legend>
|
||||||
|
<i class="bi bi-shield-fill-check"></i>
|
||||||
|
Authenticators
|
||||||
|
</legend>
|
||||||
|
<div class="list-group">
|
||||||
|
{% for authenticator in pending_tfa_methods %}
|
||||||
|
{% if authenticator["authmech"] == "webauthn" %}
|
||||||
|
<a href="#" class="list-group-item webauthn-authenticator-selection">
|
||||||
|
<i class="bi bi-key-fill" style="margin-right: 5px"></i>
|
||||||
|
<span>{{ authenticator["key_id"] }}</span>
|
||||||
|
<input type="hidden" value="{{ authenticator["id"] }}" /><br/>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="collapse pending-tfa-collapse" id="collapseWebAuthnTFA">
|
||||||
|
<p id="webauthn_status_auth"><p><i class="bi bi-arrow-repeat icon-spin"></i> {{ lang.tfa.init_webauthn }}</p></p>
|
||||||
|
<div class="alert alert-danger" style="display:none" id="webauthn_return_code"></div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="token" id="webauthn_auth_data"/>
|
||||||
|
<input type="hidden" name="tfa_method" value="webauthn">
|
||||||
|
<input type="hidden" name="verify_tfa_login"/><br/>
|
||||||
|
<input type="hidden" name="id" id="webauthn_selected_id" /><br/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</center>
|
{% endif %}
|
||||||
<p id="webauthn_status_auth"></p>
|
{% if pending_tfa_authmechs["yubi_otp"] is defined and pending_tfa_authmechs["u2f"] is not defined %}
|
||||||
<div class="alert alert-danger" style="display:none" id="webauthn_return_code"></div>
|
<div role="tabpanel" class="tab-pane {% if pending_tfa_authmechs["yubi_otp"] %}active{% endif %}" id="tfa_tab_yubi_otp">
|
||||||
<input type="hidden" name="token" id="webauthn_auth_data"/>
|
<div class="panel panel-default" style="margin-bottom: 0px;">
|
||||||
<input type="hidden" name="tfa_method" value="webauthn">
|
<div class="panel-body">
|
||||||
<input type="hidden" name="verify_tfa_login"/><br/>
|
<form role="form" method="post">
|
||||||
</form>
|
<legend>
|
||||||
{% endif %}
|
<i class="bi bi-shield-fill-check"></i>
|
||||||
{# leave this here to inform users that u2f is deprecated #}
|
Authenticate
|
||||||
{% if pending_tfa_method == 'u2f' %}
|
</legend>
|
||||||
<form role="form" method="post" id="u2f_auth_form">
|
<div class="collapse in pending-tfa-collapse" id="collapseYubiTFA">
|
||||||
<p>{{ lang.tfa.u2f_deprecated }}</p>
|
<div class="form-group">
|
||||||
<p><b>{{ lang.tfa.u2f_deprecated_important }}</b></p>
|
<div class="input-group">
|
||||||
<input type="hidden" name="token" value="destroy" />
|
<span class="input-group-addon" id="yubi-addon"><img alt="Yubicon Icon" src="/img/yubi.ico"></span>
|
||||||
<input type="hidden" name="tfa_method" value="u2f">
|
<input type="text" name="token" class="form-control" autocomplete="off" placeholder="Touch Yubikey" aria-describedby="yubi-addon">
|
||||||
<input type="hidden" name="verify_tfa_login"/><br/>
|
<input type="hidden" name="tfa_method" value="yubi_otp">
|
||||||
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button>
|
<input type="hidden" name="id" id="yubi_selected_id" />
|
||||||
</form>
|
</div>
|
||||||
{% endif %}
|
</div>
|
||||||
</div>
|
<button class="btn btn-sm visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline btn-sm btn-default" type="submit" name="verify_tfa_login">{{ lang.login.login }}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if pending_tfa_authmechs["totp"] is defined and pending_tfa_authmechs["u2f"] is not defined %}
|
||||||
|
<div role="tabpanel" class="tab-pane {% if pending_tfa_authmechs["totp"] %}active{% endif %}" id="tfa_tab_totp">
|
||||||
|
<div class="panel panel-default" style="margin-bottom: 0px;">
|
||||||
|
<div class="panel-body">
|
||||||
|
<form role="form" method="post">
|
||||||
|
<legend>
|
||||||
|
<i class="bi bi-shield-fill-check"></i>
|
||||||
|
Authenticators
|
||||||
|
</legend>
|
||||||
|
<div class="list-group">
|
||||||
|
{% for authenticator in pending_tfa_methods %}
|
||||||
|
{% if authenticator["authmech"] == "totp" %}
|
||||||
|
<a href="#" class="list-group-item totp-authenticator-selection">
|
||||||
|
<i class="bi bi-key-fill" style="margin-right: 5px"></i>
|
||||||
|
<span>{{ authenticator["key_id"] }}</span>
|
||||||
|
<input type="hidden" value="{{ authenticator["id"] }}" />
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="collapse pending-tfa-collapse" id="collapseTotpTFA">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon" id="tfa-addon"><i class="bi bi-shield-lock-fill"></i></span>
|
||||||
|
<input type="number" min="000000" max="999999" name="token" class="form-control" placeholder="123456" autocomplete="one-time-code" aria-describedby="tfa-addon">
|
||||||
|
<input type="hidden" name="tfa_method" value="totp">
|
||||||
|
<input type="hidden" name="id" id="totp_selected_id" /><br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline btn-default" type="submit" name="verify_tfa_login">{{ lang.login.login }}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<!--
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tfa_tab_hotp">
|
||||||
|
<div class="panel panel-default" style="margin-bottom: 0px;">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="empty"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
{% if pending_tfa_authmechs["u2f"] is defined %}
|
||||||
|
<div role="tabpanel" class="tab-pane active" id="tfa_tab_u2f">
|
||||||
|
<div class="panel panel-default" style="margin-bottom: 0px;">
|
||||||
|
<div class="panel-body">
|
||||||
|
{# leave this here to inform users that u2f is deprecated #}
|
||||||
|
<form role="form" method="post" id="u2f_auth_form">
|
||||||
|
<div>
|
||||||
|
<p>{{ lang.tfa.u2f_deprecated }}</p>
|
||||||
|
<p><b>{{ lang.tfa.u2f_deprecated_important }}</b></p>
|
||||||
|
<input type="hidden" name="token" value="destroy" />
|
||||||
|
<input type="hidden" name="tfa_method" value="u2f">
|
||||||
|
<input type="hidden" name="verify_tfa_login"/><br/>
|
||||||
|
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -435,11 +435,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p class="help-block">{{ lang.add.syncjob_hint }}</p>
|
<p class="help-block">{{ lang.add.syncjob_hint }}</p>
|
||||||
<form class="form-horizontal" data-cached-form="true" role="form" data-id="add_syncjob">
|
<form class="form-horizontal" data-cached-form="false" role="form" data-id="add_syncjob">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="username">{{ lang.add.username }}</label>
|
<label class="control-label col-sm-2" for="username">{{ lang.add.username }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select data-live-search="true" name="username" required>
|
<select data-live-search="true" name="username" title="{{ lang.add.select }}" required>
|
||||||
{% for mailbox in mailboxes %}
|
{% for mailbox in mailboxes %}
|
||||||
<option>{{ mailbox }}</option>
|
<option>{{ mailbox }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -2,11 +2,14 @@
|
|||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">{{ lang.user.mailbox_general }}</div>
|
<div class="panel-heading">{{ lang.user.mailbox_general }}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
{% if mailboxdata.attributes.force_pw_update == '1' %}
|
||||||
|
<div class="alert alert-danger">{{ lang.user.force_pw_update|raw }}</div>
|
||||||
|
{% endif %}
|
||||||
{% if not skip_sogo %}
|
{% if not skip_sogo %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="hidden-xs col-md-3 col-xs-5 text-right"></div>
|
<div class="hidden-xs col-md-3 col-xs-5 text-right"></div>
|
||||||
<div class="col-md-3 col-xs-12">
|
<div class="col-md-3 col-xs-12">
|
||||||
{% if dual_login and allow_admin_email_login == 'n' %}
|
{% if dual_login and allow_admin_email_login == 'n' or mailboxdata.attributes.force_pw_update == '1' %}
|
||||||
<button disabled class="btn btn-default btn-block btn-xs-lg">
|
<button disabled class="btn btn-default btn-block btn-xs-lg">
|
||||||
<i class="bi bi-inbox-fill"></i> {{ lang.user.open_webmail_sso }}
|
<i class="bi bi-inbox-fill"></i> {{ lang.user.open_webmail_sso }}
|
||||||
</button>
|
</button>
|
||||||
@@ -45,6 +48,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
{# TFA #}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 col-xs-5 text-right">{{ lang.tfa.tfa }}:</div>
|
||||||
|
<div class="col-sm-9 col-xs-7">
|
||||||
|
<p id="tfa_pretty">{{ tfa_data.pretty }}</p>
|
||||||
|
{% include 'tfa_keys.twig' %}
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 col-xs-5 text-right">{{ lang.tfa.set_tfa }}:</div>
|
||||||
|
<div class="col-sm-9 col-xs-7">
|
||||||
|
<select data-style="btn btn-sm dropdown-toggle bs-placeholder btn-default" data-width="fit" id="selectTFA" class="selectpicker" title="{{ lang.tfa.select }}">
|
||||||
|
<option value="yubi_otp">{{ lang.tfa.yubi_otp }}</option>
|
||||||
|
<option value="webauthn">{{ lang.tfa.webauthn }}</option>
|
||||||
|
<option value="totp">{{ lang.tfa.totp }}</option>
|
||||||
|
<option value="none">{{ lang.tfa.none }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
{# FIDO2 #}
|
{# FIDO2 #}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 col-xs-12 text-right text-xs-left">
|
<div class="col-sm-3 col-xs-12 text-right text-xs-left">
|
||||||
@@ -115,9 +139,6 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-offset-3 col-sm-9">
|
<div class="col-sm-offset-3 col-sm-9">
|
||||||
{% if mailboxdata.attributes.force_pw_update == '1' %}
|
|
||||||
<div class="alert alert-danger">{{ lang.user.force_pw_update|raw }}</div>
|
|
||||||
{% endif %}
|
|
||||||
<p><a target="_blank" href="https://mailcow.github.io/mailcow-dockerized-docs/client/client/#{{ clientconfigstr }}">[{{ lang.user.client_configuration }}]</a></p>
|
<p><a target="_blank" href="https://mailcow.github.io/mailcow-dockerized-docs/client/client/#{{ clientconfigstr }}">[{{ lang.user.client_configuration }}]</a></p>
|
||||||
<p><a href="#userFilterModal" data-toggle="modal">[{{ lang.user.show_sieve_filters }}]</a></p>
|
<p><a href="#userFilterModal" data-toggle="modal">[{{ lang.user.show_sieve_filters }}]</a></p>
|
||||||
<hr>
|
<hr>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user