Compare commits

..

No commits in common. "master" and "v1.0.0" have entirely different histories.

35 changed files with 380 additions and 5138 deletions

29
.drone.yml Normal file
View file

@ -0,0 +1,29 @@
kind: pipeline
name: check-app-compatbility
steps:
- name: check-app-compatbility
image: nextcloudci/php7.2:php7.2-9
environment:
APP_NAME: user_external
CORE_BRANCH: master
DB: sqlite
commands:
# Pre-setup steps
- wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh
- bash ./before_install.sh $APP_NAME $CORE_BRANCH $DB
- cd ../server
# Code checker
- ./occ app:check-code $APP_NAME -c strong-comparison
- ./occ app:check-code $APP_NAME -c deprecation
matrix:
trigger:
branch:
- master
- stable*
event:
- pull_request
- push

View file

@ -1,175 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
#
# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
name: Build and publish app release
on:
release:
types: [published]
jobs:
build_and_publish:
runs-on: ubuntu-latest
# Only allowed to be run on nextcloud-releases repositories
if: ${{ github.repository_owner == 'nextcloud-releases' }}
steps:
- name: Check actor permission
uses: skjnldsv/check-actor-permission@69e92a3c4711150929bca9fcf34448c5bf5526e7 # v3.0
with:
require: write
- name: Set app env
run: |
# Split and keep last
echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
echo "APP_VERSION=${GITHUB_REF##*/}" >> $GITHUB_ENV
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
path: ${{ env.APP_NAME }}
- name: Get appinfo data
id: appinfo
uses: skjnldsv/xpath-action@7e6a7c379d0e9abc8acaef43df403ab4fc4f770c # master
with:
filename: ${{ env.APP_NAME }}/appinfo/info.xml
expression: "//info//dependencies//nextcloud/@min-version"
- name: Read package.json node and npm engines version
uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3
id: versions
# Continue if no package.json
continue-on-error: true
with:
path: ${{ env.APP_NAME }}
fallbackNode: '^20'
fallbackNpm: '^10'
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
# Skip if no package.json
if: ${{ steps.versions.outputs.nodeVersion }}
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v3
with:
node-version: ${{ steps.versions.outputs.nodeVersion }}
- name: Set up npm ${{ steps.versions.outputs.npmVersion }}
# Skip if no package.json
if: ${{ steps.versions.outputs.npmVersion }}
run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}'
- name: Get php version
id: php-versions
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1
with:
filename: ${{ env.APP_NAME }}/appinfo/info.xml
- name: Set up php ${{ steps.php-versions.outputs.php-min }}
uses: shivammathur/setup-php@2e947f1f6932d141d076ca441d0e1e881775e95b # v2.31.0
with:
php-version: ${{ steps.php-versions.outputs.php-min }}
coverage: none
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check composer.json
id: check_composer
uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0
with:
files: "${{ env.APP_NAME }}/composer.json"
- name: Install composer dependencies
if: steps.check_composer.outputs.files_exists == 'true'
run: |
cd ${{ env.APP_NAME }}
composer install --no-dev
- name: Build ${{ env.APP_NAME }}
# Skip if no package.json
if: ${{ steps.versions.outputs.nodeVersion }}
env:
NODE_ENV: production
run: |
cd ${{ env.APP_NAME }}
npm ci
npm run build --if-present
- name: Check Krankerl config
id: krankerl
uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0
with:
files: ${{ env.APP_NAME }}/krankerl.toml
- name: Install Krankerl
if: steps.krankerl.outputs.files_exists == 'true'
run: |
wget https://github.com/ChristophWurst/krankerl/releases/download/v0.14.0/krankerl_0.14.0_amd64.deb
sudo dpkg -i krankerl_0.14.0_amd64.deb
- name: Package ${{ env.APP_NAME }} ${{ env.APP_VERSION }} with krankerl
if: steps.krankerl.outputs.files_exists == 'true'
run: |
cd ${{ env.APP_NAME }}
krankerl package
- name: Package ${{ env.APP_NAME }} ${{ env.APP_VERSION }} with makefile
if: steps.krankerl.outputs.files_exists != 'true'
run: |
cd ${{ env.APP_NAME }}
make appstore
- name: Checkout server ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }}
continue-on-error: true
id: server-checkout
run: |
NCVERSION='${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }}'
wget --quiet https://download.nextcloud.com/server/releases/latest-$NCVERSION.zip
unzip latest-$NCVERSION.zip
- name: Checkout server master fallback
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
if: ${{ steps.server-checkout.outcome != 'success' }}
with:
submodules: true
repository: nextcloud/server
path: nextcloud
- name: Sign app
run: |
# Extracting release
cd ${{ env.APP_NAME }}/build/artifacts
tar -xvf ${{ env.APP_NAME }}.tar.gz
cd ../../../
# Setting up keys
echo '${{ secrets.APP_PRIVATE_KEY }}' > ${{ env.APP_NAME }}.key
wget --quiet "https://github.com/nextcloud/app-certificate-requests/raw/master/${{ env.APP_NAME }}/${{ env.APP_NAME }}.crt"
# Signing
php nextcloud/occ integrity:sign-app --privateKey=../${{ env.APP_NAME }}.key --certificate=../${{ env.APP_NAME }}.crt --path=../${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }}
# Rebuilding archive
cd ${{ env.APP_NAME }}/build/artifacts
tar -zcvf ${{ env.APP_NAME }}.tar.gz ${{ env.APP_NAME }}
- name: Attach tarball to github release
uses: svenstaro/upload-release-action@04733e069f2d7f7f0b4aebc4fbdbce8613b03ccd # v2
id: attach_to_release
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }}.tar.gz
asset_name: ${{ env.APP_NAME }}-${{ env.APP_VERSION }}.tar.gz
tag: ${{ github.ref }}
overwrite: true
- name: Upload app to Nextcloud appstore
uses: nextcloud-releases/nextcloud-appstore-push-action@a011fe619bcf6e77ddebc96f9908e1af4071b9c1 # v1
with:
app_name: ${{ env.APP_NAME }}
appstore_token: ${{ secrets.APPSTORE_TOKEN }}
download_url: ${{ steps.attach_to_release.outputs.browser_download_url }}
app_private_key: ${{ secrets.APP_PRIVATE_KEY }}

View file

@ -1,49 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
#
# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
name: Dependabot
on:
pull_request_target:
branches:
- main
- master
- stable*
permissions:
contents: read
concurrency:
group: dependabot-approve-merge-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
auto-approve-merge:
if: github.actor == 'dependabot[bot]' || github.actor == 'renovate[bot]'
runs-on: ubuntu-latest-low
permissions:
# for hmarr/auto-approve-action to approve PRs
pull-requests: write
steps:
- name: Disabled on forks
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
run: |
echo 'Can not approve PRs from forks'
exit 1
# GitHub actions bot approve
- uses: hmarr/auto-approve-action@b40d6c9ed2fa10c9a2749eca7eb004418a705501 # v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
# Nextcloud bot approve and merge request
- uses: ahmadnassri/action-dependabot-auto-merge@45fc124d949b19b6b8bf6645b6c9d55f4f9ac61a # v2
with:
target: minor
github-token: ${{ secrets.DEPENDABOT_AUTOMERGE_TOKEN }}

View file

@ -1,36 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
#
# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
name: Block fixup and squash commits
on:
pull_request:
types: [opened, ready_for_review, reopened, synchronize]
permissions:
contents: read
concurrency:
group: fixup-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
commit-message-check:
if: github.event.pull_request.draft == false
permissions:
pull-requests: write
name: Block fixup and squash commits
runs-on: ubuntu-latest-low
steps:
- name: Run check
uses: skjnldsv/block-fixup-merge-action@c138ea99e45e186567b64cf065ce90f7158c236a # v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -1,36 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
#
# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
name: Lint info.xml
on: pull_request
permissions:
contents: read
concurrency:
group: lint-info-xml-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
xml-linters:
runs-on: ubuntu-latest-low
name: info.xml lint
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Download schema
run: wget https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/api/v1/release/info.xsd
- name: Lint info.xml
uses: ChristophWurst/xmllint-action@36f2a302f84f8c83fceea0b9c59e1eb4a616d3c1 # v1.2
with:
xml-file: ./appinfo/info.xml
xml-schema-file: ./info.xsd

View file

@ -1,48 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
#
# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
name: Lint php-cs
on: pull_request
permissions:
contents: read
concurrency:
group: lint-php-cs-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
name: php-cs
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Get php version
id: versions
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1
- name: Set up php${{ steps.versions.outputs.php-available }}
uses: shivammathur/setup-php@2e947f1f6932d141d076ca441d0e1e881775e95b # v2.31.0
with:
php-version: ${{ steps.versions.outputs.php-available }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
coverage: none
ini-file: development
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies
run: composer i
- name: Lint
run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 )

View file

@ -1,70 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
#
# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
name: Lint php
on: pull_request
permissions:
contents: read
concurrency:
group: lint-php-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
matrix:
runs-on: ubuntu-latest-low
outputs:
php-versions: ${{ steps.versions.outputs.php-versions }}
steps:
- name: Checkout app
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Get version matrix
id: versions
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.0.0
php-lint:
runs-on: ubuntu-latest
needs: matrix
strategy:
matrix:
php-versions: ${{fromJson(needs.matrix.outputs.php-versions)}}
name: php-lint
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@2e947f1f6932d141d076ca441d0e1e881775e95b # v2.31.0
with:
php-version: ${{ matrix.php-versions }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
coverage: none
ini-file: development
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Lint
run: composer run lint
summary:
permissions:
contents: none
runs-on: ubuntu-latest-low
needs: php-lint
if: always()
name: php-lint-summary
steps:
- name: Summary status
run: if ${{ needs.php-lint.result != 'success' && needs.php-lint.result != 'skipped' }}; then exit 1; fi

2
.gitignore vendored
View file

@ -6,5 +6,3 @@ tests/clover.xml
# packaged app
build/artifacts
vendor
.php-cs-fixer.cache

View file

@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
require_once './vendor/autoload.php';
use Nextcloud\CodingStandard\Config;
$config = new Config();
$config
->getFinder()
->notPath('build')
->notPath('l10n')
->notPath('node_modules')
->notPath('src')
->notPath('vendor')
->in(__DIR__);
return $config;

56
.travis.yml Normal file
View file

@ -0,0 +1,56 @@
language: php
php:
- 7.1
- 7.2
- 7.3
services:
- mysql
- postgresql
env:
global:
- APP_NAME=user_external
- PHP_COVERAGE=FALSE
matrix:
# - DB=sqlite SERVER=nextcloud/travis_ci/master SERVER_BRANCH=master
- DB=sqlite SERVER=nextcloud/travis_ci/master SERVER_BRANCH=stable15
# - DB=mysql SERVER=nextcloud/travis_ci/master SERVER_BRANCH=master PHP_COVERAGE=TRUE
- DB=mysql SERVER=nextcloud/travis_ci/master SERVER_BRANCH=stable15 PHP_COVERAGE=TRUE
# - DB=pgsql SERVER=nextcloud/travis_ci/master SERVER_BRANCH=master
- DB=pgsql SERVER=nextcloud/travis_ci/master SERVER_BRANCH=stable15
matrix:
fast_finish: true
branches:
only:
- master
- /^stable\d+(\.\d+)?$/
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y libxml2-utils
- wget https://raw.githubusercontent.com/$SERVER/before_install.sh
- . ./before_install.sh "$APP_NAME" "$SERVER_BRANCH" "$DB"
- cd ../core || cd ../server
- php occ app:enable $APP_NAME
before_script:
# Test lint
- cd apps/$APP_NAME
- find . -name \*.php -exec php -l "{}" \;
script:
# Check info.xml schema validity
- wget https://apps.nextcloud.com/schema/apps/info.xsd
- xmllint appinfo/info.xml --schema info.xsd --noout
- rm info.xsd
# Run phpunit tests
# - cd tests
# - phpunit --configuration configuration.xml
# Create coverage report
# - sh -c "if [ '$PHP_COVERAGE' != 'FALSE' ]; then wget https://scrutinizer-ci.com/ocular.phar; fi"
# - sh -c "if [ '$PHP_COVERAGE' != 'FALSE' ]; then php ocular.phar code-coverage:upload --format=php-clover clover.xml; fi"

View file

@ -1,45 +0,0 @@
# Changelog
Starting from v3.0.0, all notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.4.0] -
- Fix out-of-bound array access in IMAP backend
[#229](https://github.com/nextcloud/user_external/pull/229) @BjoKaSH
- Distinguish wrong credentials from other problems in logging output for IMAP backend
[#228](https://github.com/nextcloud/user_external/pull/228) @BjoKaSH
- 🐛 FIX: wrong user count
[#249](https://github.com/nextcloud/user_external/pull/249)
- Make compatible with Nextcloud 29
[#256](https://github.com/nextcloud/user_external/pull/256)
## [3.3.0] - 2024-03-30
- Fix wrong capitalisation of `WebDavAuth` class name in readme
[#238](https://github.com/nextcloud/user_external/pull/238) @pierrecorsini
- Mark compatible with Nextcloud 28
[#241](https://github.com/nextcloud/user_external/pull/241) @Glandos
## [3.2.0] - 2023-06-13
- Fix IMAP authentication on empty mailboxes
[#164](https://github.com/nextcloud/user_external/pull/164) @tem-hth
- Trim doesn't accept null anymore
[#217](https://github.com/nextcloud/user_external/pull/217) @Glandos
## [3.1.0] - 2022-12-27
- Support for Nextcloud 25
[#212](https://github.com/nextcloud/user_external/pull/212) @michael-dev
## [3.0.0] - 2022-04-26
### Breaking Changes
- Namespace change: ⚠This requires configuration changes to be applied to your config.php.⚠\
Specifically the `class` attribute needs to be changed to the full class path starting with `\OCA\UserExternal\` and ending with the name of the specific authentication backend you use (e.g. IMAP or FTP). Check the [README.md](https://github.com/nextcloud/user_external#readme) for the concrete value you have to set.
### Added
- Support for Nextcloud 23 and 24
[#191](https://github.com/nextcloud/user_external/pull/191) @MarBie77
- New CI config (migrate to Github Workflows)
[#192](https://github.com/nextcloud/user_external/pull/192) @skjnldsv
## Older releases
For versions before 3.0.0 please check the [github releases](https://github.com/nextcloud/user_external/releases) for release notes.

View file

@ -60,9 +60,9 @@ appstore:
--certificate=$(cert_dir)/$(app_name).crt\
--path=$(sign_dir)/$(app_name); \
fi
tar -czf $(build_dir)/$(app_name).tar.gz \
tar -czf $(build_dir)/$(app_name)-$(version).tar.gz \
-C $(sign_dir) $(app_name)
@if [ -f $(cert_dir)/$(app_name).key ]; then \
echo "Signing package…"; \
openssl dgst -sha512 -sign $(cert_dir)/$(app_name).key $(build_dir)/$(app_name).tar.gz | openssl base64; \
openssl dgst -sha512 -sign $(cert_dir)/$(app_name).key $(build_dir)/$(app_name)-$(version).tar.gz | openssl base64; \
fi

View file

@ -1,8 +1,5 @@
External user authentication
============================
**⚠⚠ Warning:** As of Version 3.0 this app uses namespace \OCA\UserExternal now. You MUST change your config to adopt to this change. See examples below. ⚠⚠
**Authenticate user login against IMAP, SMB, FTP, WebDAV, HTTP BasicAuth, SSH and XMPP**
Passwords are not stored locally; authentication always happens against
@ -18,8 +15,6 @@ If something does not work, check the log file at `nextcloud/data/nextcloud.log`
**⚠⚠ Warning:** If you are using more than one backend or especially one backend more often than once, make sure that you still have resp. get unique `uid`s in the database. ⚠⚠
**⚠⚠ Warning:** If you are using tools like fail2ban (https://www.fail2ban.org) to protect your authentication source (e.g. IMAP server), be sure to disable it for the host that runs `user_external`. Otherwise a single user failing to login too many times can practically DoS your whole Nextcloud installation because `fail2ban` will then block the Nextcloud IP address. ⚠⚠
Instead, make sure the bruteforce protection app for Nextcloud is installed and configured correctly.
FTP
---
@ -35,7 +30,7 @@ Add the following to `config.php`:
'user_backends' => array(
array(
'class' => '\OCA\UserExternal\FTP',
'class' => 'OC_User_FTP',
'arguments' => array('127.0.0.1'),
),
),
@ -44,7 +39,7 @@ To enable SSL connections via `ftps`, append a second parameter `true`:
'user_backends' => array(
array(
'class' => '\OCA\UserExternal\FTP',
'class' => 'OC_User_FTP',
'arguments' => array('127.0.0.1', true),
),
),
@ -73,7 +68,7 @@ Add the following to your `config.php`:
'user_backends' => array(
array(
'class' => '\OCA\UserExternal\IMAP',
'class' => 'OC_User_IMAP',
'arguments' => array(
'127.0.0.1', 993, 'ssl', 'example.com', true, false
),
@ -109,7 +104,7 @@ Add the following to your `config.php`:
'user_backends' => array(
array(
'class' => '\OCA\UserExternal\SMB',
'class' => 'OC_User_SMB',
'arguments' => array('127.0.0.1'),
),
),
@ -133,7 +128,7 @@ Add the following to your `config.php`:
'user_backends' => array(
array(
'class' => '\OCA\UserExternal\WebDavAuth',
'class' => '\OCA\User_External\WebDAVAuth',
'arguments' => array('https://example.com/webdav'),
),
),
@ -156,7 +151,7 @@ Add the following to your `config.php`:
'user_backends' => array(
array(
'class' => '\OCA\UserExternal\BasicAuth',
'class' => 'OC_User_BasicAuth',
'arguments' => array('https://example.com/basic_auth'),
),
),
@ -177,7 +172,7 @@ Add the following to your `config.php`:
'user_backends' => array(
array(
'class' => '\OCA\UserExternal\SSH',
'class' => 'OC_User_SSH',
'arguments' => array('127.0.0.1', '22'),
),
),
@ -198,7 +193,7 @@ Add the following to your `config.php`:
'user_backends' => array (
0 => array (
'class' => '\OCA\UserExternal\XMPP',
'class' => 'OC_User_XMPP',
'arguments' => array (
0 => 'dbhost',
1 => 'prosodydb',

7
appinfo/app.php Normal file
View file

@ -0,0 +1,7 @@
<?php
OC::$CLASSPATH['OC_User_IMAP']='user_external/lib/imap.php';
OC::$CLASSPATH['OC_User_SMB']='user_external/lib/smb.php';
OC::$CLASSPATH['OC_User_FTP']='user_external/lib/ftp.php';
OC::$CLASSPATH['OC_User_BasicAuth']='user_external/lib/basicauth.php';
OC::$CLASSPATH['OC_User_SSH']='user_external/lib/ssh.php';
OC::$CLASSPATH['OC_User_XMPP']='user_external/lib/xmpp.php';

View file

@ -16,10 +16,9 @@
Read the [documentation](https://github.com/nextcloud/user_external#readme) to learn how to configure it!
]]></description>
<version>3.5.0</version>
<version>1.0.0</version>
<licence>agpl</licence>
<author>Robin Appelman</author>
<namespace>UserExternal</namespace>
<types>
<prelogin/>
<authentication/>
@ -33,6 +32,6 @@ Read the [documentation](https://github.com/nextcloud/user_external#readme) to l
<bugs>https://github.com/nextcloud/user_external/issues</bugs>
<repository type="git">https://github.com/nextcloud/user_external.git</repository>
<dependencies>
<nextcloud min-version="25" max-version="30" />
<nextcloud min-version="17" max-version="20" />
</dependencies>
</info>

View file

@ -1,20 +0,0 @@
{
"name": "nextcloud/user_external",
"config": {
"optimize-autoloader": true,
"classmap-authoritative": true,
"platform": {
"php": "7.3"
}
},
"scripts": {
"cs:fix": "php-cs-fixer fix",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l"
},
"require-dev": {
"nextcloud/coding-standard": "^1.0.0",
"phpunit/phpunit": "^9.5",
"christophwurst/nextcloud_testing": "^0.12.4"
}
}

4280
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
namespace OCA\UserExternal\AppInfo;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
class Application extends App implements IBootstrap {
public function __construct() {
parent::__construct('user_external');
}
public function register(IRegistrationContext $context): void {
}
public function boot(IBootContext $context): void {
}
}

View file

@ -23,11 +23,11 @@ declare(strict_types=1);
*
*/
namespace OCA\UserExternal\Migration;
namespace OCA\User_external\Migration;
use Closure;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
@ -44,17 +44,17 @@ class Version0010Date20200630193751 extends SimpleMigrationStep {
if (!$schema->hasTable('users_external')) {
$table = $schema->createTable('users_external');
$table->addColumn('backend', Types::STRING, [
$table->addColumn('backend', Type::STRING, [
'notnull' => true,
'length' => 128,
'default' => '',
]);
$table->addColumn('uid', Types::STRING, [
$table->addColumn('uid', Type::STRING, [
'notnull' => true,
'length' => 64,
'default' => '',
]);
$table->addColumn('displayname', Types::STRING, [
$table->addColumn('displayname', Type::STRING, [
'notnull' => false,
'length' => 64,
]);

View file

@ -1,129 +0,0 @@
<?php
/**
* Copyright (c) 2019 Sebastian Sterk <sebastian@wiuwiu.de>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace OCA\UserExternal;
/**
* User authentication against a XMPP Prosody MySQL database
*
* @category Apps
* @package UserExternal
* @author Sebastian Sterk https://wiuwiu.de/Imprint
* @license http://www.gnu.org/licenses/agpl AGPL
*/
class XMPP extends Base {
private $host;
private $xmppDb;
private $xmppDbUser;
private $xmppDbPassword;
private $xmppDomain;
private $passwordHashed;
public function __construct($host, $xmppDb, $xmppDbUser, $xmppDbPassword, $xmppDomain, $passwordHashed = true) {
parent::__construct($host);
$this->host = $host;
$this->xmppDb = $xmppDb;
$this->xmppDbUser = $xmppDbUser;
$this->xmppDbPassword = $xmppDbPassword;
$this->xmppDomain = $xmppDomain;
$this->passwordHashed = $passwordHashed;
}
public function hmacSha1($key, $data) {
if (strlen($key) > 64) {
$key = str_pad(sha1($key, true), 64, chr(0));
}
if (strlen($key) < 64) {
$key = str_pad($key, 64, chr(0));
}
$oPad = str_repeat(chr(0x5C), 64);
$iPad = str_repeat(chr(0x36), 64);
for ($i = 0; $i < strlen($key); $i++) {
$oPad[$i] = $oPad[$i] ^ $key[$i];
$iPad[$i] = $iPad[$i] ^ $key[$i];
}
return sha1($oPad.sha1($iPad.$data, true));
}
public function validateHashedPassword($user, $uid, $submittedPassword) {
foreach ($user as $key) {
if ($key[3] === "salt") {
$internalSalt = $key['value'];
}
if ($key[3] === "server_key") {
$internalServerKey = $key['value'];
}
if ($key[3] === "stored_key") {
$internalStoredKey = $key['value'];
}
}
unset($user);
$internalIteration = '4096';
$newSaltedPassword = hash_pbkdf2('sha1', $submittedPassword, $internalSalt, $internalIteration, 0, true);
$newServerKey = $this->hmacSha1($newSaltedPassword, 'Server Key');
$newClientKey = $this->hmacSha1($newSaltedPassword, 'Client Key');
$newStoredKey = sha1(hex2bin($newClientKey));
if ($newServerKey === $internalServerKey
&& $newStoredKey === $internalStoredKey) {
$uid = mb_strtolower($uid);
$this->storeUser($uid);
return $uid;
} else {
return false;
}
}
public function validatePlainPassword($user, $uid, $submittedPassword) {
foreach ($user as $key) {
if ($key[3] === "password") {
$internalPlainPassword = $key['value'];
}
}
unset($user);
if ($submittedPassword === $internalPlainPassword) {
$uid = mb_strtolower($uid);
$this->storeUser($uid);
return $uid;
} else {
return false;
}
}
public function checkPassword($uid, $password) {
$pdo = new \PDO("mysql:host=$this->host;dbname=$this->xmppDb", $this->xmppDbUser, $this->xmppDbPassword);
if (isset($uid)
&& isset($password)) {
if (!filter_var($uid, FILTER_VALIDATE_EMAIL)
|| !strpos($uid, $this->xmppDomain)
|| substr($uid, -strlen($this->xmppDomain)) !== $this->xmppDomain
) {
return false;
}
$user = explode("@", $uid);
$userName = strtolower($user[0]);
$submittedPassword = $password;
$statement = $pdo->prepare("SELECT * FROM prosody WHERE user = :user AND host = :xmppDomain AND store = 'accounts'");
$result = $statement->execute(array(
'user' => $userName,
'xmppDomain' => $this->xmppDomain
));
$user = $statement->fetchAll();
if (empty($user)) {
return false;
}
if ($this->passwordHashed === true) {
return $this->validateHashedPassword($user, $uid, $submittedPassword);
} else {
return $this->validatePlainPassword($user, $uid, $submittedPassword);
}
}
}
}

View file

@ -7,7 +7,7 @@
* later.
* See the COPYING-README file.
*/
namespace OCA\UserExternal;
namespace OCA\user_external;
/**
* Base class for external auth implementations that stores users
@ -21,7 +21,7 @@ namespace OCA\UserExternal;
* @license http://www.gnu.org/licenses/agpl AGPL
* @link http://github.com/owncloud/apps
*/
abstract class Base extends \OC\User\Backend {
abstract class Base extends \OC\User\Backend{
protected $backend = '';
/**
@ -66,7 +66,7 @@ abstract class Base extends \OC\User\Backend {
$user = $result->fetch();
$result->closeCursor();
$displayName = trim($user['displayname'] ?? '', ' ');
$displayName = trim($user['displayname'], ' ');
if (!empty($displayName)) {
return $displayName;
} else {
@ -80,6 +80,7 @@ abstract class Base extends \OC\User\Backend {
* @return array with all displayNames (value) and the corresponding uids (key)
*/
public function getDisplayNames($search = '', $limit = null, $offset = null) {
$connection = \OC::$server->getDatabaseConnection();
$query = $connection->getQueryBuilder();
$query->select('uid', 'displayname')
@ -105,10 +106,10 @@ abstract class Base extends \OC\User\Backend {
}
/**
* Get a list of all users
*
* @return array with all uids
*/
* Get a list of all users
*
* @return array with all uids
*/
public function getUsers($search = '', $limit = null, $offset = null) {
$connection = \OC::$server->getDatabaseConnection();
$query = $connection->getQueryBuilder();
@ -216,7 +217,7 @@ abstract class Base extends \OC\User\Backend {
/**
* Count the number of users.
*
* @return int the number of users
* @return int|bool The number of users on success false on failure
*/
public function countUsers() {
$connection = \OC::$server->getDatabaseConnection();
@ -228,6 +229,7 @@ abstract class Base extends \OC\User\Backend {
$users = $result->fetchColumn();
$result->closeCursor();
return $users;
return $users > 0;
}
}

View file

@ -6,14 +6,13 @@
* See the COPYING-README file.
*/
namespace OCA\UserExternal;
class OC_User_BasicAuth extends \OCA\user_external\Base {
class BasicAuth extends Base {
private $authUrl;
public function __construct($authUrl) {
parent::__construct($authUrl);
$this->authUrl = $authUrl;
$this->authUrl =$authUrl;
}
/**
@ -30,21 +29,21 @@ class BasicAuth extends Base {
* URL is indeed authenticating or not...
*/
$context = stream_context_create(array(
'http' => array(
'method' => "GET",
'follow_location' => 0
))
'http' => array(
'method' => "GET",
'follow_location' => 0
))
);
$canary = get_headers($this->authUrl, 1, $context);
if (!$canary) {
\OC::$server->getLogger()->error(
if(!$canary) {
OC::$server->getLogger()->error(
'ERROR: Not possible to connect to BasicAuth Url: '.$this->authUrl,
['app' => 'user_external']
);
return false;
}
if (!isset(array_change_key_case($canary, CASE_LOWER)['www-authenticate'])) {
\OC::$server->getLogger()->error(
OC::$server->getLogger()->error(
'ERROR: Mis-configured BasicAuth Url: '.$this->authUrl.', provided URL does not do authentication!',
['app' => 'user_external']
);
@ -52,17 +51,17 @@ class BasicAuth extends Base {
}
$context = stream_context_create(array(
'http' => array(
'method' => "GET",
'header' => "authorization: Basic " . base64_encode("$uid:$password"),
'follow_location' => 0
))
'http' => array(
'method' => "GET",
'header' => "authorization: Basic " . base64_encode("$uid:$password"),
'follow_location' => 0
))
);
$headers = get_headers($this->authUrl, 1, $context);
if (!$headers) {
\OC::$server->getLogger()->error(
'ERROR: Not possible to connect to BasicAuth Url: '.$this->authUrl,
if(!$headers) {
OC::$server->getLogger()->error(
'ERROR: Not possible to connect to BasicAuth Url: '.$this->authUrl,
['app' => 'user_external']
);
return false;
@ -82,8 +81,8 @@ class BasicAuth extends Base {
$this->storeUser($uid);
return $uid;
case "3":
\OC::$server->getLogger()->error(
'ERROR: Too many redirects from BasicAuth Url: '.$this->authUrl,
OC::$server->getLogger()->error(
'ERROR: Too many redirects from BasicAuth Url: '.$this->authUrl,
['app' => 'user_external']
);
return false;

View file

@ -6,8 +6,6 @@
* See the COPYING-README file.
*/
namespace OCA\UserExternal;
/**
* User authentication against a FTP/FTPS server
*
@ -17,7 +15,7 @@ namespace OCA\UserExternal;
* @license http://www.gnu.org/licenses/agpl AGPL
* @link http://github.com/owncloud/apps
*/
class FTP extends Base {
class OC_User_FTP extends \OCA\user_external\Base{
private $host;
private $secure;
private $protocol;
@ -28,12 +26,12 @@ class FTP extends Base {
* @param string $host Hostname or IP of FTP server
* @param boolean $secure TRUE to enable SSL
*/
public function __construct($host, $secure = false) {
$this->host = $host;
$this->secure = $secure;
$this->protocol = 'ftp';
if ($this->secure) {
$this->protocol .= 's';
public function __construct($host,$secure=false) {
$this->host=$host;
$this->secure=$secure;
$this->protocol='ftp';
if($this->secure) {
$this->protocol.='s';
}
parent::__construct($this->protocol . '://' . $this->host);
}
@ -48,7 +46,7 @@ class FTP extends Base {
*/
public function checkPassword($uid, $password) {
if (false === array_search($this->protocol, stream_get_wrappers())) {
\OC::$server->getLogger()->error(
OC::$server->getLogger()->error(
'ERROR: Stream wrapper not available: ' . $this->protocol,
['app' => 'user_external']
);
@ -56,11 +54,11 @@ class FTP extends Base {
}
// opendir handles the as %-encoded string, but this is not true for usernames and passwords, encode them before passing them
$url = sprintf('%s://%s:%s@%s/', $this->protocol, urlencode($uid), urlencode($password), $this->host);
$result = @opendir($url);
if (is_resource($result)) {
$result=@opendir($url);
if(is_resource($result)) {
$this->storeUser($uid);
return $uid;
} else {
}else{
return false;
}
}

View file

@ -7,7 +7,6 @@
* later.
* See the COPYING-README file.
*/
namespace OCA\UserExternal;
/**
* User authentication against an IMAP mail server
@ -18,7 +17,7 @@ namespace OCA\UserExternal;
* @license http://www.gnu.org/licenses/agpl AGPL
* @link http://github.com/owncloud/apps
*/
class IMAP extends Base {
class OC_User_IMAP extends \OCA\user_external\Base {
private $mailbox;
private $port;
private $sslmode;
@ -58,20 +57,20 @@ class IMAP extends Base {
// Replace escaped @ symbol in uid (which is a mail address)
// but only if there is no @ symbol and if there is a %40 inside the uid
if (!(strpos($uid, '@') !== false) && (strpos($uid, '%40') !== false)) {
$uid = str_replace("%40", "@", $uid);
$uid = str_replace("%40","@",$uid);
}
$pieces = explode('@', $uid);
if ($this->domain !== '') {
if (count($pieces) === 1) {
$username = $uid . '@' . $this->domain;
} elseif (count($pieces) === 2 && $pieces[1] === $this->domain) {
} else if(count($pieces) === 2 && $pieces[1] === $this->domain) {
$username = $uid;
if ($this->stripeDomain) {
$uid = $pieces[0];
}
} else {
\OC::$server->getLogger()->error(
OC::$server->getLogger()->error(
'ERROR: User has a wrong domain! Expecting: '.$this->domain,
['app' => 'user_external']
);
@ -79,11 +78,11 @@ class IMAP extends Base {
}
} else {
$username = $uid;
}
}
$groups = [];
if ((count($pieces) > 1) && $this->groupDomain && $pieces[1]) {
$groups[] = $pieces[1];
if ($this->groupDomain && $pieces[1]) {
$groups[] = $pieces[1];
}
$protocol = ($this->sslmode === "ssl") ? "imaps" : "imap";
@ -98,37 +97,16 @@ class IMAP extends Base {
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'CAPABILITY');
curl_exec($ch);
$errorcode = curl_errno($ch);
$canconnect = curl_exec($ch);
if ($errorcode === 0) {
if($canconnect) {
curl_close($ch);
$uid = mb_strtolower($uid);
$this->storeUser($uid, $groups);
return $uid;
} elseif ($errorcode === CURLE_COULDNT_CONNECT ||
$errorcode === CURLE_SSL_CONNECT_ERROR ||
$errorcode === 28) {
# This is not defined in PHP-8.x
# 28: CURLE_OPERATION_TIMEDOUT
\OC::$server->getLogger()->error(
'ERROR: Could not connect to imap server via curl: ' . curl_strerror($errorcode),
['app' => 'user_external']
);
} elseif ($errorcode === 9 ||
$errorcode === 67 ||
$errorcode === 94) {
# These are not defined in PHP-8.x
# 9: CURLE_REMOTE_ACCESS_DENIED
# 67: CURLE_LOGIN_DENIED
# 94: CURLE_AUTH_ERROR)
\OC::$server->getLogger()->error(
'ERROR: IMAP Login failed via curl: ' . curl_strerror($errorcode),
['app' => 'user_external']
);
} else {
\OC::$server->getLogger()->error(
'ERROR: IMAP server returned an error: ' . $errorcode . ' / ' . curl_strerror($errorcode),
OC::$server->getLogger()->error(
'ERROR: Could not connect to imap server via curl: '.curl_error($ch),
['app' => 'user_external']
);
}

View file

@ -5,7 +5,6 @@
* later.
* See the COPYING-README file.
*/
namespace OCA\UserExternal;
/**
* User authentication via samba (smbclient)
@ -16,11 +15,11 @@ namespace OCA\UserExternal;
* @license http://www.gnu.org/licenses/agpl AGPL
* @link http://github.com/owncloud/apps
*/
class SMB extends Base {
class OC_User_SMB extends \OCA\user_external\Base{
private $host;
public const SMBCLIENT = 'smbclient -L';
public const LOGINERROR = 'NT_STATUS_LOGON_FAILURE';
const SMBCLIENT = 'smbclient -L';
const LOGINERROR = 'NT_STATUS_LOGON_FAILURE';
/**
* Create new samba authentication provider
@ -29,7 +28,7 @@ class SMB extends Base {
*/
public function __construct($host) {
parent::__construct($host);
$this->host = $host;
$this->host=$host;
}
/**
@ -43,20 +42,20 @@ class SMB extends Base {
$command = self::SMBCLIENT.' '.escapeshellarg('//' . $this->host . '/dummy').' -U '.$uidEscaped.'%'.$password;
$lastline = exec($command, $output, $retval);
if ($retval === 127) {
\OC::$server->getLogger()->error(
OC::$server->getLogger()->error(
'ERROR: smbclient executable missing',
['app' => 'user_external']
);
return false;
} elseif (strpos($lastline, self::LOGINERROR) !== false) {
} else if (strpos($lastline, self::LOGINERROR) !== false) {
//normal login error
return false;
} elseif (strpos($lastline, 'NT_STATUS_BAD_NETWORK_NAME') !== false) {
} else if (strpos($lastline, 'NT_STATUS_BAD_NETWORK_NAME') !== false) {
//login on minor error
goto login;
} elseif ($retval !== 0) {
} else if ($retval !== 0) {
//some other error
\OC::$server->getLogger()->error(
OC::$server->getLogger()->error(
'ERROR: smbclient error: ' . trim($lastline),
['app' => 'user_external']
);
@ -78,13 +77,13 @@ class SMB extends Base {
public function checkPassword($uid, $password) {
// Check with an invalid password, if the user authenticates then fail
$attemptWithInvalidPassword = $this->tryAuthentication($uid, base64_encode($password));
if (is_string($attemptWithInvalidPassword)) {
if(is_string($attemptWithInvalidPassword)) {
return false;
}
// Check with valid password
$attemptWithValidPassword = $this->tryAuthentication($uid, $password);
if (is_string($attemptWithValidPassword)) {
if(is_string($attemptWithValidPassword)) {
$this->storeUser($uid);
return $uid;
}

View file

@ -5,7 +5,6 @@
* later.
* See the COPYING-README file.
*/
namespace OCA\UserExternal;
/**
* User authentication against a SSH server
@ -18,15 +17,15 @@ namespace OCA\UserExternal;
*/
class SSH extends Base {
class OC_User_SSH extends \OCA\user_external\Base {
private $host;
private $port;
private $port;
/**
* Create a new SSH authentication provider
*
* @param string $host Hostname or IP address of SSH servr
*/
* Create a new SSH authentication provider
*
* @param string $host Hostname or IP address of SSH servr
*/
public function __construct($host, $port = 22) {
parent::__construct($host);
$this->host = $host;
@ -34,17 +33,17 @@ class SSH extends Base {
}
/**
* Check if the password is correct without logging in
* Requires the php-ssh2 pecl extension
*
* @param string $uid The username
* @param string $password The password
*
* @return true/false
*/
* Check if the password is correct without logging in
* Requires the php-ssh2 pecl extension
*
* @param string $uid The username
* @param string $password The password
*
* @return true/false
*/
public function checkPassword($uid, $password) {
if (!extension_loaded('ssh2')) {
\OC::$server->getLogger()->error(
OC::$server->getLogger()->error(
'ERROR: php-ssh2 PECL module missing',
['app' => 'user_external']
);

View file

@ -6,14 +6,15 @@
* See the COPYING-README file.
*/
namespace OCA\UserExternal;
namespace OCA\user_external;
class WebDavAuth extends Base {
private $webDavAuthUrl;
public function __construct($webDavAuthUrl) {
parent::__construct($webDavAuthUrl);
$this->webDavAuthUrl = $webDavAuthUrl;
$this->webDavAuthUrl =$webDavAuthUrl;
}
/**
@ -26,20 +27,21 @@ class WebDavAuth extends Base {
*/
public function checkPassword($uid, $password) {
$arr = explode('://', $this->webDavAuthUrl, 2);
if (! isset($arr) or count($arr) !== 2) {
\OC::$server->getLogger()->error('ERROR: Invalid WebdavUrl: "'.$this->webDavAuthUrl.'" ', ['app' => 'user_external']);
if( ! isset($arr) OR count($arr) !== 2) {
OC::$server->getLogger()->error('ERROR: Invalid WebdavUrl: "'.$this->webDavAuthUrl.'" ', ['app' => 'user_external']);
return false;
}
list($protocol, $path) = $arr;
$url = $protocol.'://'.urlencode($uid).':'.urlencode($password).'@'.$path;
$url= $protocol.'://'.urlencode($uid).':'.urlencode($password).'@'.$path;
$headers = get_headers($url);
if ($headers === false) {
\OC::$server->getLogger()->error('ERROR: Not possible to connect to WebDAV Url: "'.$protocol.'://'.$path.'" ', ['app' => 'user_external']);
if($headers === false) {
OC::$server->getLogger()->error('ERROR: Not possible to connect to WebDAV Url: "'.$protocol.'://'.$path.'" ', ['app' => 'user_external']);
return false;
}
$returnCode = substr($headers[0], 9, 3);
if (substr($returnCode, 0, 1) === '2') {
}
$returnCode= substr($headers[0], 9, 3);
if(substr($returnCode, 0, 1) === '2') {
$this->storeUser($uid);
return $uid;
} else {

128
lib/xmpp.php Normal file
View file

@ -0,0 +1,128 @@
<?php
/**
* Copyright (c) 2019 Sebastian Sterk <sebastian@wiuwiu.de>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
/**
* User authentication against a XMPP Prosody MySQL database
*
* @category Apps
* @package UserExternal
* @author Sebastian Sterk https://wiuwiu.de/Imprint
* @license http://www.gnu.org/licenses/agpl AGPL
*/
class OC_User_XMPP extends \OCA\user_external\Base {
private $host;
private $xmppDb;
private $xmppDbUser;
private $xmppDbPassword;
private $xmppDomain;
private $passwordHashed;
public function __construct($host, $xmppDb, $xmppDbUser, $xmppDbPassword, $xmppDomain, $passwordHashed = true) {
parent::__construct($host);
$this->host = $host;
$this->xmppDb = $xmppDb;
$this->xmppDbUser = $xmppDbUser;
$this->xmppDbPassword = $xmppDbPassword;
$this->xmppDomain = $xmppDomain;
$this->passwordHashed = $passwordHashed;
}
public function hmacSha1($key, $data) {
if (strlen($key) > 64) {
$key = str_pad(sha1($key, true), 64, chr(0));
}
if (strlen($key) < 64) {
$key = str_pad($key, 64, chr(0));
}
$oPad = str_repeat(chr(0x5C), 64);
$iPad = str_repeat(chr(0x36), 64);
for ($i = 0; $i < strlen($key); $i++) {
$oPad[$i] = $oPad[$i] ^ $key[$i];
$iPad[$i] = $iPad[$i] ^ $key[$i];
}
return sha1($oPad.sha1($iPad.$data, true));
}
public function validateHashedPassword($user, $uid, $submittedPassword){
foreach ($user as $key){
if($key[3] === "salt") {
$internalSalt = $key['value'];
}
if($key[3] === "server_key") {
$internalServerKey = $key['value'];
}
if($key[3] === "stored_key") {
$internalStoredKey = $key['value'];
}
}
unset($user);
$internalIteration = '4096';
$newSaltedPassword = hash_pbkdf2('sha1', $submittedPassword, $internalSalt, $internalIteration, 0, true);
$newServerKey = $this->hmacSha1($newSaltedPassword, 'Server Key');
$newClientKey = $this->hmacSha1($newSaltedPassword, 'Client Key');
$newStoredKey = sha1(hex2bin($newClientKey));
if ($newServerKey === $internalServerKey
&& $newStoredKey === $internalStoredKey) {
$uid = mb_strtolower($uid);
$this->storeUser($uid);
return $uid;
} else {
return false;
}
}
public function validatePlainPassword($user, $uid, $submittedPassword) {
foreach ($user as $key) {
if($key[3] === "password") {
$internalPlainPassword = $key['value'];
}
}
unset($user);
if ($submittedPassword === $internalPlainPassword) {
$uid = mb_strtolower($uid);
$this->storeUser($uid);
return $uid;
} else {
return false;
}
}
public function checkPassword($uid, $password){
$pdo = new PDO("mysql:host=$this->host;dbname=$this->xmppDb", $this->xmppDbUser, $this->xmppDbPassword);
if(isset($uid)
&& isset($password)) {
if(!filter_var($uid, FILTER_VALIDATE_EMAIL)
|| !strpos($uid, $this->xmppDomain)
|| substr($uid, -strlen($this->xmppDomain)) !== $this->xmppDomain
) {
return false;
}
$user = explode("@", $uid);
$userName = strtolower($user[0]);
$submittedPassword = $password;
$statement = $pdo->prepare("SELECT * FROM prosody WHERE user = :user AND host = :xmppDomain AND store = 'accounts'");
$result = $statement->execute(array(
'user' => $userName,
'xmppDomain' => $this->xmppDomain
));
$user = $statement->fetchAll();
if(empty($user)) {
return false;
}
if ($this->passwordHashed === true) {
return $this->validateHashedPassword($user, $uid, $submittedPassword);
} else {
return $this->validatePlainPassword($user, $uid, $submittedPassword);
}
}
}
}

View file

@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
require_once './vendor/autoload.php';
use Nextcloud\CodingStandard\Config;
$config = new Config();
$config
->getFinder()
->notPath('build')
->notPath('l10n')
->notPath('node_modules')
->notPath('src')
->notPath('vendor')
->in(__DIR__);
return $config;

View file

@ -16,20 +16,20 @@ class Test_User_BasicAuth extends \Test\TestCase {
return include(__DIR__.'/config.php');
}
public function skip() {
$config = $this->getConfig();
function skip() {
$config=$this->getConfig();
$this->skipUnless($config['basic_auth']['run']);
}
protected function setUp() {
parent::setUp();
$config = $this->getConfig();
$this->instance = new OC_User_BasicAuth($config['basic_auth']['url']);
$config=$this->getConfig();
$this->instance=new OC_User_BasicAuth($config['basic_auth']['url']);
}
public function testLogin() {
$config = $this->getConfig();
$this->assertEquals($config['basic_auth']['user'], $this->instance->checkPassword($config['basic_auth']['user'], $config['basic_auth']['password']));
$this->assertFalse($this->instance->checkPassword($config['basic_auth']['user'], $config['basic_auth']['password'].'foo'));
function testLogin() {
$config=$this->getConfig();
$this->assertEquals($config['basic_auth']['user'],$this->instance->checkPassword($config['basic_auth']['user'],$config['basic_auth']['password']));
$this->assertFalse($this->instance->checkPassword($config['basic_auth']['user'],$config['basic_auth']['password'].'foo'));
}
}

View file

@ -1,5 +1,4 @@
<?php
if (!defined('PHPUNIT_RUN')) {
define('PHPUNIT_RUN', 1);
}
@ -10,7 +9,7 @@ if (!class_exists('\PHPUnit\Framework\TestCase')) {
\OC_App::loadApp('user_external');
$dummyClass = \OC::$SERVERROOT . '/tests/lib/Util/User/Dummy.php';
if (file_exists($dummyClass)) {
if(file_exists($dummyClass)) {
require_once($dummyClass);
}
OC_Hook::clear();

View file

@ -8,28 +8,28 @@
OC_App::loadApp('user_external');
return array(
'imap' => array(
'run' => false,
'mailbox' => '{imap.gmail.com:993/imap/ssl}INBOX', //see http://php.net/manual/en/function.imap-open.php
'user' => 'foo',//valid username/password combination
'password' => 'bar',
'imap'=>array(
'run'=>false,
'mailbox'=>'{imap.gmail.com:993/imap/ssl}INBOX', //see http://php.net/manual/en/function.imap-open.php
'user'=>'foo',//valid username/password combination
'password'=>'bar',
),
'smb' => array(
'run' => false,
'host' => 'localhost',
'user' => 'test',//valid username/password combination
'password' => 'test',
'smb'=>array(
'run'=>false,
'host'=>'localhost',
'user'=>'test',//valid username/password combination
'password'=>'test',
),
'ftp' => array(
'run' => false,
'host' => 'localhost',
'user' => 'test',//valid username/password combination
'password' => 'test',
'ftp'=>array(
'run'=>false,
'host'=>'localhost',
'user'=>'test',//valid username/password combination
'password'=>'test',
),
'basic_auth' => array(
'run' => false,
'url' => 'localhost/basic_auth',
'user' => 'test',//valid username/password combination
'password' => 'test',
'basic_auth'=>array(
'run'=>false,
'url'=>'localhost/basic_auth',
'user'=>'test',//valid username/password combination
'password'=>'test',
),
);

View file

@ -16,20 +16,20 @@ class Test_User_FTP extends \Test\TestCase {
return include(__DIR__.'/config.php');
}
public function skip() {
$config = $this->getConfig();
function skip() {
$config=$this->getConfig();
$this->skipUnless($config['ftp']['run']);
}
protected function setUp() {
parent::setUp();
$config = $this->getConfig();
$this->instance = new OC_User_FTP($config['ftp']['host']);
$config=$this->getConfig();
$this->instance=new OC_User_FTP($config['ftp']['host']);
}
public function testLogin() {
$config = $this->getConfig();
$this->assertEquals($config['ftp']['user'], $this->instance->checkPassword($config['ftp']['user'], $config['ftp']['password']));
$this->assertFalse($this->instance->checkPassword($config['ftp']['user'], $config['ftp']['password'].'foo'));
function testLogin() {
$config=$this->getConfig();
$this->assertEquals($config['ftp']['user'],$this->instance->checkPassword($config['ftp']['user'],$config['ftp']['password']));
$this->assertFalse($this->instance->checkPassword($config['ftp']['user'],$config['ftp']['password'].'foo'));
}
}

View file

@ -16,21 +16,21 @@ class Test_User_Imap extends \Test\TestCase {
return include(__DIR__.'/config.php');
}
public function skip() {
$config = $this->getConfig();
function skip() {
$config=$this->getConfig();
$this->skipUnless($config['imap']['run']);
}
protected function setUp() {
parent::setUp();
$config = $this->getConfig();
$this->instance = new OC_User_IMAP($config['imap']['mailbox']);
$config=$this->getConfig();
$this->instance=new OC_User_IMAP($config['imap']['mailbox']);
}
public function testLogin() {
$config = $this->getConfig();
$this->assertEquals($config['imap']['user'], $this->instance->checkPassword($config['imap']['user'], $config['imap']['password']));
$this->assertFalse($this->instance->checkPassword($config['imap']['user'], $config['imap']['password'].'foo'));
function testLogin() {
$config=$this->getConfig();
$this->assertEquals($config['imap']['user'],$this->instance->checkPassword($config['imap']['user'],$config['imap']['password']));
$this->assertFalse($this->instance->checkPassword($config['imap']['user'],$config['imap']['password'].'foo'));
}
}

View file

@ -16,21 +16,21 @@ class Test_User_SMB extends \Test\TestCase {
return include(__DIR__.'/config.php');
}
public function skip() {
$config = $this->getConfig();
function skip() {
$config=$this->getConfig();
$this->skipUnless($config['smb']['run']);
}
protected function setUp() {
parent::setUp();
$config = $this->getConfig();
$this->instance = new OC_User_SMB($config['smb']['host']);
$config=$this->getConfig();
$this->instance=new OC_User_SMB($config['smb']['host']);
}
public function testLogin() {
$config = $this->getConfig();
$this->assertEquals($config['smb']['user'], $this->instance->checkPassword($config['smb']['user'], $config['smb']['password']));
$this->assertFalse($this->instance->checkPassword($config['smb']['user'], $config['smb']['password'].'foo'));
function testLogin() {
$config=$this->getConfig();
$this->assertEquals($config['smb']['user'],$this->instance->checkPassword($config['smb']['user'],$config['smb']['password']));
$this->assertFalse($this->instance->checkPassword($config['smb']['user'],$config['smb']['password'].'foo'));
}
}