Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Distinguish 'done' from 'configuring' in 2FA #35555

Closed
wants to merge 12 commits into from
95 changes: 67 additions & 28 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,47 +1,86 @@
FROM ubuntu:focal
FROM ubuntu:jammy

ARG DEBIAN_FRONTEND=noninteractive

# PHP
RUN apt-get update -y
RUN apt-get update -y && \
apt install -y apache2 vim software-properties-common sudo nano gnupg2

RUN apt-get install --no-install-recommends -y \
php7.4 \
php7.4-gd \
php7.4-zip \
php7.4-curl \
php7.4-xml \
php7.4-mbstring \
php7.4-sqlite \
php7.4-xdebug \
php7.4-pgsql \
php7.4-intl \
php7.4-imagick \
php7.4-gmp \
php7.4-apcu \
php7.4-bcmath \
php8.1 \
php8.1-common \
php8.1-gd \
php8.1-zip \
php8.1-curl \
php8.1-xml \
php8.1-xmlrpc \
php8.1-mbstring \
php8.1-sqlite \
php8.1-xdebug \
php8.1-pgsql \
php8.1-intl \
php8.1-imagick \
php8.1-gmp \
php8.1-apcu \
php8.1-bcmath \
php8.1-redis \
php8.1-soap \
php8.1-imap \
php8.1-opcache \
php8.1-cli \
php8.1-dev \
libmagickcore-6.q16-3-extra \
curl \
vim \
lsof \
make \
nodejs \
npm
unzip

# Composer
RUN curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php && \
curl -sS https://composer.github.io/installer.sig -o /tmp/composer-setup.sig && \
php -r "if (hash_file('sha384', '/tmp/composer-setup.php') !== trim(file_get_contents('/tmp/composer-setup.sig'))) { echo 'Composer installation failed, invalid hash'; exit(1); }" && \
php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer && \
rm /tmp/composer-setup.php /tmp/composer-setup.sig

RUN echo "xdebug.remote_enable = 1" >> /etc/php/7.4/cli/conf.d/20-xdebug.ini
RUN echo "xdebug.remote_autostart = 1" >> /etc/php/7.4/cli/conf.d/20-xdebug.ini
RUN echo "xdebug.remote_enable = 1" >> /etc/php/8.1/cli/conf.d/20-xdebug.ini && \
echo "xdebug.remote_autostart = 1" >> /etc/php/8.1/cli/conf.d/20-xdebug.ini && \
echo "apc.enable_cli=1" >> /etc/php/8.1/cli/conf.d/20-apcu.ini

# Autostart XDebug for apache
RUN { \
echo "xdebug.mode=debug"; \
echo "xdebug.start_with_request=yes"; \
} >> /etc/php/8.1/apache2/conf.d/20-xdebug.ini

# Docker
RUN apt-get -y install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
RUN add-apt-repository \
software-properties-common && \
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - && \
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
RUN apt-get update -y
RUN apt-get install -y docker-ce docker-ce-cli containerd.io
RUN ln -s /var/run/docker-host.sock /var/run/docker.sock
stable" && \
apt-get update -y && \
apt-get install -y docker-ce docker-ce-cli containerd.io && \
ln -s /var/run/docker-host.sock /var/run/docker.sock

# Dedicated DevContainer user runs Apache
ENV APACHE_RUN_USER=devcontainer
ENV APACHE_RUN_GROUP=devcontainer
RUN useradd -ms /bin/bash ${APACHE_RUN_USER} && \
adduser ${APACHE_RUN_USER} sudo && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
sed -ri "s/^export APACHE_RUN_USER=.*$/export APACHE_RUN_USER=${APACHE_RUN_USER}/" "/etc/apache2/envvars" && \
sed -ri "s/^export APACHE_RUN_GROUP=.*$/export APACHE_RUN_GROUP=${APACHE_RUN_GROUP}/" "/etc/apache2/envvars"

USER devcontainer

# NVM
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
RUN bash --login -i -c 'source /home/devcontainer/.bashrc && nvm install 16'

WORKDIR /var/www/html
72 changes: 72 additions & 0 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Nextcloud DevContainer

## Usage

Make sure you have the [VSCode DevContainer](https://code.visualstudio.com/docs/devcontainers/containers) extensions installed. If you open the project, VSCode will ask you if you want to open it inside of the DevContainer. If that's not the case, use <kbd>F1</kbd>&rarr;*Dev Containers: Open Folder in Container*.

Alternatively open the project directly in [GitHub Codespaces](https://github.com/features/codespaces).

That's already it. Everything else will be configured automatically by the Containers startup routine.

## Credentials

On first start the Container installs and configures Nextcloud with the following credentials:

**Nextcloud Admin Login**

Username: `admin` <br>
Password: `admin`

**Postgres credentials**

Username: `postgres` <br>
Password: `postgres` <br>
Database: `postgres`

## Services

The following services will be started:

| Service | Local port | Description |
|---------|------------|-------------|
| Nextcloud (served via Apache) | `80` | The main application |
| Mailhog | `8025` | SMTP email delivery for testing |
| Adminer | `8080` | Database viewer. Use credentials from above and connect to `localhost` to get access to the NC database |

## Permissions

The container runs with the user `devcontainer` who is also running the Apache2 process. All mounted source files have
proper permissions so that this user can access everything which is inside the current workspace. If you need to
get root permissions for whatever reason, use `sudo su` or `sudo <command>` (for example `sudo service apache2 restart`).
Everything else (like building the application, adjusting files, ...) should be done as `devcontainer` user.

## NodeJs and NVM

The container comes with [`nvm`](https://github.com/nvm-sh/nvm) and Node 16 installed. This should be sufficient to
build Nextcloud Core sources via `make`. If you need a different Node Version (for example for
app development), you can easily switch between different versions by running:

```bash
# Install and use Node 14
nvm install 14
nvm use 14

# Check version
node -v

# Switch back to Node 16
nvm use 16

# Check version
node -v
```

Note that `nvm` is only installed for the user `devcontainer` and won't work out of the box for
any other user.

## Debugging

The Apache webserver is already configured to automatically try to connect to a debugger process
listening on port `9003`. To start the VSCode debugger process, use the delivered debug profile `Listen for XDebug`.
After you started the VSCode debugger, just navigate to the appropriate Nextcloud URL to get your
debug hits.
5 changes: 4 additions & 1 deletion .devcontainer/codespace.config.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
];

if(is_string($codespaceName) && !empty($codespaceName) && is_string($codespaceDomain) && !empty($codespaceDomain)) {
$CONFIG['overwritehost'] = $codespaceName . '-80.' . $codespaceDomain;
$host = $codespaceName . '-80.' . $codespaceDomain;
$CONFIG['overwritehost'] = $host;
$CONFIG['overwrite.cli.url'] = 'https://' . $host;
$CONFIG['overwriteprotocol'] = 'https';
$CONFIG['trusted_domains'] = [ $host ];
}
27 changes: 16 additions & 11 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@
8080,
8025
],
"runArgs": [
"--privileged"
],
"extensions": [
"felixfbecker.php-debug",
"felixfbecker.php-intellisense",
"ms-azuretools.vscode-docker"
],
"settings": {
"php.suggest.basic": false,
}
"customizations": {
"vscode": {
"extensions": [
"felixfbecker.php-debug",
"felixfbecker.php-intellisense",
"ms-azuretools.vscode-docker",
"xdebug.php-debug",
"donjayamanne.githistory"
],
"settings": {
"php.suggest.basic": false
}
}
},
"workspaceFolder": "/var/www/html",
"remoteUser": "devcontainer"
}
9 changes: 8 additions & 1 deletion .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ services:
volumes:
- .:/workspace:cached
- /var/run/docker.sock:/var/run/docker-host.sock
command: /bin/sh -c "while sleep 1000; do :; done"
- ..:/var/www/html
command: /var/www/html/.devcontainer/entrypoint.sh
ports:
- 80:80
- 8080:8080
Expand All @@ -16,6 +17,9 @@ services:
restart: always
environment:
POSTGRES_PASSWORD: postgres
PGDATA: /data/postgres
volumes:
- db:/data/postgres
network_mode: service:nextclouddev

adminer:
Expand All @@ -27,3 +31,6 @@ services:
image: mailhog/mailhog
restart: always
network_mode: service:nextclouddev

volumes:
db:
5 changes: 5 additions & 0 deletions .devcontainer/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

sudo service apache2 start

while sleep 1000; do :; done
14 changes: 14 additions & 0 deletions .devcontainer/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003
}
]
}
25 changes: 25 additions & 0 deletions .devcontainer/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,32 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" >/dev/null 2>&1 && pwd )"

cd $DIR/

# Set git safe.directory
git config --global --add safe.directory /var/www/html
git config --global --add safe.directory /var/www/html/3rdparty

git submodule update --init

# Codespace config
cp .devcontainer/codespace.config.php config/codespace.config.php

# VSCode debugger profile
mkdir -p .vscode && cp .devcontainer/launch.json .vscode/launch.json

# Onetime installation setup
if [[ ! $(sudo -u ${APACHE_RUN_USER} php occ status) =~ installed:[[:space:]]*true ]]; then
echo "Running NC installation"
sudo -u ${APACHE_RUN_USER} php occ maintenance:install \
--verbose \
--database=pgsql \
--database-name=postgres \
--database-host=127.0.0.1 \
--database-port=5432 \
--database-user=postgres \
--database-pass=postgres \
--admin-user admin \
--admin-pass admin
fi

sudo service apache2 restart
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/dist/* binary
/package-lock.json merge=binary
/core/css/*.css* binary
/.devcontainer/*.sh text eol=lf
3 changes: 2 additions & 1 deletion lib/private/Authentication/TwoFactorAuth/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
class Manager {
public const SESSION_UID_KEY = 'two_factor_auth_uid';
public const SESSION_UID_DONE = 'two_factor_auth_passed';
public const SESSION_UID_CONFIGURING = 'two_factor_auth_configuring';
public const REMEMBER_LOGIN = 'two_factor_remember_login';
public const BACKUP_CODES_PROVIDER_ID = 'backup_codes';

Expand Down Expand Up @@ -359,7 +360,7 @@ public function needsSecondFactor(IUser $user = null): bool {
$tokensNeeding2FA = $this->config->getUserKeys($user->getUID(), 'login_token_2fa');

if (!\in_array((string) $tokenId, $tokensNeeding2FA, true)) {
$this->session->set(self::SESSION_UID_DONE, $user->getUID());
$this->session->set(self::SESSION_UID_CONFIGURING, $user->getUID());
return false;
}
} catch (InvalidTokenException|SessionNotAvailableException $e) {
Expand Down
35 changes: 34 additions & 1 deletion tests/lib/Authentication/TwoFactorAuth/ManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -673,11 +673,44 @@ public function testNeedsSecondFactorSessionAuthFailDBPass() {

$this->session->expects($this->once())
->method('set')
->with(Manager::SESSION_UID_DONE, 'user');
->with(Manager::SESSION_UID_CONFIGURING, 'user');

$this->assertFalse($this->manager->needsSecondFactor($user));
}

public function testNeedsSecondFactorWhileConfiguring() {
$user = $this->createMock(IUser::class);
$user->method('getUID')
->willReturn('user');

$this->session->method('exists')
->willReturn(false);
$this->session->method('getId')
->willReturn('mysessionid');

$token = $this->createMock(OC\Authentication\Token\IToken::class);
$token->method('getId')
->willReturn(40);

$this->tokenProvider->method('getToken')
->with('mysessionid')
->willReturn($token);

$this->config->method('getUserKeys')
->with('user', 'login_token_2fa')
->willReturn([
'42', '43', '44'
]);

// the user is still configuring 2FA with token 40
$this->session->expects($this->once())
->method('set')
->with(Manager::SESSION_UID_CONFIGURING, 'user');

// 2FA should not be required if configuration is not complete
$this->assertFalse($this->manager->needsSecondFactor($user));
}

public function testNeedsSecondFactorInvalidToken() {
$this->prepareNoProviders();

Expand Down