diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 4374d03954f71..6605e2b6499ab 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,34 +1,56 @@
-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 \
@@ -36,12 +58,29 @@ RUN apt-get -y install \
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
diff --git a/.devcontainer/README.md b/.devcontainer/README.md
new file mode 100644
index 0000000000000..da540711fc871
--- /dev/null
+++ b/.devcontainer/README.md
@@ -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 F1→*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`
+Password: `admin`
+
+**Postgres credentials**
+
+Username: `postgres`
+Password: `postgres`
+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 ` (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.
\ No newline at end of file
diff --git a/.devcontainer/codespace.config.php b/.devcontainer/codespace.config.php
index 5d883c3c9dfdb..3c5257635400b 100644
--- a/.devcontainer/codespace.config.php
+++ b/.devcontainer/codespace.config.php
@@ -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 ];
}
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index f86a8cf34302e..3fb1bf42e4406 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -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"
}
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index ada819429f442..a30bba74a6653 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -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
@@ -16,6 +17,9 @@ services:
restart: always
environment:
POSTGRES_PASSWORD: postgres
+ PGDATA: /data/postgres
+ volumes:
+ - db:/data/postgres
network_mode: service:nextclouddev
adminer:
@@ -27,3 +31,6 @@ services:
image: mailhog/mailhog
restart: always
network_mode: service:nextclouddev
+
+volumes:
+ db:
diff --git a/.devcontainer/entrypoint.sh b/.devcontainer/entrypoint.sh
new file mode 100755
index 0000000000000..952f6f8aad65a
--- /dev/null
+++ b/.devcontainer/entrypoint.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+sudo service apache2 start
+
+while sleep 1000; do :; done
diff --git a/.devcontainer/launch.json b/.devcontainer/launch.json
new file mode 100644
index 0000000000000..8103dd29461d2
--- /dev/null
+++ b/.devcontainer/launch.json
@@ -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
+ }
+ ]
+}
diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh
index 9d2e3d73f98e8..3485d4f7257c6 100755
--- a/.devcontainer/setup.sh
+++ b/.devcontainer/setup.sh
@@ -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
diff --git a/.gitattributes b/.gitattributes
index 7f8fd14dd1d6e..baaf0e69fe00a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,4 @@
/dist/* binary
/package-lock.json merge=binary
/core/css/*.css* binary
+/.devcontainer/*.sh text eol=lf
diff --git a/lib/private/Authentication/TwoFactorAuth/Manager.php b/lib/private/Authentication/TwoFactorAuth/Manager.php
index 7e115cf9b4221..3e71d0787b313 100644
--- a/lib/private/Authentication/TwoFactorAuth/Manager.php
+++ b/lib/private/Authentication/TwoFactorAuth/Manager.php
@@ -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';
@@ -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) {
diff --git a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php b/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php
index da11b11e53780..7aa1fe9668cbc 100644
--- a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php
+++ b/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php
@@ -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();