Completed
Push — master ( f236c0...92efa0 )
by John
20:54
created
lib/public/AppFramework/AuthPublicShareController.php 1 patch
Indentation   +201 added lines, -201 removed lines patch added patch discarded remove patch
@@ -30,205 +30,205 @@
 block discarded – undo
30 30
  * @since 14.0.0
31 31
  */
32 32
 abstract class AuthPublicShareController extends PublicShareController {
33
-	/** @var IURLGenerator */
34
-	protected $urlGenerator;
35
-
36
-	/**
37
-	 * @since 14.0.0
38
-	 */
39
-	public function __construct(string $appName,
40
-		IRequest $request,
41
-		ISession $session,
42
-		IURLGenerator $urlGenerator) {
43
-		parent::__construct($appName, $request, $session);
44
-
45
-		$this->urlGenerator = $urlGenerator;
46
-	}
47
-
48
-	/**
49
-	 * Show the authentication page
50
-	 * The form has to submit to the authenticate method route
51
-	 *
52
-	 * @since 14.0.0
53
-	 */
54
-	#[NoCSRFRequired]
55
-	#[PublicPage]
56
-	public function showAuthenticate(): TemplateResponse {
57
-		return new TemplateResponse('core', 'publicshareauth', [], 'guest');
58
-	}
59
-
60
-	/**
61
-	 * The template to show when authentication failed
62
-	 *
63
-	 * @since 14.0.0
64
-	 */
65
-	protected function showAuthFailed(): TemplateResponse {
66
-		return new TemplateResponse('core', 'publicshareauth', ['wrongpw' => true], 'guest');
67
-	}
68
-
69
-	/**
70
-	 * The template to show after user identification
71
-	 *
72
-	 * @since 24.0.0
73
-	 */
74
-	protected function showIdentificationResult(bool $success): TemplateResponse {
75
-		return new TemplateResponse('core', 'publicshareauth', ['identityOk' => $success], 'guest');
76
-	}
77
-
78
-	/**
79
-	 * Validates that the provided identity is allowed to receive a temporary password
80
-	 *
81
-	 * @since 24.0.0
82
-	 */
83
-	protected function validateIdentity(?string $identityToken = null): bool {
84
-		return false;
85
-	}
86
-
87
-	/**
88
-	 * Generates a password
89
-	 *
90
-	 * @since 24.0.0
91
-	 */
92
-	protected function generatePassword(): void {
93
-	}
94
-
95
-	/**
96
-	 * Verify the password
97
-	 *
98
-	 * @since 24.0.0
99
-	 */
100
-	protected function verifyPassword(string $password): bool {
101
-		return false;
102
-	}
103
-
104
-	/**
105
-	 * Function called after failed authentication
106
-	 *
107
-	 * You can use this to do some logging for example
108
-	 *
109
-	 * @since 14.0.0
110
-	 */
111
-	protected function authFailed() {
112
-	}
113
-
114
-	/**
115
-	 * Function called after successful authentication
116
-	 *
117
-	 * You can use this to do some logging for example
118
-	 *
119
-	 * @since 14.0.0
120
-	 */
121
-	protected function authSucceeded() {
122
-	}
123
-
124
-	/**
125
-	 * Authenticate the share
126
-	 *
127
-	 * @since 14.0.0
128
-	 */
129
-	#[BruteForceProtection(action: 'publicLinkAuth')]
130
-	#[PublicPage]
131
-	#[UseSession]
132
-	final public function authenticate(string $password = '', string $passwordRequest = 'no', string $identityToken = '') {
133
-		// Already authenticated
134
-		if ($this->isAuthenticated()) {
135
-			return $this->getRedirect();
136
-		}
137
-
138
-		// Is user requesting a temporary password?
139
-		if ($passwordRequest == '') {
140
-			if ($this->validateIdentity($identityToken)) {
141
-				$this->generatePassword();
142
-				$response = $this->showIdentificationResult(true);
143
-				return $response;
144
-			} else {
145
-				$response = $this->showIdentificationResult(false);
146
-				$response->throttle();
147
-				return $response;
148
-			}
149
-		}
150
-
151
-		if (!$this->verifyPassword($password)) {
152
-			$this->authFailed();
153
-			$response = $this->showAuthFailed();
154
-			$response->throttle();
155
-			return $response;
156
-		}
157
-
158
-		$this->session->regenerateId(true, true);
159
-		$response = $this->getRedirect();
160
-
161
-		$this->storeTokenSession($this->getToken(), $this->getPasswordHash());
162
-
163
-		$this->authSucceeded();
164
-
165
-		return $response;
166
-	}
167
-
168
-	/**
169
-	 * Default landing page
170
-	 *
171
-	 * @since 14.0.0
172
-	 */
173
-	abstract public function showShare(): TemplateResponse;
174
-
175
-	/**
176
-	 * @since 14.0.0
177
-	 */
178
-	final public function getAuthenticationRedirect(string $redirect): RedirectResponse {
179
-		return new RedirectResponse(
180
-			$this->urlGenerator->linkToRoute($this->getRoute('showAuthenticate'), ['token' => $this->getToken(), 'redirect' => $redirect])
181
-		);
182
-	}
183
-
184
-
185
-	/**
186
-	 * @since 14.0.0
187
-	 */
188
-	private function getRoute(string $function): string {
189
-		$app = strtolower($this->appName);
190
-		$class = (new \ReflectionClass($this))->getShortName();
191
-		if (str_ends_with($class, 'Controller')) {
192
-			$class = substr($class, 0, -10);
193
-		}
194
-		return $app . '.' . $class . '.' . $function;
195
-	}
196
-
197
-	/**
198
-	 * @since 14.0.0
199
-	 */
200
-	private function getRedirect(): RedirectResponse {
201
-		//Get all the stored redirect parameters:
202
-		$params = $this->session->get('public_link_authenticate_redirect');
203
-
204
-		$route = $this->getRoute('showShare');
205
-
206
-		if ($params === null) {
207
-			$params = [
208
-				'token' => $this->getToken(),
209
-			];
210
-		} else {
211
-			$params = json_decode($params, true);
212
-			if (isset($params['_route'])) {
213
-				$route = $params['_route'];
214
-				unset($params['_route']);
215
-			}
216
-
217
-			// If the token doesn't match the rest of the arguments can't be trusted either
218
-			if (isset($params['token']) && $params['token'] !== $this->getToken()) {
219
-				$params = [
220
-					'token' => $this->getToken(),
221
-				];
222
-			}
223
-
224
-			// We need a token
225
-			if (!isset($params['token'])) {
226
-				$params = [
227
-					'token' => $this->getToken(),
228
-				];
229
-			}
230
-		}
231
-
232
-		return new RedirectResponse($this->urlGenerator->linkToRoute($route, $params));
233
-	}
33
+    /** @var IURLGenerator */
34
+    protected $urlGenerator;
35
+
36
+    /**
37
+     * @since 14.0.0
38
+     */
39
+    public function __construct(string $appName,
40
+        IRequest $request,
41
+        ISession $session,
42
+        IURLGenerator $urlGenerator) {
43
+        parent::__construct($appName, $request, $session);
44
+
45
+        $this->urlGenerator = $urlGenerator;
46
+    }
47
+
48
+    /**
49
+     * Show the authentication page
50
+     * The form has to submit to the authenticate method route
51
+     *
52
+     * @since 14.0.0
53
+     */
54
+    #[NoCSRFRequired]
55
+    #[PublicPage]
56
+    public function showAuthenticate(): TemplateResponse {
57
+        return new TemplateResponse('core', 'publicshareauth', [], 'guest');
58
+    }
59
+
60
+    /**
61
+     * The template to show when authentication failed
62
+     *
63
+     * @since 14.0.0
64
+     */
65
+    protected function showAuthFailed(): TemplateResponse {
66
+        return new TemplateResponse('core', 'publicshareauth', ['wrongpw' => true], 'guest');
67
+    }
68
+
69
+    /**
70
+     * The template to show after user identification
71
+     *
72
+     * @since 24.0.0
73
+     */
74
+    protected function showIdentificationResult(bool $success): TemplateResponse {
75
+        return new TemplateResponse('core', 'publicshareauth', ['identityOk' => $success], 'guest');
76
+    }
77
+
78
+    /**
79
+     * Validates that the provided identity is allowed to receive a temporary password
80
+     *
81
+     * @since 24.0.0
82
+     */
83
+    protected function validateIdentity(?string $identityToken = null): bool {
84
+        return false;
85
+    }
86
+
87
+    /**
88
+     * Generates a password
89
+     *
90
+     * @since 24.0.0
91
+     */
92
+    protected function generatePassword(): void {
93
+    }
94
+
95
+    /**
96
+     * Verify the password
97
+     *
98
+     * @since 24.0.0
99
+     */
100
+    protected function verifyPassword(string $password): bool {
101
+        return false;
102
+    }
103
+
104
+    /**
105
+     * Function called after failed authentication
106
+     *
107
+     * You can use this to do some logging for example
108
+     *
109
+     * @since 14.0.0
110
+     */
111
+    protected function authFailed() {
112
+    }
113
+
114
+    /**
115
+     * Function called after successful authentication
116
+     *
117
+     * You can use this to do some logging for example
118
+     *
119
+     * @since 14.0.0
120
+     */
121
+    protected function authSucceeded() {
122
+    }
123
+
124
+    /**
125
+     * Authenticate the share
126
+     *
127
+     * @since 14.0.0
128
+     */
129
+    #[BruteForceProtection(action: 'publicLinkAuth')]
130
+    #[PublicPage]
131
+    #[UseSession]
132
+    final public function authenticate(string $password = '', string $passwordRequest = 'no', string $identityToken = '') {
133
+        // Already authenticated
134
+        if ($this->isAuthenticated()) {
135
+            return $this->getRedirect();
136
+        }
137
+
138
+        // Is user requesting a temporary password?
139
+        if ($passwordRequest == '') {
140
+            if ($this->validateIdentity($identityToken)) {
141
+                $this->generatePassword();
142
+                $response = $this->showIdentificationResult(true);
143
+                return $response;
144
+            } else {
145
+                $response = $this->showIdentificationResult(false);
146
+                $response->throttle();
147
+                return $response;
148
+            }
149
+        }
150
+
151
+        if (!$this->verifyPassword($password)) {
152
+            $this->authFailed();
153
+            $response = $this->showAuthFailed();
154
+            $response->throttle();
155
+            return $response;
156
+        }
157
+
158
+        $this->session->regenerateId(true, true);
159
+        $response = $this->getRedirect();
160
+
161
+        $this->storeTokenSession($this->getToken(), $this->getPasswordHash());
162
+
163
+        $this->authSucceeded();
164
+
165
+        return $response;
166
+    }
167
+
168
+    /**
169
+     * Default landing page
170
+     *
171
+     * @since 14.0.0
172
+     */
173
+    abstract public function showShare(): TemplateResponse;
174
+
175
+    /**
176
+     * @since 14.0.0
177
+     */
178
+    final public function getAuthenticationRedirect(string $redirect): RedirectResponse {
179
+        return new RedirectResponse(
180
+            $this->urlGenerator->linkToRoute($this->getRoute('showAuthenticate'), ['token' => $this->getToken(), 'redirect' => $redirect])
181
+        );
182
+    }
183
+
184
+
185
+    /**
186
+     * @since 14.0.0
187
+     */
188
+    private function getRoute(string $function): string {
189
+        $app = strtolower($this->appName);
190
+        $class = (new \ReflectionClass($this))->getShortName();
191
+        if (str_ends_with($class, 'Controller')) {
192
+            $class = substr($class, 0, -10);
193
+        }
194
+        return $app . '.' . $class . '.' . $function;
195
+    }
196
+
197
+    /**
198
+     * @since 14.0.0
199
+     */
200
+    private function getRedirect(): RedirectResponse {
201
+        //Get all the stored redirect parameters:
202
+        $params = $this->session->get('public_link_authenticate_redirect');
203
+
204
+        $route = $this->getRoute('showShare');
205
+
206
+        if ($params === null) {
207
+            $params = [
208
+                'token' => $this->getToken(),
209
+            ];
210
+        } else {
211
+            $params = json_decode($params, true);
212
+            if (isset($params['_route'])) {
213
+                $route = $params['_route'];
214
+                unset($params['_route']);
215
+            }
216
+
217
+            // If the token doesn't match the rest of the arguments can't be trusted either
218
+            if (isset($params['token']) && $params['token'] !== $this->getToken()) {
219
+                $params = [
220
+                    'token' => $this->getToken(),
221
+                ];
222
+            }
223
+
224
+            // We need a token
225
+            if (!isset($params['token'])) {
226
+                $params = [
227
+                    'token' => $this->getToken(),
228
+                ];
229
+            }
230
+        }
231
+
232
+        return new RedirectResponse($this->urlGenerator->linkToRoute($route, $params));
233
+    }
234 234
 }
Please login to merge, or discard this patch.
lib/public/AppFramework/PublicShareController.php 1 patch
Indentation   +116 added lines, -116 removed lines patch added patch discarded remove patch
@@ -26,120 +26,120 @@
 block discarded – undo
26 26
  */
27 27
 abstract class PublicShareController extends Controller {
28 28
 
29
-	public const DAV_AUTHENTICATED_FRONTEND = 'public_link_authenticated_frontend';
30
-
31
-	/** @var string */
32
-	private $token;
33
-
34
-	/**
35
-	 * @since 14.0.0
36
-	 */
37
-	public function __construct(
38
-		string $appName,
39
-		IRequest $request,
40
-		protected ISession $session,
41
-	) {
42
-		parent::__construct($appName, $request);
43
-	}
44
-
45
-	/**
46
-	 * Middleware set the token for the request
47
-	 *
48
-	 * @since 14.0.0
49
-	 */
50
-	final public function setToken(string $token) {
51
-		$this->token = $token;
52
-	}
53
-
54
-	/**
55
-	 * Get the token for this request
56
-	 *
57
-	 * @since 14.0.0
58
-	 */
59
-	final public function getToken(): string {
60
-		return $this->token;
61
-	}
62
-
63
-	/**
64
-	 * Get a hash of the password for this share
65
-	 *
66
-	 * To ensure access is blocked when the password to a share is changed we store
67
-	 * a hash of the password for this token.
68
-	 *
69
-	 * @since 14.0.0
70
-	 */
71
-	abstract protected function getPasswordHash(): ?string;
72
-
73
-	/**
74
-	 * Is the provided token a valid token
75
-	 *
76
-	 * This function is already called from the middleware directly after setting the token.
77
-	 *
78
-	 * @since 14.0.0
79
-	 */
80
-	abstract public function isValidToken(): bool;
81
-
82
-	/**
83
-	 * Is a share with this token password protected
84
-	 *
85
-	 * @since 14.0.0
86
-	 */
87
-	abstract protected function isPasswordProtected(): bool;
88
-
89
-	/**
90
-	 * Check if a share is authenticated or not
91
-	 *
92
-	 * @since 14.0.0
93
-	 */
94
-	public function isAuthenticated(): bool {
95
-		// Always authenticated against non password protected shares
96
-		if (!$this->isPasswordProtected()) {
97
-			return true;
98
-		}
99
-
100
-		// If we are authenticated properly
101
-		if ($this->validateTokenSession($this->getToken(), $this->getPasswordHash())) {
102
-			return true;
103
-		}
104
-
105
-		// Fail by default if nothing matches
106
-		return false;
107
-	}
108
-
109
-	/**
110
-	 * Function called if the share is not found.
111
-	 *
112
-	 * You can use this to do some logging for example
113
-	 *
114
-	 * @since 14.0.0
115
-	 */
116
-	public function shareNotFound() {
117
-	}
118
-
119
-	/**
120
-	 * Validate the token and password hash stored in session
121
-	 */
122
-	protected function validateTokenSession(string $token, string $passwordHash): bool {
123
-		$allowedTokensJSON = $this->session->get(self::DAV_AUTHENTICATED_FRONTEND) ?? '[]';
124
-		$allowedTokens = json_decode($allowedTokensJSON, true);
125
-		if (!is_array($allowedTokens)) {
126
-			$allowedTokens = [];
127
-		}
128
-
129
-		return ($allowedTokens[$token] ?? '') === $passwordHash;
130
-	}
131
-
132
-	/**
133
-	 * Store the token and password hash in session
134
-	 */
135
-	protected function storeTokenSession(string $token, string $passwordHash = ''): void {
136
-		$allowedTokensJSON = $this->session->get(self::DAV_AUTHENTICATED_FRONTEND) ?? '[]';
137
-		$allowedTokens = json_decode($allowedTokensJSON, true);
138
-		if (!is_array($allowedTokens)) {
139
-			$allowedTokens = [];
140
-		}
141
-
142
-		$allowedTokens[$token] = $passwordHash;
143
-		$this->session->set(self::DAV_AUTHENTICATED_FRONTEND, json_encode($allowedTokens));
144
-	}
29
+    public const DAV_AUTHENTICATED_FRONTEND = 'public_link_authenticated_frontend';
30
+
31
+    /** @var string */
32
+    private $token;
33
+
34
+    /**
35
+     * @since 14.0.0
36
+     */
37
+    public function __construct(
38
+        string $appName,
39
+        IRequest $request,
40
+        protected ISession $session,
41
+    ) {
42
+        parent::__construct($appName, $request);
43
+    }
44
+
45
+    /**
46
+     * Middleware set the token for the request
47
+     *
48
+     * @since 14.0.0
49
+     */
50
+    final public function setToken(string $token) {
51
+        $this->token = $token;
52
+    }
53
+
54
+    /**
55
+     * Get the token for this request
56
+     *
57
+     * @since 14.0.0
58
+     */
59
+    final public function getToken(): string {
60
+        return $this->token;
61
+    }
62
+
63
+    /**
64
+     * Get a hash of the password for this share
65
+     *
66
+     * To ensure access is blocked when the password to a share is changed we store
67
+     * a hash of the password for this token.
68
+     *
69
+     * @since 14.0.0
70
+     */
71
+    abstract protected function getPasswordHash(): ?string;
72
+
73
+    /**
74
+     * Is the provided token a valid token
75
+     *
76
+     * This function is already called from the middleware directly after setting the token.
77
+     *
78
+     * @since 14.0.0
79
+     */
80
+    abstract public function isValidToken(): bool;
81
+
82
+    /**
83
+     * Is a share with this token password protected
84
+     *
85
+     * @since 14.0.0
86
+     */
87
+    abstract protected function isPasswordProtected(): bool;
88
+
89
+    /**
90
+     * Check if a share is authenticated or not
91
+     *
92
+     * @since 14.0.0
93
+     */
94
+    public function isAuthenticated(): bool {
95
+        // Always authenticated against non password protected shares
96
+        if (!$this->isPasswordProtected()) {
97
+            return true;
98
+        }
99
+
100
+        // If we are authenticated properly
101
+        if ($this->validateTokenSession($this->getToken(), $this->getPasswordHash())) {
102
+            return true;
103
+        }
104
+
105
+        // Fail by default if nothing matches
106
+        return false;
107
+    }
108
+
109
+    /**
110
+     * Function called if the share is not found.
111
+     *
112
+     * You can use this to do some logging for example
113
+     *
114
+     * @since 14.0.0
115
+     */
116
+    public function shareNotFound() {
117
+    }
118
+
119
+    /**
120
+     * Validate the token and password hash stored in session
121
+     */
122
+    protected function validateTokenSession(string $token, string $passwordHash): bool {
123
+        $allowedTokensJSON = $this->session->get(self::DAV_AUTHENTICATED_FRONTEND) ?? '[]';
124
+        $allowedTokens = json_decode($allowedTokensJSON, true);
125
+        if (!is_array($allowedTokens)) {
126
+            $allowedTokens = [];
127
+        }
128
+
129
+        return ($allowedTokens[$token] ?? '') === $passwordHash;
130
+    }
131
+
132
+    /**
133
+     * Store the token and password hash in session
134
+     */
135
+    protected function storeTokenSession(string $token, string $passwordHash = ''): void {
136
+        $allowedTokensJSON = $this->session->get(self::DAV_AUTHENTICATED_FRONTEND) ?? '[]';
137
+        $allowedTokens = json_decode($allowedTokensJSON, true);
138
+        if (!is_array($allowedTokens)) {
139
+            $allowedTokens = [];
140
+        }
141
+
142
+        $allowedTokens[$token] = $passwordHash;
143
+        $this->session->set(self::DAV_AUTHENTICATED_FRONTEND, json_encode($allowedTokens));
144
+    }
145 145
 }
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/Auth.php 1 patch
Indentation   +178 added lines, -178 removed lines patch added patch discarded remove patch
@@ -28,182 +28,182 @@
 block discarded – undo
28 28
 use Sabre\HTTP\ResponseInterface;
29 29
 
30 30
 class Auth extends AbstractBasic {
31
-	public const DAV_AUTHENTICATED = 'AUTHENTICATED_TO_DAV_BACKEND';
32
-	private ?string $currentUser = null;
33
-
34
-	public function __construct(
35
-		private ISession $session,
36
-		private Session $userSession,
37
-		private IRequest $request,
38
-		private Manager $twoFactorManager,
39
-		private IThrottler $throttler,
40
-		string $principalPrefix = 'principals/users/',
41
-	) {
42
-		$this->principalPrefix = $principalPrefix;
43
-
44
-		// setup realm
45
-		$defaults = new Defaults();
46
-		$this->realm = $defaults->getName() ?: 'Nextcloud';
47
-	}
48
-
49
-	/**
50
-	 * Whether the user has initially authenticated via DAV
51
-	 *
52
-	 * This is required for WebDAV clients that resent the cookies even when the
53
-	 * account was changed.
54
-	 *
55
-	 * @see https://github.com/owncloud/core/issues/13245
56
-	 */
57
-	public function isDavAuthenticated(string $username): bool {
58
-		if (is_null($this->session->get(self::DAV_AUTHENTICATED))) {
59
-			return false;
60
-		}
61
-
62
-		return $this->session->get(self::DAV_AUTHENTICATED) === $username;
63
-	}
64
-
65
-	/**
66
-	 * Validates a username and password
67
-	 *
68
-	 * This method should return true or false depending on if login
69
-	 * succeeded.
70
-	 *
71
-	 * @param string $username
72
-	 * @param string $password
73
-	 * @return bool
74
-	 * @throws PasswordLoginForbidden
75
-	 */
76
-	protected function validateUserPass($username, $password) {
77
-		if ($this->userSession->isLoggedIn()
78
-			&& $this->isDavAuthenticated($this->userSession->getUser()->getUID())
79
-		) {
80
-			$this->session->close();
81
-			return true;
82
-		} else {
83
-			try {
84
-				if ($this->userSession->logClientIn($username, $password, $this->request, $this->throttler)) {
85
-					$this->session->set(self::DAV_AUTHENTICATED, $this->userSession->getUser()->getUID());
86
-					$this->session->close();
87
-					return true;
88
-				} else {
89
-					$this->session->close();
90
-					return false;
91
-				}
92
-			} catch (PasswordLoginForbiddenException $ex) {
93
-				$this->session->close();
94
-				throw new PasswordLoginForbidden();
95
-			} catch (MaxDelayReached $ex) {
96
-				$this->session->close();
97
-				throw new TooManyRequests();
98
-			}
99
-		}
100
-	}
101
-
102
-	/**
103
-	 * @return array{bool, string}
104
-	 * @throws NotAuthenticated
105
-	 * @throws ServiceUnavailable
106
-	 */
107
-	public function check(RequestInterface $request, ResponseInterface $response) {
108
-		try {
109
-			return $this->auth($request, $response);
110
-		} catch (NotAuthenticated $e) {
111
-			throw $e;
112
-		} catch (Exception $e) {
113
-			$class = get_class($e);
114
-			$msg = $e->getMessage();
115
-			Server::get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
116
-			throw new ServiceUnavailable("$class: $msg");
117
-		}
118
-	}
119
-
120
-	/**
121
-	 * Checks whether a CSRF check is required on the request
122
-	 */
123
-	private function requiresCSRFCheck(): bool {
124
-
125
-		$methodsWithoutCsrf = ['GET', 'HEAD', 'OPTIONS'];
126
-		if (in_array($this->request->getMethod(), $methodsWithoutCsrf)) {
127
-			return false;
128
-		}
129
-
130
-		// Official Nextcloud clients require no checks
131
-		if ($this->request->isUserAgent([
132
-			IRequest::USER_AGENT_CLIENT_DESKTOP,
133
-			IRequest::USER_AGENT_CLIENT_ANDROID,
134
-			IRequest::USER_AGENT_CLIENT_IOS,
135
-		])) {
136
-			return false;
137
-		}
138
-
139
-		// If not logged-in no check is required
140
-		if (!$this->userSession->isLoggedIn()) {
141
-			return false;
142
-		}
143
-
144
-		// POST always requires a check
145
-		if ($this->request->getMethod() === 'POST') {
146
-			return true;
147
-		}
148
-
149
-		// If logged-in AND DAV authenticated no check is required
150
-		if ($this->userSession->isLoggedIn()
151
-			&& $this->isDavAuthenticated($this->userSession->getUser()->getUID())) {
152
-			return false;
153
-		}
154
-
155
-		return true;
156
-	}
157
-
158
-	/**
159
-	 * @return array{bool, string}
160
-	 * @throws NotAuthenticated
161
-	 */
162
-	private function auth(RequestInterface $request, ResponseInterface $response): array {
163
-		$forcedLogout = false;
164
-
165
-		if (!$this->request->passesCSRFCheck()
166
-			&& $this->requiresCSRFCheck()) {
167
-			// In case of a fail with POST we need to recheck the credentials
168
-			if ($this->request->getMethod() === 'POST') {
169
-				$forcedLogout = true;
170
-			} else {
171
-				$response->setStatus(Http::STATUS_UNAUTHORIZED);
172
-				throw new \Sabre\DAV\Exception\NotAuthenticated('CSRF check not passed.');
173
-			}
174
-		}
175
-
176
-		if ($forcedLogout) {
177
-			$this->userSession->logout();
178
-		} else {
179
-			if ($this->twoFactorManager->needsSecondFactor($this->userSession->getUser())) {
180
-				throw new \Sabre\DAV\Exception\NotAuthenticated('2FA challenge not passed.');
181
-			}
182
-			if (
183
-				//Fix for broken webdav clients
184
-				($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED)))
185
-				//Well behaved clients that only send the cookie are allowed
186
-				|| ($this->userSession->isLoggedIn() && $this->session->get(self::DAV_AUTHENTICATED) === $this->userSession->getUser()->getUID() && empty($request->getHeader('Authorization')))
187
-				|| \OC_User::handleApacheAuth()
188
-			) {
189
-				$user = $this->userSession->getUser()->getUID();
190
-				$this->currentUser = $user;
191
-				$this->session->close();
192
-				return [true, $this->principalPrefix . $user];
193
-			}
194
-		}
195
-
196
-		$data = parent::check($request, $response);
197
-		if ($data[0] === true) {
198
-			$startPos = strrpos($data[1], '/') + 1;
199
-			$user = $this->userSession->getUser()->getUID();
200
-			$data[1] = substr_replace($data[1], $user, $startPos);
201
-		} elseif (in_array('XMLHttpRequest', explode(',', $request->getHeader('X-Requested-With') ?? ''))) {
202
-			// For ajax requests use dummy auth name to prevent browser popup in case of invalid creditials
203
-			$response->addHeader('WWW-Authenticate', 'DummyBasic realm="' . $this->realm . '"');
204
-			$response->setStatus(Http::STATUS_UNAUTHORIZED);
205
-			throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls');
206
-		}
207
-		return $data;
208
-	}
31
+    public const DAV_AUTHENTICATED = 'AUTHENTICATED_TO_DAV_BACKEND';
32
+    private ?string $currentUser = null;
33
+
34
+    public function __construct(
35
+        private ISession $session,
36
+        private Session $userSession,
37
+        private IRequest $request,
38
+        private Manager $twoFactorManager,
39
+        private IThrottler $throttler,
40
+        string $principalPrefix = 'principals/users/',
41
+    ) {
42
+        $this->principalPrefix = $principalPrefix;
43
+
44
+        // setup realm
45
+        $defaults = new Defaults();
46
+        $this->realm = $defaults->getName() ?: 'Nextcloud';
47
+    }
48
+
49
+    /**
50
+     * Whether the user has initially authenticated via DAV
51
+     *
52
+     * This is required for WebDAV clients that resent the cookies even when the
53
+     * account was changed.
54
+     *
55
+     * @see https://github.com/owncloud/core/issues/13245
56
+     */
57
+    public function isDavAuthenticated(string $username): bool {
58
+        if (is_null($this->session->get(self::DAV_AUTHENTICATED))) {
59
+            return false;
60
+        }
61
+
62
+        return $this->session->get(self::DAV_AUTHENTICATED) === $username;
63
+    }
64
+
65
+    /**
66
+     * Validates a username and password
67
+     *
68
+     * This method should return true or false depending on if login
69
+     * succeeded.
70
+     *
71
+     * @param string $username
72
+     * @param string $password
73
+     * @return bool
74
+     * @throws PasswordLoginForbidden
75
+     */
76
+    protected function validateUserPass($username, $password) {
77
+        if ($this->userSession->isLoggedIn()
78
+            && $this->isDavAuthenticated($this->userSession->getUser()->getUID())
79
+        ) {
80
+            $this->session->close();
81
+            return true;
82
+        } else {
83
+            try {
84
+                if ($this->userSession->logClientIn($username, $password, $this->request, $this->throttler)) {
85
+                    $this->session->set(self::DAV_AUTHENTICATED, $this->userSession->getUser()->getUID());
86
+                    $this->session->close();
87
+                    return true;
88
+                } else {
89
+                    $this->session->close();
90
+                    return false;
91
+                }
92
+            } catch (PasswordLoginForbiddenException $ex) {
93
+                $this->session->close();
94
+                throw new PasswordLoginForbidden();
95
+            } catch (MaxDelayReached $ex) {
96
+                $this->session->close();
97
+                throw new TooManyRequests();
98
+            }
99
+        }
100
+    }
101
+
102
+    /**
103
+     * @return array{bool, string}
104
+     * @throws NotAuthenticated
105
+     * @throws ServiceUnavailable
106
+     */
107
+    public function check(RequestInterface $request, ResponseInterface $response) {
108
+        try {
109
+            return $this->auth($request, $response);
110
+        } catch (NotAuthenticated $e) {
111
+            throw $e;
112
+        } catch (Exception $e) {
113
+            $class = get_class($e);
114
+            $msg = $e->getMessage();
115
+            Server::get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
116
+            throw new ServiceUnavailable("$class: $msg");
117
+        }
118
+    }
119
+
120
+    /**
121
+     * Checks whether a CSRF check is required on the request
122
+     */
123
+    private function requiresCSRFCheck(): bool {
124
+
125
+        $methodsWithoutCsrf = ['GET', 'HEAD', 'OPTIONS'];
126
+        if (in_array($this->request->getMethod(), $methodsWithoutCsrf)) {
127
+            return false;
128
+        }
129
+
130
+        // Official Nextcloud clients require no checks
131
+        if ($this->request->isUserAgent([
132
+            IRequest::USER_AGENT_CLIENT_DESKTOP,
133
+            IRequest::USER_AGENT_CLIENT_ANDROID,
134
+            IRequest::USER_AGENT_CLIENT_IOS,
135
+        ])) {
136
+            return false;
137
+        }
138
+
139
+        // If not logged-in no check is required
140
+        if (!$this->userSession->isLoggedIn()) {
141
+            return false;
142
+        }
143
+
144
+        // POST always requires a check
145
+        if ($this->request->getMethod() === 'POST') {
146
+            return true;
147
+        }
148
+
149
+        // If logged-in AND DAV authenticated no check is required
150
+        if ($this->userSession->isLoggedIn()
151
+            && $this->isDavAuthenticated($this->userSession->getUser()->getUID())) {
152
+            return false;
153
+        }
154
+
155
+        return true;
156
+    }
157
+
158
+    /**
159
+     * @return array{bool, string}
160
+     * @throws NotAuthenticated
161
+     */
162
+    private function auth(RequestInterface $request, ResponseInterface $response): array {
163
+        $forcedLogout = false;
164
+
165
+        if (!$this->request->passesCSRFCheck()
166
+            && $this->requiresCSRFCheck()) {
167
+            // In case of a fail with POST we need to recheck the credentials
168
+            if ($this->request->getMethod() === 'POST') {
169
+                $forcedLogout = true;
170
+            } else {
171
+                $response->setStatus(Http::STATUS_UNAUTHORIZED);
172
+                throw new \Sabre\DAV\Exception\NotAuthenticated('CSRF check not passed.');
173
+            }
174
+        }
175
+
176
+        if ($forcedLogout) {
177
+            $this->userSession->logout();
178
+        } else {
179
+            if ($this->twoFactorManager->needsSecondFactor($this->userSession->getUser())) {
180
+                throw new \Sabre\DAV\Exception\NotAuthenticated('2FA challenge not passed.');
181
+            }
182
+            if (
183
+                //Fix for broken webdav clients
184
+                ($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED)))
185
+                //Well behaved clients that only send the cookie are allowed
186
+                || ($this->userSession->isLoggedIn() && $this->session->get(self::DAV_AUTHENTICATED) === $this->userSession->getUser()->getUID() && empty($request->getHeader('Authorization')))
187
+                || \OC_User::handleApacheAuth()
188
+            ) {
189
+                $user = $this->userSession->getUser()->getUID();
190
+                $this->currentUser = $user;
191
+                $this->session->close();
192
+                return [true, $this->principalPrefix . $user];
193
+            }
194
+        }
195
+
196
+        $data = parent::check($request, $response);
197
+        if ($data[0] === true) {
198
+            $startPos = strrpos($data[1], '/') + 1;
199
+            $user = $this->userSession->getUser()->getUID();
200
+            $data[1] = substr_replace($data[1], $user, $startPos);
201
+        } elseif (in_array('XMLHttpRequest', explode(',', $request->getHeader('X-Requested-With') ?? ''))) {
202
+            // For ajax requests use dummy auth name to prevent browser popup in case of invalid creditials
203
+            $response->addHeader('WWW-Authenticate', 'DummyBasic realm="' . $this->realm . '"');
204
+            $response->setStatus(Http::STATUS_UNAUTHORIZED);
205
+            throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls');
206
+        }
207
+        return $data;
208
+    }
209 209
 }
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/PublicAuth.php 2 patches
Indentation   +210 added lines, -210 removed lines patch added patch discarded remove patch
@@ -36,214 +36,214 @@
 block discarded – undo
36 36
  * @package OCA\DAV\Connector
37 37
  */
38 38
 class PublicAuth extends AbstractBasic {
39
-	private const BRUTEFORCE_ACTION = 'public_dav_auth';
40
-	public const DAV_AUTHENTICATED = 'public_link_authenticated';
41
-
42
-	private ?IShare $share = null;
43
-
44
-	public function __construct(
45
-		private IRequest $request,
46
-		private IManager $shareManager,
47
-		private ISession $session,
48
-		private IThrottler $throttler,
49
-		private LoggerInterface $logger,
50
-		private IURLGenerator $urlGenerator,
51
-	) {
52
-		// setup realm
53
-		$defaults = new Defaults();
54
-		$this->realm = $defaults->getName();
55
-	}
56
-
57
-	/**
58
-	 * @throws NotAuthenticated
59
-	 * @throws MaxDelayReached
60
-	 * @throws ServiceUnavailable
61
-	 */
62
-	public function check(RequestInterface $request, ResponseInterface $response): array {
63
-		try {
64
-			$this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);
65
-
66
-			if (count($_COOKIE) > 0 && !$this->request->passesStrictCookieCheck() && $this->getShare()->getPassword() !== null) {
67
-				throw new PreconditionFailed('Strict cookie check failed');
68
-			}
69
-
70
-			$auth = new HTTP\Auth\Basic(
71
-				$this->realm,
72
-				$request,
73
-				$response
74
-			);
75
-
76
-			$userpass = $auth->getCredentials();
77
-			// If authentication provided, checking its validity
78
-			if ($userpass && !$this->validateUserPass($userpass[0], $userpass[1])) {
79
-				return [false, 'Username or password was incorrect'];
80
-			}
81
-
82
-			return $this->checkToken();
83
-		} catch (NotAuthenticated|MaxDelayReached $e) {
84
-			$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
85
-			throw $e;
86
-		} catch (PreconditionFailed $e) {
87
-			$response->setHeader(
88
-				'Location',
89
-				$this->urlGenerator->linkToRoute(
90
-					'files_sharing.share.showShare',
91
-					[ 'token' => $this->getToken() ],
92
-				),
93
-			);
94
-			throw $e;
95
-		} catch (\Exception $e) {
96
-			$class = get_class($e);
97
-			$msg = $e->getMessage();
98
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
99
-			throw new ServiceUnavailable("$class: $msg");
100
-		}
101
-	}
102
-
103
-	/**
104
-	 * Extract token from request url
105
-	 * @throws NotFound
106
-	 */
107
-	private function getToken(): string {
108
-		$path = $this->request->getPathInfo() ?: '';
109
-		// ['', 'dav', 'files', 'token']
110
-		$splittedPath = explode('/', $path);
111
-
112
-		if (count($splittedPath) < 4 || $splittedPath[3] === '') {
113
-			throw new NotFound();
114
-		}
115
-
116
-		return $splittedPath[3];
117
-	}
118
-
119
-	/**
120
-	 * Check token validity
121
-	 *
122
-	 * @throws NotFound
123
-	 * @throws NotAuthenticated
124
-	 */
125
-	private function checkToken(): array {
126
-		$token = $this->getToken();
127
-
128
-		try {
129
-			/** @var IShare $share */
130
-			$share = $this->shareManager->getShareByToken($token);
131
-		} catch (ShareNotFound $e) {
132
-			$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
133
-			throw new NotFound();
134
-		}
135
-
136
-		$this->share = $share;
137
-		\OC_User::setIncognitoMode(true);
138
-
139
-		// If already authenticated
140
-		if ($this->isShareInSession($share)) {
141
-			return [true, $this->principalPrefix . $token];
142
-		}
143
-
144
-		// If the share is protected but user is not authenticated
145
-		if ($share->getPassword() !== null) {
146
-			$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
147
-			throw new NotAuthenticated();
148
-		}
149
-
150
-		return [true, $this->principalPrefix . $token];
151
-	}
152
-
153
-	/**
154
-	 * Validates a username and password
155
-	 *
156
-	 * This method should return true or false depending on if login
157
-	 * succeeded.
158
-	 *
159
-	 * @param string $username
160
-	 * @param string $password
161
-	 *
162
-	 * @return bool
163
-	 * @throws NotAuthenticated
164
-	 */
165
-	protected function validateUserPass($username, $password) {
166
-		$this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);
167
-
168
-		try {
169
-			$share = $this->getShare();
170
-		} catch (ShareNotFound $e) {
171
-			$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
172
-			return false;
173
-		}
174
-
175
-		\OC_User::setIncognitoMode(true);
176
-
177
-		// check if the share is password protected
178
-		if ($share->getPassword() !== null) {
179
-			if ($share->getShareType() === IShare::TYPE_LINK
180
-				|| $share->getShareType() === IShare::TYPE_EMAIL
181
-				|| $share->getShareType() === IShare::TYPE_CIRCLE) {
182
-				// Validate password if provided
183
-				if ($this->shareManager->checkPassword($share, $password)) {
184
-					// If not set, set authenticated session cookie
185
-					if (!$this->isShareInSession($share)) {
186
-						$this->addShareToSession($share);
187
-					}
188
-					return true;
189
-				}
190
-
191
-				// We are already authenticated for this share in the session
192
-				if ($this->isShareInSession($share)) {
193
-					return true;
194
-				}
195
-
196
-				if (in_array('XMLHttpRequest', explode(',', $this->request->getHeader('X-Requested-With')))) {
197
-					// do not re-authenticate over ajax, use dummy auth name to prevent browser popup
198
-					http_response_code(401);
199
-					header('WWW-Authenticate: DummyBasic realm="' . $this->realm . '"');
200
-					throw new NotAuthenticated('Cannot authenticate over ajax calls');
201
-				}
202
-
203
-				$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
204
-				return false;
205
-			} elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
206
-				return true;
207
-			}
208
-
209
-			$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
210
-			return false;
211
-		}
212
-
213
-		return true;
214
-	}
215
-
216
-	public function getShare(): IShare {
217
-		$token = $this->getToken();
218
-
219
-		if ($this->share === null) {
220
-			$share = $this->shareManager->getShareByToken($token);
221
-			$this->share = $share;
222
-		}
223
-
224
-		return $this->share;
225
-	}
226
-
227
-	private function addShareToSession(IShare $share): void {
228
-		$allowedShareIds = $this->session->get(self::DAV_AUTHENTICATED) ?? [];
229
-		if (!is_array($allowedShareIds)) {
230
-			$allowedShareIds = [];
231
-		}
232
-
233
-		$allowedShareIds[] = $share->getId();
234
-		$this->session->set(self::DAV_AUTHENTICATED, $allowedShareIds);
235
-	}
236
-
237
-	private function isShareInSession(IShare $share): bool {
238
-		if (!$this->session->exists(self::DAV_AUTHENTICATED)) {
239
-			return false;
240
-		}
241
-
242
-		$allowedShareIds = $this->session->get(self::DAV_AUTHENTICATED);
243
-		if (!is_array($allowedShareIds)) {
244
-			return false;
245
-		}
246
-
247
-		return in_array($share->getId(), $allowedShareIds);
248
-	}
39
+    private const BRUTEFORCE_ACTION = 'public_dav_auth';
40
+    public const DAV_AUTHENTICATED = 'public_link_authenticated';
41
+
42
+    private ?IShare $share = null;
43
+
44
+    public function __construct(
45
+        private IRequest $request,
46
+        private IManager $shareManager,
47
+        private ISession $session,
48
+        private IThrottler $throttler,
49
+        private LoggerInterface $logger,
50
+        private IURLGenerator $urlGenerator,
51
+    ) {
52
+        // setup realm
53
+        $defaults = new Defaults();
54
+        $this->realm = $defaults->getName();
55
+    }
56
+
57
+    /**
58
+     * @throws NotAuthenticated
59
+     * @throws MaxDelayReached
60
+     * @throws ServiceUnavailable
61
+     */
62
+    public function check(RequestInterface $request, ResponseInterface $response): array {
63
+        try {
64
+            $this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);
65
+
66
+            if (count($_COOKIE) > 0 && !$this->request->passesStrictCookieCheck() && $this->getShare()->getPassword() !== null) {
67
+                throw new PreconditionFailed('Strict cookie check failed');
68
+            }
69
+
70
+            $auth = new HTTP\Auth\Basic(
71
+                $this->realm,
72
+                $request,
73
+                $response
74
+            );
75
+
76
+            $userpass = $auth->getCredentials();
77
+            // If authentication provided, checking its validity
78
+            if ($userpass && !$this->validateUserPass($userpass[0], $userpass[1])) {
79
+                return [false, 'Username or password was incorrect'];
80
+            }
81
+
82
+            return $this->checkToken();
83
+        } catch (NotAuthenticated|MaxDelayReached $e) {
84
+            $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
85
+            throw $e;
86
+        } catch (PreconditionFailed $e) {
87
+            $response->setHeader(
88
+                'Location',
89
+                $this->urlGenerator->linkToRoute(
90
+                    'files_sharing.share.showShare',
91
+                    [ 'token' => $this->getToken() ],
92
+                ),
93
+            );
94
+            throw $e;
95
+        } catch (\Exception $e) {
96
+            $class = get_class($e);
97
+            $msg = $e->getMessage();
98
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
99
+            throw new ServiceUnavailable("$class: $msg");
100
+        }
101
+    }
102
+
103
+    /**
104
+     * Extract token from request url
105
+     * @throws NotFound
106
+     */
107
+    private function getToken(): string {
108
+        $path = $this->request->getPathInfo() ?: '';
109
+        // ['', 'dav', 'files', 'token']
110
+        $splittedPath = explode('/', $path);
111
+
112
+        if (count($splittedPath) < 4 || $splittedPath[3] === '') {
113
+            throw new NotFound();
114
+        }
115
+
116
+        return $splittedPath[3];
117
+    }
118
+
119
+    /**
120
+     * Check token validity
121
+     *
122
+     * @throws NotFound
123
+     * @throws NotAuthenticated
124
+     */
125
+    private function checkToken(): array {
126
+        $token = $this->getToken();
127
+
128
+        try {
129
+            /** @var IShare $share */
130
+            $share = $this->shareManager->getShareByToken($token);
131
+        } catch (ShareNotFound $e) {
132
+            $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
133
+            throw new NotFound();
134
+        }
135
+
136
+        $this->share = $share;
137
+        \OC_User::setIncognitoMode(true);
138
+
139
+        // If already authenticated
140
+        if ($this->isShareInSession($share)) {
141
+            return [true, $this->principalPrefix . $token];
142
+        }
143
+
144
+        // If the share is protected but user is not authenticated
145
+        if ($share->getPassword() !== null) {
146
+            $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
147
+            throw new NotAuthenticated();
148
+        }
149
+
150
+        return [true, $this->principalPrefix . $token];
151
+    }
152
+
153
+    /**
154
+     * Validates a username and password
155
+     *
156
+     * This method should return true or false depending on if login
157
+     * succeeded.
158
+     *
159
+     * @param string $username
160
+     * @param string $password
161
+     *
162
+     * @return bool
163
+     * @throws NotAuthenticated
164
+     */
165
+    protected function validateUserPass($username, $password) {
166
+        $this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);
167
+
168
+        try {
169
+            $share = $this->getShare();
170
+        } catch (ShareNotFound $e) {
171
+            $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
172
+            return false;
173
+        }
174
+
175
+        \OC_User::setIncognitoMode(true);
176
+
177
+        // check if the share is password protected
178
+        if ($share->getPassword() !== null) {
179
+            if ($share->getShareType() === IShare::TYPE_LINK
180
+                || $share->getShareType() === IShare::TYPE_EMAIL
181
+                || $share->getShareType() === IShare::TYPE_CIRCLE) {
182
+                // Validate password if provided
183
+                if ($this->shareManager->checkPassword($share, $password)) {
184
+                    // If not set, set authenticated session cookie
185
+                    if (!$this->isShareInSession($share)) {
186
+                        $this->addShareToSession($share);
187
+                    }
188
+                    return true;
189
+                }
190
+
191
+                // We are already authenticated for this share in the session
192
+                if ($this->isShareInSession($share)) {
193
+                    return true;
194
+                }
195
+
196
+                if (in_array('XMLHttpRequest', explode(',', $this->request->getHeader('X-Requested-With')))) {
197
+                    // do not re-authenticate over ajax, use dummy auth name to prevent browser popup
198
+                    http_response_code(401);
199
+                    header('WWW-Authenticate: DummyBasic realm="' . $this->realm . '"');
200
+                    throw new NotAuthenticated('Cannot authenticate over ajax calls');
201
+                }
202
+
203
+                $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
204
+                return false;
205
+            } elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
206
+                return true;
207
+            }
208
+
209
+            $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
210
+            return false;
211
+        }
212
+
213
+        return true;
214
+    }
215
+
216
+    public function getShare(): IShare {
217
+        $token = $this->getToken();
218
+
219
+        if ($this->share === null) {
220
+            $share = $this->shareManager->getShareByToken($token);
221
+            $this->share = $share;
222
+        }
223
+
224
+        return $this->share;
225
+    }
226
+
227
+    private function addShareToSession(IShare $share): void {
228
+        $allowedShareIds = $this->session->get(self::DAV_AUTHENTICATED) ?? [];
229
+        if (!is_array($allowedShareIds)) {
230
+            $allowedShareIds = [];
231
+        }
232
+
233
+        $allowedShareIds[] = $share->getId();
234
+        $this->session->set(self::DAV_AUTHENTICATED, $allowedShareIds);
235
+    }
236
+
237
+    private function isShareInSession(IShare $share): bool {
238
+        if (!$this->session->exists(self::DAV_AUTHENTICATED)) {
239
+            return false;
240
+        }
241
+
242
+        $allowedShareIds = $this->session->get(self::DAV_AUTHENTICATED);
243
+        if (!is_array($allowedShareIds)) {
244
+            return false;
245
+        }
246
+
247
+        return in_array($share->getId(), $allowedShareIds);
248
+    }
249 249
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -80,7 +80,7 @@  discard block
 block discarded – undo
80 80
 			}
81 81
 
82 82
 			return $this->checkToken();
83
-		} catch (NotAuthenticated|MaxDelayReached $e) {
83
+		} catch (NotAuthenticated | MaxDelayReached $e) {
84 84
 			$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
85 85
 			throw $e;
86 86
 		} catch (PreconditionFailed $e) {
@@ -88,7 +88,7 @@  discard block
 block discarded – undo
88 88
 				'Location',
89 89
 				$this->urlGenerator->linkToRoute(
90 90
 					'files_sharing.share.showShare',
91
-					[ 'token' => $this->getToken() ],
91
+					['token' => $this->getToken()],
92 92
 				),
93 93
 			);
94 94
 			throw $e;
@@ -138,7 +138,7 @@  discard block
 block discarded – undo
138 138
 
139 139
 		// If already authenticated
140 140
 		if ($this->isShareInSession($share)) {
141
-			return [true, $this->principalPrefix . $token];
141
+			return [true, $this->principalPrefix.$token];
142 142
 		}
143 143
 
144 144
 		// If the share is protected but user is not authenticated
@@ -147,7 +147,7 @@  discard block
 block discarded – undo
147 147
 			throw new NotAuthenticated();
148 148
 		}
149 149
 
150
-		return [true, $this->principalPrefix . $token];
150
+		return [true, $this->principalPrefix.$token];
151 151
 	}
152 152
 
153 153
 	/**
@@ -196,7 +196,7 @@  discard block
 block discarded – undo
196 196
 				if (in_array('XMLHttpRequest', explode(',', $this->request->getHeader('X-Requested-With')))) {
197 197
 					// do not re-authenticate over ajax, use dummy auth name to prevent browser popup
198 198
 					http_response_code(401);
199
-					header('WWW-Authenticate: DummyBasic realm="' . $this->realm . '"');
199
+					header('WWW-Authenticate: DummyBasic realm="'.$this->realm.'"');
200 200
 					throw new NotAuthenticated('Cannot authenticate over ajax calls');
201 201
 				}
202 202
 
Please login to merge, or discard this patch.
apps/dav/tests/unit/Connector/Sabre/PublicAuthTest.php 1 patch
Indentation   +283 added lines, -283 removed lines patch added patch discarded remove patch
@@ -28,357 +28,357 @@
 block discarded – undo
28 28
  */
29 29
 class PublicAuthTest extends \Test\TestCase {
30 30
 
31
-	private ISession&MockObject $session;
32
-	private IRequest&MockObject $request;
33
-	private IManager&MockObject $shareManager;
34
-	private IThrottler&MockObject $throttler;
35
-	private LoggerInterface&MockObject $logger;
36
-	private IURLGenerator&MockObject $urlGenerator;
37
-	private PublicAuth $auth;
38
-
39
-	private bool|string $oldUser;
40
-
41
-	protected function setUp(): void {
42
-		parent::setUp();
43
-
44
-		$this->session = $this->createMock(ISession::class);
45
-		$this->request = $this->createMock(IRequest::class);
46
-		$this->shareManager = $this->createMock(IManager::class);
47
-		$this->throttler = $this->createMock(IThrottler::class);
48
-		$this->logger = $this->createMock(LoggerInterface::class);
49
-		$this->urlGenerator = $this->createMock(IURLGenerator::class);
50
-
51
-		$this->auth = new PublicAuth(
52
-			$this->request,
53
-			$this->shareManager,
54
-			$this->session,
55
-			$this->throttler,
56
-			$this->logger,
57
-			$this->urlGenerator,
58
-		);
59
-
60
-		// Store current user
61
-		$this->oldUser = \OC_User::getUser();
62
-	}
63
-
64
-	protected function tearDown(): void {
65
-		\OC_User::setIncognitoMode(false);
66
-
67
-		// Set old user
68
-		\OC_User::setUserId($this->oldUser);
69
-		if ($this->oldUser !== false) {
70
-			\OC_Util::setupFS($this->oldUser);
71
-		}
72
-
73
-		parent::tearDown();
74
-	}
75
-
76
-	public function testGetToken(): void {
77
-		$this->request->method('getPathInfo')
78
-			->willReturn('/dav/files/GX9HSGQrGE');
79
-
80
-		$result = self::invokePrivate($this->auth, 'getToken');
81
-
82
-		$this->assertSame('GX9HSGQrGE', $result);
83
-	}
84
-
85
-	public function testGetTokenInvalid(): void {
86
-		$this->request->method('getPathInfo')
87
-			->willReturn('/dav/files');
88
-
89
-		$this->expectException(\Sabre\DAV\Exception\NotFound::class);
90
-		self::invokePrivate($this->auth, 'getToken');
91
-	}
92
-
93
-	public function testCheckTokenValidShare(): void {
94
-		$this->request->method('getPathInfo')
95
-			->willReturn('/dav/files/GX9HSGQrGE');
96
-
97
-		$share = $this->createMock(IShare::class);
98
-		$share->method('getPassword')->willReturn(null);
99
-
100
-		$this->shareManager->expects($this->once())
101
-			->method('getShareByToken')
102
-			->with('GX9HSGQrGE')
103
-			->willReturn($share);
104
-
105
-		$result = self::invokePrivate($this->auth, 'checkToken');
106
-		$this->assertSame([true, 'principals/GX9HSGQrGE'], $result);
107
-	}
31
+    private ISession&MockObject $session;
32
+    private IRequest&MockObject $request;
33
+    private IManager&MockObject $shareManager;
34
+    private IThrottler&MockObject $throttler;
35
+    private LoggerInterface&MockObject $logger;
36
+    private IURLGenerator&MockObject $urlGenerator;
37
+    private PublicAuth $auth;
38
+
39
+    private bool|string $oldUser;
40
+
41
+    protected function setUp(): void {
42
+        parent::setUp();
43
+
44
+        $this->session = $this->createMock(ISession::class);
45
+        $this->request = $this->createMock(IRequest::class);
46
+        $this->shareManager = $this->createMock(IManager::class);
47
+        $this->throttler = $this->createMock(IThrottler::class);
48
+        $this->logger = $this->createMock(LoggerInterface::class);
49
+        $this->urlGenerator = $this->createMock(IURLGenerator::class);
50
+
51
+        $this->auth = new PublicAuth(
52
+            $this->request,
53
+            $this->shareManager,
54
+            $this->session,
55
+            $this->throttler,
56
+            $this->logger,
57
+            $this->urlGenerator,
58
+        );
59
+
60
+        // Store current user
61
+        $this->oldUser = \OC_User::getUser();
62
+    }
63
+
64
+    protected function tearDown(): void {
65
+        \OC_User::setIncognitoMode(false);
66
+
67
+        // Set old user
68
+        \OC_User::setUserId($this->oldUser);
69
+        if ($this->oldUser !== false) {
70
+            \OC_Util::setupFS($this->oldUser);
71
+        }
72
+
73
+        parent::tearDown();
74
+    }
75
+
76
+    public function testGetToken(): void {
77
+        $this->request->method('getPathInfo')
78
+            ->willReturn('/dav/files/GX9HSGQrGE');
79
+
80
+        $result = self::invokePrivate($this->auth, 'getToken');
81
+
82
+        $this->assertSame('GX9HSGQrGE', $result);
83
+    }
84
+
85
+    public function testGetTokenInvalid(): void {
86
+        $this->request->method('getPathInfo')
87
+            ->willReturn('/dav/files');
88
+
89
+        $this->expectException(\Sabre\DAV\Exception\NotFound::class);
90
+        self::invokePrivate($this->auth, 'getToken');
91
+    }
92
+
93
+    public function testCheckTokenValidShare(): void {
94
+        $this->request->method('getPathInfo')
95
+            ->willReturn('/dav/files/GX9HSGQrGE');
96
+
97
+        $share = $this->createMock(IShare::class);
98
+        $share->method('getPassword')->willReturn(null);
99
+
100
+        $this->shareManager->expects($this->once())
101
+            ->method('getShareByToken')
102
+            ->with('GX9HSGQrGE')
103
+            ->willReturn($share);
104
+
105
+        $result = self::invokePrivate($this->auth, 'checkToken');
106
+        $this->assertSame([true, 'principals/GX9HSGQrGE'], $result);
107
+    }
108 108
 
109
-	public function testCheckTokenInvalidShare(): void {
110
-		$this->request->method('getPathInfo')
111
-			->willReturn('/dav/files/GX9HSGQrGE');
109
+    public function testCheckTokenInvalidShare(): void {
110
+        $this->request->method('getPathInfo')
111
+            ->willReturn('/dav/files/GX9HSGQrGE');
112 112
 
113
-		$this->shareManager
114
-			->expects($this->once())
115
-			->method('getShareByToken')
116
-			->with('GX9HSGQrGE')
117
-			->willThrowException(new ShareNotFound());
118
-
119
-		$this->expectException(\Sabre\DAV\Exception\NotFound::class);
120
-		self::invokePrivate($this->auth, 'checkToken');
121
-	}
122
-
123
-	public function testCheckTokenAlreadyAuthenticated(): void {
124
-		$this->request->method('getPathInfo')
125
-			->willReturn('/dav/files/GX9HSGQrGE');
113
+        $this->shareManager
114
+            ->expects($this->once())
115
+            ->method('getShareByToken')
116
+            ->with('GX9HSGQrGE')
117
+            ->willThrowException(new ShareNotFound());
118
+
119
+        $this->expectException(\Sabre\DAV\Exception\NotFound::class);
120
+        self::invokePrivate($this->auth, 'checkToken');
121
+    }
122
+
123
+    public function testCheckTokenAlreadyAuthenticated(): void {
124
+        $this->request->method('getPathInfo')
125
+            ->willReturn('/dav/files/GX9HSGQrGE');
126 126
 
127
-		$share = $this->createMock(IShare::class);
128
-		$share->method('getShareType')->willReturn(42);
127
+        $share = $this->createMock(IShare::class);
128
+        $share->method('getShareType')->willReturn(42);
129 129
 
130
-		$this->shareManager->expects($this->once())
131
-			->method('getShareByToken')
132
-			->with('GX9HSGQrGE')
133
-			->willReturn($share);
130
+        $this->shareManager->expects($this->once())
131
+            ->method('getShareByToken')
132
+            ->with('GX9HSGQrGE')
133
+            ->willReturn($share);
134 134
 
135
-		$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
136
-		$this->session->method('get')->with('public_link_authenticated')->willReturn('42');
135
+        $this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
136
+        $this->session->method('get')->with('public_link_authenticated')->willReturn('42');
137 137
 
138
-		$result = self::invokePrivate($this->auth, 'checkToken');
139
-		$this->assertSame([true, 'principals/GX9HSGQrGE'], $result);
140
-	}
138
+        $result = self::invokePrivate($this->auth, 'checkToken');
139
+        $this->assertSame([true, 'principals/GX9HSGQrGE'], $result);
140
+    }
141 141
 
142
-	public function testCheckTokenPasswordNotAuthenticated(): void {
143
-		$this->request->method('getPathInfo')
144
-			->willReturn('/dav/files/GX9HSGQrGE');
142
+    public function testCheckTokenPasswordNotAuthenticated(): void {
143
+        $this->request->method('getPathInfo')
144
+            ->willReturn('/dav/files/GX9HSGQrGE');
145 145
 
146
-		$share = $this->createMock(IShare::class);
147
-		$share->method('getPassword')->willReturn('password');
148
-		$share->method('getShareType')->willReturn(42);
146
+        $share = $this->createMock(IShare::class);
147
+        $share->method('getPassword')->willReturn('password');
148
+        $share->method('getShareType')->willReturn(42);
149 149
 
150
-		$this->shareManager->expects($this->once())
151
-			->method('getShareByToken')
152
-			->with('GX9HSGQrGE')
153
-			->willReturn($share);
150
+        $this->shareManager->expects($this->once())
151
+            ->method('getShareByToken')
152
+            ->with('GX9HSGQrGE')
153
+            ->willReturn($share);
154 154
 
155
-		$this->session->method('exists')->with('public_link_authenticated')->willReturn(false);
155
+        $this->session->method('exists')->with('public_link_authenticated')->willReturn(false);
156 156
 
157
-		$this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class);
158
-		self::invokePrivate($this->auth, 'checkToken');
159
-	}
157
+        $this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class);
158
+        self::invokePrivate($this->auth, 'checkToken');
159
+    }
160 160
 
161
-	public function testCheckTokenPasswordAuthenticatedWrongShare(): void {
162
-		$this->request->method('getPathInfo')
163
-			->willReturn('/dav/files/GX9HSGQrGE');
161
+    public function testCheckTokenPasswordAuthenticatedWrongShare(): void {
162
+        $this->request->method('getPathInfo')
163
+            ->willReturn('/dav/files/GX9HSGQrGE');
164 164
 
165
-		$share = $this->createMock(IShare::class);
166
-		$share->method('getPassword')->willReturn('password');
167
-		$share->method('getShareType')->willReturn(42);
165
+        $share = $this->createMock(IShare::class);
166
+        $share->method('getPassword')->willReturn('password');
167
+        $share->method('getShareType')->willReturn(42);
168 168
 
169
-		$this->shareManager->expects($this->once())
170
-			->method('getShareByToken')
171
-			->with('GX9HSGQrGE')
172
-			->willReturn($share);
169
+        $this->shareManager->expects($this->once())
170
+            ->method('getShareByToken')
171
+            ->with('GX9HSGQrGE')
172
+            ->willReturn($share);
173 173
 
174
-		$this->session->method('exists')->with('public_link_authenticated')->willReturn(false);
175
-		$this->session->method('get')->with('public_link_authenticated')->willReturn('43');
174
+        $this->session->method('exists')->with('public_link_authenticated')->willReturn(false);
175
+        $this->session->method('get')->with('public_link_authenticated')->willReturn('43');
176 176
 
177
-		$this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class);
178
-		self::invokePrivate($this->auth, 'checkToken');
179
-	}
177
+        $this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class);
178
+        self::invokePrivate($this->auth, 'checkToken');
179
+    }
180 180
 
181
-	public function testNoShare(): void {
182
-		$this->request->method('getPathInfo')
183
-			->willReturn('/dav/files/GX9HSGQrGE');
181
+    public function testNoShare(): void {
182
+        $this->request->method('getPathInfo')
183
+            ->willReturn('/dav/files/GX9HSGQrGE');
184 184
 
185
-		$this->shareManager->expects($this->once())
186
-			->method('getShareByToken')
187
-			->with('GX9HSGQrGE')
188
-			->willThrowException(new ShareNotFound());
185
+        $this->shareManager->expects($this->once())
186
+            ->method('getShareByToken')
187
+            ->with('GX9HSGQrGE')
188
+            ->willThrowException(new ShareNotFound());
189 189
 
190
-		$result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
190
+        $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
191 191
 
192
-		$this->assertFalse($result);
193
-	}
192
+        $this->assertFalse($result);
193
+    }
194 194
 
195
-	public function testShareNoPassword(): void {
196
-		$this->request->method('getPathInfo')
197
-			->willReturn('/dav/files/GX9HSGQrGE');
195
+    public function testShareNoPassword(): void {
196
+        $this->request->method('getPathInfo')
197
+            ->willReturn('/dav/files/GX9HSGQrGE');
198 198
 
199
-		$share = $this->createMock(IShare::class);
200
-		$share->method('getPassword')->willReturn(null);
199
+        $share = $this->createMock(IShare::class);
200
+        $share->method('getPassword')->willReturn(null);
201 201
 
202
-		$this->shareManager->expects($this->once())
203
-			->method('getShareByToken')
204
-			->with('GX9HSGQrGE')
205
-			->willReturn($share);
202
+        $this->shareManager->expects($this->once())
203
+            ->method('getShareByToken')
204
+            ->with('GX9HSGQrGE')
205
+            ->willReturn($share);
206 206
 
207
-		$result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
207
+        $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
208 208
 
209
-		$this->assertTrue($result);
210
-	}
209
+        $this->assertTrue($result);
210
+    }
211 211
 
212
-	public function testSharePasswordFancyShareType(): void {
213
-		$this->request->method('getPathInfo')
214
-			->willReturn('/dav/files/GX9HSGQrGE');
212
+    public function testSharePasswordFancyShareType(): void {
213
+        $this->request->method('getPathInfo')
214
+            ->willReturn('/dav/files/GX9HSGQrGE');
215 215
 
216
-		$share = $this->createMock(IShare::class);
217
-		$share->method('getPassword')->willReturn('password');
218
-		$share->method('getShareType')->willReturn(42);
216
+        $share = $this->createMock(IShare::class);
217
+        $share->method('getPassword')->willReturn('password');
218
+        $share->method('getShareType')->willReturn(42);
219 219
 
220
-		$this->shareManager->expects($this->once())
221
-			->method('getShareByToken')
222
-			->with('GX9HSGQrGE')
223
-			->willReturn($share);
220
+        $this->shareManager->expects($this->once())
221
+            ->method('getShareByToken')
222
+            ->with('GX9HSGQrGE')
223
+            ->willReturn($share);
224 224
 
225
-		$result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
225
+        $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
226 226
 
227
-		$this->assertFalse($result);
228
-	}
227
+        $this->assertFalse($result);
228
+    }
229 229
 
230 230
 
231
-	public function testSharePasswordRemote(): void {
232
-		$this->request->method('getPathInfo')
233
-			->willReturn('/dav/files/GX9HSGQrGE');
231
+    public function testSharePasswordRemote(): void {
232
+        $this->request->method('getPathInfo')
233
+            ->willReturn('/dav/files/GX9HSGQrGE');
234 234
 
235
-		$share = $this->createMock(IShare::class);
236
-		$share->method('getPassword')->willReturn('password');
237
-		$share->method('getShareType')->willReturn(IShare::TYPE_REMOTE);
235
+        $share = $this->createMock(IShare::class);
236
+        $share->method('getPassword')->willReturn('password');
237
+        $share->method('getShareType')->willReturn(IShare::TYPE_REMOTE);
238 238
 
239
-		$this->shareManager->expects($this->once())
240
-			->method('getShareByToken')
241
-			->with('GX9HSGQrGE')
242
-			->willReturn($share);
239
+        $this->shareManager->expects($this->once())
240
+            ->method('getShareByToken')
241
+            ->with('GX9HSGQrGE')
242
+            ->willReturn($share);
243 243
 
244
-		$result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
244
+        $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
245 245
 
246
-		$this->assertTrue($result);
247
-	}
246
+        $this->assertTrue($result);
247
+    }
248 248
 
249
-	public function testSharePasswordLinkValidPassword(): void {
250
-		$this->request->method('getPathInfo')
251
-			->willReturn('/dav/files/GX9HSGQrGE');
249
+    public function testSharePasswordLinkValidPassword(): void {
250
+        $this->request->method('getPathInfo')
251
+            ->willReturn('/dav/files/GX9HSGQrGE');
252 252
 
253
-		$share = $this->createMock(IShare::class);
254
-		$share->method('getPassword')->willReturn('password');
255
-		$share->method('getShareType')->willReturn(IShare::TYPE_LINK);
253
+        $share = $this->createMock(IShare::class);
254
+        $share->method('getPassword')->willReturn('password');
255
+        $share->method('getShareType')->willReturn(IShare::TYPE_LINK);
256 256
 
257
-		$this->shareManager->expects($this->once())
258
-			->method('getShareByToken')
259
-			->with('GX9HSGQrGE')
260
-			->willReturn($share);
257
+        $this->shareManager->expects($this->once())
258
+            ->method('getShareByToken')
259
+            ->with('GX9HSGQrGE')
260
+            ->willReturn($share);
261 261
 
262
-		$this->shareManager->expects($this->once())
263
-			->method('checkPassword')->with(
264
-				$this->equalTo($share),
265
-				$this->equalTo('password')
266
-			)->willReturn(true);
262
+        $this->shareManager->expects($this->once())
263
+            ->method('checkPassword')->with(
264
+                $this->equalTo($share),
265
+                $this->equalTo('password')
266
+            )->willReturn(true);
267 267
 
268
-		$result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
268
+        $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
269 269
 
270
-		$this->assertTrue($result);
271
-	}
270
+        $this->assertTrue($result);
271
+    }
272 272
 
273
-	public function testSharePasswordMailValidPassword(): void {
274
-		$this->request->method('getPathInfo')
275
-			->willReturn('/dav/files/GX9HSGQrGE');
273
+    public function testSharePasswordMailValidPassword(): void {
274
+        $this->request->method('getPathInfo')
275
+            ->willReturn('/dav/files/GX9HSGQrGE');
276 276
 
277
-		$share = $this->createMock(IShare::class);
278
-		$share->method('getPassword')->willReturn('password');
279
-		$share->method('getShareType')->willReturn(IShare::TYPE_EMAIL);
277
+        $share = $this->createMock(IShare::class);
278
+        $share->method('getPassword')->willReturn('password');
279
+        $share->method('getShareType')->willReturn(IShare::TYPE_EMAIL);
280 280
 
281
-		$this->shareManager->expects($this->once())
282
-			->method('getShareByToken')
283
-			->with('GX9HSGQrGE')
284
-			->willReturn($share);
281
+        $this->shareManager->expects($this->once())
282
+            ->method('getShareByToken')
283
+            ->with('GX9HSGQrGE')
284
+            ->willReturn($share);
285 285
 
286
-		$this->shareManager->expects($this->once())
287
-			->method('checkPassword')->with(
288
-				$this->equalTo($share),
289
-				$this->equalTo('password')
290
-			)->willReturn(true);
286
+        $this->shareManager->expects($this->once())
287
+            ->method('checkPassword')->with(
288
+                $this->equalTo($share),
289
+                $this->equalTo('password')
290
+            )->willReturn(true);
291 291
 
292
-		$result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
292
+        $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
293 293
 
294
-		$this->assertTrue($result);
295
-	}
294
+        $this->assertTrue($result);
295
+    }
296 296
 
297
-	public function testInvalidSharePasswordLinkValidSession(): void {
298
-		$this->request->method('getPathInfo')
299
-			->willReturn('/dav/files/GX9HSGQrGE');
297
+    public function testInvalidSharePasswordLinkValidSession(): void {
298
+        $this->request->method('getPathInfo')
299
+            ->willReturn('/dav/files/GX9HSGQrGE');
300 300
 
301
-		$share = $this->createMock(IShare::class);
302
-		$share->method('getPassword')->willReturn('password');
303
-		$share->method('getShareType')->willReturn(IShare::TYPE_LINK);
304
-		$share->method('getId')->willReturn('42');
301
+        $share = $this->createMock(IShare::class);
302
+        $share->method('getPassword')->willReturn('password');
303
+        $share->method('getShareType')->willReturn(IShare::TYPE_LINK);
304
+        $share->method('getId')->willReturn('42');
305 305
 
306
-		$this->shareManager->expects($this->once())
307
-			->method('getShareByToken')
308
-			->with('GX9HSGQrGE')
309
-			->willReturn($share);
306
+        $this->shareManager->expects($this->once())
307
+            ->method('getShareByToken')
308
+            ->with('GX9HSGQrGE')
309
+            ->willReturn($share);
310 310
 
311
-		$this->shareManager->expects($this->once())
312
-			->method('checkPassword')
313
-			->with(
314
-				$this->equalTo($share),
315
-				$this->equalTo('password')
316
-			)->willReturn(false);
311
+        $this->shareManager->expects($this->once())
312
+            ->method('checkPassword')
313
+            ->with(
314
+                $this->equalTo($share),
315
+                $this->equalTo('password')
316
+            )->willReturn(false);
317 317
 
318
-		$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
319
-		$this->session->method('get')->with('public_link_authenticated')->willReturn(['42']);
318
+        $this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
319
+        $this->session->method('get')->with('public_link_authenticated')->willReturn(['42']);
320 320
 
321
-		$result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
321
+        $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
322 322
 
323
-		$this->assertTrue($result);
324
-	}
323
+        $this->assertTrue($result);
324
+    }
325 325
 
326
-	public function testSharePasswordLinkInvalidSession(): void {
327
-		$this->request->method('getPathInfo')
328
-			->willReturn('/dav/files/GX9HSGQrGE');
326
+    public function testSharePasswordLinkInvalidSession(): void {
327
+        $this->request->method('getPathInfo')
328
+            ->willReturn('/dav/files/GX9HSGQrGE');
329 329
 
330
-		$share = $this->createMock(IShare::class);
331
-		$share->method('getPassword')->willReturn('password');
332
-		$share->method('getShareType')->willReturn(IShare::TYPE_LINK);
333
-		$share->method('getId')->willReturn('42');
330
+        $share = $this->createMock(IShare::class);
331
+        $share->method('getPassword')->willReturn('password');
332
+        $share->method('getShareType')->willReturn(IShare::TYPE_LINK);
333
+        $share->method('getId')->willReturn('42');
334 334
 
335
-		$this->shareManager->expects($this->once())
336
-			->method('getShareByToken')
337
-			->with('GX9HSGQrGE')
338
-			->willReturn($share);
335
+        $this->shareManager->expects($this->once())
336
+            ->method('getShareByToken')
337
+            ->with('GX9HSGQrGE')
338
+            ->willReturn($share);
339 339
 
340
-		$this->shareManager->expects($this->once())
341
-			->method('checkPassword')
342
-			->with(
343
-				$this->equalTo($share),
344
-				$this->equalTo('password')
345
-			)->willReturn(false);
340
+        $this->shareManager->expects($this->once())
341
+            ->method('checkPassword')
342
+            ->with(
343
+                $this->equalTo($share),
344
+                $this->equalTo('password')
345
+            )->willReturn(false);
346 346
 
347
-		$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
348
-		$this->session->method('get')->with('public_link_authenticated')->willReturn('43');
347
+        $this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
348
+        $this->session->method('get')->with('public_link_authenticated')->willReturn('43');
349 349
 
350
-		$result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
350
+        $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
351 351
 
352
-		$this->assertFalse($result);
353
-	}
352
+        $this->assertFalse($result);
353
+    }
354 354
 
355 355
 
356
-	public function testSharePasswordMailInvalidSession(): void {
357
-		$this->request->method('getPathInfo')
358
-			->willReturn('/dav/files/GX9HSGQrGE');
356
+    public function testSharePasswordMailInvalidSession(): void {
357
+        $this->request->method('getPathInfo')
358
+            ->willReturn('/dav/files/GX9HSGQrGE');
359 359
 
360
-		$share = $this->createMock(IShare::class);
361
-		$share->method('getPassword')->willReturn('password');
362
-		$share->method('getShareType')->willReturn(IShare::TYPE_EMAIL);
363
-		$share->method('getId')->willReturn('42');
360
+        $share = $this->createMock(IShare::class);
361
+        $share->method('getPassword')->willReturn('password');
362
+        $share->method('getShareType')->willReturn(IShare::TYPE_EMAIL);
363
+        $share->method('getId')->willReturn('42');
364 364
 
365
-		$this->shareManager->expects($this->once())
366
-			->method('getShareByToken')
367
-			->with('GX9HSGQrGE')
368
-			->willReturn($share);
365
+        $this->shareManager->expects($this->once())
366
+            ->method('getShareByToken')
367
+            ->with('GX9HSGQrGE')
368
+            ->willReturn($share);
369 369
 
370
-		$this->shareManager->expects($this->once())
371
-			->method('checkPassword')
372
-			->with(
373
-				$this->equalTo($share),
374
-				$this->equalTo('password')
375
-			)->willReturn(false);
370
+        $this->shareManager->expects($this->once())
371
+            ->method('checkPassword')
372
+            ->with(
373
+                $this->equalTo($share),
374
+                $this->equalTo('password')
375
+            )->willReturn(false);
376 376
 
377
-		$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
378
-		$this->session->method('get')->with('public_link_authenticated')->willReturn('43');
377
+        $this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
378
+        $this->session->method('get')->with('public_link_authenticated')->willReturn('43');
379 379
 
380
-		$result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
380
+        $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
381 381
 
382
-		$this->assertFalse($result);
383
-	}
382
+        $this->assertFalse($result);
383
+    }
384 384
 }
Please login to merge, or discard this patch.
apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php 1 patch
Indentation   +149 added lines, -149 removed lines patch added patch discarded remove patch
@@ -38,153 +38,153 @@
 block discarded – undo
38 38
  * @package OCA\FederatedFileSharing\Controller
39 39
  */
40 40
 class MountPublicLinkController extends Controller {
41
-	/**
42
-	 * MountPublicLinkController constructor.
43
-	 */
44
-	public function __construct(
45
-		string $appName,
46
-		IRequest $request,
47
-		private FederatedShareProvider $federatedShareProvider,
48
-		private IManager $shareManager,
49
-		private AddressHandler $addressHandler,
50
-		private ISession $session,
51
-		private IL10N $l,
52
-		private IUserSession $userSession,
53
-		private IClientService $clientService,
54
-		private ICloudIdManager $cloudIdManager,
55
-		private LoggerInterface $logger,
56
-	) {
57
-		parent::__construct($appName, $request);
58
-	}
59
-
60
-	/**
61
-	 * send federated share to a user of a public link
62
-	 *
63
-	 * @param string $shareWith Username to share with
64
-	 * @param string $token Token of the share
65
-	 * @param string $password Password of the share
66
-	 * @return JSONResponse<Http::STATUS_OK, array{remoteUrl: string}, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST, array{message: string}, array{}>
67
-	 *
68
-	 * 200: Remote URL returned
69
-	 * 400: Creating share is not possible
70
-	 */
71
-	#[NoCSRFRequired]
72
-	#[PublicPage]
73
-	#[BruteForceProtection(action: 'publicLink2FederatedShare')]
74
-	#[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
75
-	public function createFederatedShare($shareWith, $token, $password = '') {
76
-		if (!$this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
77
-			return new JSONResponse(
78
-				['message' => 'This server doesn\'t support outgoing federated shares'],
79
-				Http::STATUS_BAD_REQUEST
80
-			);
81
-		}
82
-
83
-		try {
84
-			[, $server] = $this->addressHandler->splitUserRemote($shareWith);
85
-			$share = $this->shareManager->getShareByToken($token);
86
-		} catch (HintException $e) {
87
-			$response = new JSONResponse(['message' => $e->getHint()], Http::STATUS_BAD_REQUEST);
88
-			$response->throttle();
89
-			return $response;
90
-		}
91
-
92
-		// make sure that user is authenticated in case of a password protected link
93
-		$allowedShareIds = $this->session->get(PublicAuth::DAV_AUTHENTICATED);
94
-		if (!is_array($allowedShareIds)) {
95
-			$allowedShareIds = [];
96
-		}
97
-
98
-		$authenticated = in_array($share->getId(), $allowedShareIds)
99
-			|| $this->shareManager->checkPassword($share, $password);
100
-
101
-		$storedPassword = $share->getPassword();
102
-		if (!empty($storedPassword) && !$authenticated) {
103
-			$response = new JSONResponse(
104
-				['message' => 'No permission to access the share'],
105
-				Http::STATUS_BAD_REQUEST
106
-			);
107
-			$response->throttle();
108
-			return $response;
109
-		}
110
-
111
-		if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
112
-			$response = new JSONResponse(
113
-				['message' => 'Mounting file drop not supported'],
114
-				Http::STATUS_BAD_REQUEST
115
-			);
116
-			$response->throttle();
117
-			return $response;
118
-		}
119
-
120
-		$share->setSharedWith($shareWith);
121
-		$share->setShareType(IShare::TYPE_REMOTE);
122
-
123
-		try {
124
-			$this->federatedShareProvider->create($share);
125
-		} catch (\Exception $e) {
126
-			$this->logger->warning($e->getMessage(), [
127
-				'app' => 'federatedfilesharing',
128
-				'exception' => $e,
129
-			]);
130
-			return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
131
-		}
132
-
133
-		return new JSONResponse(['remoteUrl' => $server]);
134
-	}
135
-
136
-	/**
137
-	 * ask other server to get a federated share
138
-	 *
139
-	 * @param string $token
140
-	 * @param string $remote
141
-	 * @param string $password
142
-	 * @param string $owner (only for legacy reasons, can be removed with legacyMountPublicLink())
143
-	 * @param string $ownerDisplayName (only for legacy reasons, can be removed with legacyMountPublicLink())
144
-	 * @param string $name (only for legacy reasons, can be removed with legacyMountPublicLink())
145
-	 * @return JSONResponse
146
-	 */
147
-	#[NoAdminRequired]
148
-	public function askForFederatedShare($token, $remote, $password = '', $owner = '', $ownerDisplayName = '', $name = '') {
149
-		// check if server admin allows to mount public links from other servers
150
-		if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() === false) {
151
-			return new JSONResponse(['message' => $this->l->t('Server to server sharing is not enabled on this server')], Http::STATUS_BAD_REQUEST);
152
-		}
153
-
154
-		$cloudId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getUID(), $this->addressHandler->generateRemoteURL());
155
-
156
-		$httpClient = $this->clientService->newClient();
157
-
158
-		try {
159
-			$response = $httpClient->post($remote . '/index.php/apps/federatedfilesharing/createFederatedShare',
160
-				[
161
-					'body' => [
162
-						'token' => $token,
163
-						'shareWith' => rtrim($cloudId->getId(), '/'),
164
-						'password' => $password
165
-					],
166
-					'connect_timeout' => 10,
167
-				]
168
-			);
169
-		} catch (\Exception $e) {
170
-			if (empty($password)) {
171
-				$message = $this->l->t("Couldn't establish a federated share.");
172
-			} else {
173
-				$message = $this->l->t("Couldn't establish a federated share, maybe the password was wrong.");
174
-			}
175
-			return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
176
-		}
177
-
178
-		$body = $response->getBody();
179
-		$result = json_decode($body, true);
180
-
181
-		if (is_array($result) && isset($result['remoteUrl'])) {
182
-			return new JSONResponse(['message' => $this->l->t('Federated Share request sent, you will receive an invitation. Check your notifications.')]);
183
-		}
184
-
185
-		// if we doesn't get the expected response we assume that we try to add
186
-		// a federated share from a Nextcloud <= 9 server
187
-		$message = $this->l->t("Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9).");
188
-		return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
189
-	}
41
+    /**
42
+     * MountPublicLinkController constructor.
43
+     */
44
+    public function __construct(
45
+        string $appName,
46
+        IRequest $request,
47
+        private FederatedShareProvider $federatedShareProvider,
48
+        private IManager $shareManager,
49
+        private AddressHandler $addressHandler,
50
+        private ISession $session,
51
+        private IL10N $l,
52
+        private IUserSession $userSession,
53
+        private IClientService $clientService,
54
+        private ICloudIdManager $cloudIdManager,
55
+        private LoggerInterface $logger,
56
+    ) {
57
+        parent::__construct($appName, $request);
58
+    }
59
+
60
+    /**
61
+     * send federated share to a user of a public link
62
+     *
63
+     * @param string $shareWith Username to share with
64
+     * @param string $token Token of the share
65
+     * @param string $password Password of the share
66
+     * @return JSONResponse<Http::STATUS_OK, array{remoteUrl: string}, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST, array{message: string}, array{}>
67
+     *
68
+     * 200: Remote URL returned
69
+     * 400: Creating share is not possible
70
+     */
71
+    #[NoCSRFRequired]
72
+    #[PublicPage]
73
+    #[BruteForceProtection(action: 'publicLink2FederatedShare')]
74
+    #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
75
+    public function createFederatedShare($shareWith, $token, $password = '') {
76
+        if (!$this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
77
+            return new JSONResponse(
78
+                ['message' => 'This server doesn\'t support outgoing federated shares'],
79
+                Http::STATUS_BAD_REQUEST
80
+            );
81
+        }
82
+
83
+        try {
84
+            [, $server] = $this->addressHandler->splitUserRemote($shareWith);
85
+            $share = $this->shareManager->getShareByToken($token);
86
+        } catch (HintException $e) {
87
+            $response = new JSONResponse(['message' => $e->getHint()], Http::STATUS_BAD_REQUEST);
88
+            $response->throttle();
89
+            return $response;
90
+        }
91
+
92
+        // make sure that user is authenticated in case of a password protected link
93
+        $allowedShareIds = $this->session->get(PublicAuth::DAV_AUTHENTICATED);
94
+        if (!is_array($allowedShareIds)) {
95
+            $allowedShareIds = [];
96
+        }
97
+
98
+        $authenticated = in_array($share->getId(), $allowedShareIds)
99
+            || $this->shareManager->checkPassword($share, $password);
100
+
101
+        $storedPassword = $share->getPassword();
102
+        if (!empty($storedPassword) && !$authenticated) {
103
+            $response = new JSONResponse(
104
+                ['message' => 'No permission to access the share'],
105
+                Http::STATUS_BAD_REQUEST
106
+            );
107
+            $response->throttle();
108
+            return $response;
109
+        }
110
+
111
+        if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
112
+            $response = new JSONResponse(
113
+                ['message' => 'Mounting file drop not supported'],
114
+                Http::STATUS_BAD_REQUEST
115
+            );
116
+            $response->throttle();
117
+            return $response;
118
+        }
119
+
120
+        $share->setSharedWith($shareWith);
121
+        $share->setShareType(IShare::TYPE_REMOTE);
122
+
123
+        try {
124
+            $this->federatedShareProvider->create($share);
125
+        } catch (\Exception $e) {
126
+            $this->logger->warning($e->getMessage(), [
127
+                'app' => 'federatedfilesharing',
128
+                'exception' => $e,
129
+            ]);
130
+            return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
131
+        }
132
+
133
+        return new JSONResponse(['remoteUrl' => $server]);
134
+    }
135
+
136
+    /**
137
+     * ask other server to get a federated share
138
+     *
139
+     * @param string $token
140
+     * @param string $remote
141
+     * @param string $password
142
+     * @param string $owner (only for legacy reasons, can be removed with legacyMountPublicLink())
143
+     * @param string $ownerDisplayName (only for legacy reasons, can be removed with legacyMountPublicLink())
144
+     * @param string $name (only for legacy reasons, can be removed with legacyMountPublicLink())
145
+     * @return JSONResponse
146
+     */
147
+    #[NoAdminRequired]
148
+    public function askForFederatedShare($token, $remote, $password = '', $owner = '', $ownerDisplayName = '', $name = '') {
149
+        // check if server admin allows to mount public links from other servers
150
+        if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() === false) {
151
+            return new JSONResponse(['message' => $this->l->t('Server to server sharing is not enabled on this server')], Http::STATUS_BAD_REQUEST);
152
+        }
153
+
154
+        $cloudId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getUID(), $this->addressHandler->generateRemoteURL());
155
+
156
+        $httpClient = $this->clientService->newClient();
157
+
158
+        try {
159
+            $response = $httpClient->post($remote . '/index.php/apps/federatedfilesharing/createFederatedShare',
160
+                [
161
+                    'body' => [
162
+                        'token' => $token,
163
+                        'shareWith' => rtrim($cloudId->getId(), '/'),
164
+                        'password' => $password
165
+                    ],
166
+                    'connect_timeout' => 10,
167
+                ]
168
+            );
169
+        } catch (\Exception $e) {
170
+            if (empty($password)) {
171
+                $message = $this->l->t("Couldn't establish a federated share.");
172
+            } else {
173
+                $message = $this->l->t("Couldn't establish a federated share, maybe the password was wrong.");
174
+            }
175
+            return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
176
+        }
177
+
178
+        $body = $response->getBody();
179
+        $result = json_decode($body, true);
180
+
181
+        if (is_array($result) && isset($result['remoteUrl'])) {
182
+            return new JSONResponse(['message' => $this->l->t('Federated Share request sent, you will receive an invitation. Check your notifications.')]);
183
+        }
184
+
185
+        // if we doesn't get the expected response we assume that we try to add
186
+        // a federated share from a Nextcloud <= 9 server
187
+        $message = $this->l->t("Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9).");
188
+        return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
189
+    }
190 190
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareController.php 1 patch
Indentation   +354 added lines, -354 removed lines patch added patch discarded remove patch
@@ -51,358 +51,358 @@
 block discarded – undo
51 51
  */
52 52
 #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
53 53
 class ShareController extends AuthPublicShareController {
54
-	protected ?IShare $share = null;
55
-
56
-	public const SHARE_ACCESS = 'access';
57
-	public const SHARE_AUTH = 'auth';
58
-	public const SHARE_DOWNLOAD = 'download';
59
-
60
-	public function __construct(
61
-		string $appName,
62
-		IRequest $request,
63
-		protected IConfig $config,
64
-		IURLGenerator $urlGenerator,
65
-		protected IUserManager $userManager,
66
-		protected \OCP\Activity\IManager $activityManager,
67
-		protected ShareManager $shareManager,
68
-		ISession $session,
69
-		protected IPreview $previewManager,
70
-		protected IRootFolder $rootFolder,
71
-		protected FederatedShareProvider $federatedShareProvider,
72
-		protected IAccountManager $accountManager,
73
-		protected IEventDispatcher $eventDispatcher,
74
-		protected IL10N $l10n,
75
-		protected ISecureRandom $secureRandom,
76
-		protected Defaults $defaults,
77
-		private IPublicShareTemplateFactory $publicShareTemplateFactory,
78
-	) {
79
-		parent::__construct($appName, $request, $session, $urlGenerator);
80
-	}
81
-
82
-	/**
83
-	 * Show the authentication page
84
-	 * The form has to submit to the authenticate method route
85
-	 */
86
-	#[PublicPage]
87
-	#[NoCSRFRequired]
88
-	public function showAuthenticate(): TemplateResponse {
89
-		$templateParameters = ['share' => $this->share];
90
-
91
-		$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
92
-
93
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
94
-		if ($this->share->getSendPasswordByTalk()) {
95
-			$csp = new ContentSecurityPolicy();
96
-			$csp->addAllowedConnectDomain('*');
97
-			$csp->addAllowedMediaDomain('blob:');
98
-			$response->setContentSecurityPolicy($csp);
99
-		}
100
-
101
-		return $response;
102
-	}
103
-
104
-	/**
105
-	 * The template to show when authentication failed
106
-	 */
107
-	protected function showAuthFailed(): TemplateResponse {
108
-		$templateParameters = ['share' => $this->share, 'wrongpw' => true];
109
-
110
-		$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
111
-
112
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
113
-		if ($this->share->getSendPasswordByTalk()) {
114
-			$csp = new ContentSecurityPolicy();
115
-			$csp->addAllowedConnectDomain('*');
116
-			$csp->addAllowedMediaDomain('blob:');
117
-			$response->setContentSecurityPolicy($csp);
118
-		}
119
-
120
-		return $response;
121
-	}
122
-
123
-	/**
124
-	 * The template to show after user identification
125
-	 */
126
-	protected function showIdentificationResult(bool $success = false): TemplateResponse {
127
-		$templateParameters = ['share' => $this->share, 'identityOk' => $success];
128
-
129
-		$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
130
-
131
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
132
-		if ($this->share->getSendPasswordByTalk()) {
133
-			$csp = new ContentSecurityPolicy();
134
-			$csp->addAllowedConnectDomain('*');
135
-			$csp->addAllowedMediaDomain('blob:');
136
-			$response->setContentSecurityPolicy($csp);
137
-		}
138
-
139
-		return $response;
140
-	}
141
-
142
-	/**
143
-	 * Validate the identity token of a public share
144
-	 *
145
-	 * @param ?string $identityToken
146
-	 * @return bool
147
-	 */
148
-	protected function validateIdentity(?string $identityToken = null): bool {
149
-		if ($this->share->getShareType() !== IShare::TYPE_EMAIL) {
150
-			return false;
151
-		}
152
-
153
-		if ($identityToken === null || $this->share->getSharedWith() === null) {
154
-			return false;
155
-		}
156
-
157
-		return $identityToken === $this->share->getSharedWith();
158
-	}
159
-
160
-	/**
161
-	 * Generates a password for the share, respecting any password policy defined
162
-	 */
163
-	protected function generatePassword(): void {
164
-		$event = new GenerateSecurePasswordEvent(PasswordContext::SHARING);
165
-		$this->eventDispatcher->dispatchTyped($event);
166
-		$password = $event->getPassword() ?? $this->secureRandom->generate(20);
167
-
168
-		$this->share->setPassword($password);
169
-		$this->shareManager->updateShare($this->share);
170
-	}
171
-
172
-	protected function verifyPassword(string $password): bool {
173
-		return $this->shareManager->checkPassword($this->share, $password);
174
-	}
175
-
176
-	protected function getPasswordHash(): ?string {
177
-		return $this->share->getPassword();
178
-	}
179
-
180
-	public function isValidToken(): bool {
181
-		try {
182
-			$this->share = $this->shareManager->getShareByToken($this->getToken());
183
-		} catch (ShareNotFound $e) {
184
-			return false;
185
-		}
186
-
187
-		return true;
188
-	}
189
-
190
-	protected function isPasswordProtected(): bool {
191
-		return $this->share->getPassword() !== null;
192
-	}
193
-
194
-	protected function authSucceeded() {
195
-		if ($this->share === null) {
196
-			throw new NotFoundException();
197
-		}
198
-
199
-		// For share this was always set so it is still used in other apps
200
-		$allowedShareIds = $this->session->get(PublicAuth::DAV_AUTHENTICATED);
201
-		if (!is_array($allowedShareIds)) {
202
-			$allowedShareIds = [];
203
-		}
204
-
205
-		$this->session->set(PublicAuth::DAV_AUTHENTICATED, array_merge($allowedShareIds, [$this->share->getId()]));
206
-	}
207
-
208
-	protected function authFailed() {
209
-		$this->emitAccessShareHook($this->share, 403, 'Wrong password');
210
-		$this->emitShareAccessEvent($this->share, self::SHARE_AUTH, 403, 'Wrong password');
211
-	}
212
-
213
-	/**
214
-	 * throws hooks when a share is attempted to be accessed
215
-	 *
216
-	 * @param IShare|string $share the Share instance if available,
217
-	 *                             otherwise token
218
-	 * @param int $errorCode
219
-	 * @param string $errorMessage
220
-	 *
221
-	 * @throws HintException
222
-	 * @throws \OC\ServerNotAvailableException
223
-	 *
224
-	 * @deprecated use OCP\Files_Sharing\Event\ShareLinkAccessedEvent
225
-	 */
226
-	protected function emitAccessShareHook($share, int $errorCode = 200, string $errorMessage = '') {
227
-		$itemType = $itemSource = $uidOwner = '';
228
-		$token = $share;
229
-		$exception = null;
230
-		if ($share instanceof IShare) {
231
-			try {
232
-				$token = $share->getToken();
233
-				$uidOwner = $share->getSharedBy();
234
-				$itemType = $share->getNodeType();
235
-				$itemSource = $share->getNodeId();
236
-			} catch (\Exception $e) {
237
-				// we log what we know and pass on the exception afterwards
238
-				$exception = $e;
239
-			}
240
-		}
241
-
242
-		\OC_Hook::emit(Share::class, 'share_link_access', [
243
-			'itemType' => $itemType,
244
-			'itemSource' => $itemSource,
245
-			'uidOwner' => $uidOwner,
246
-			'token' => $token,
247
-			'errorCode' => $errorCode,
248
-			'errorMessage' => $errorMessage
249
-		]);
250
-
251
-		if (!is_null($exception)) {
252
-			throw $exception;
253
-		}
254
-	}
255
-
256
-	/**
257
-	 * Emit a ShareLinkAccessedEvent event when a share is accessed, downloaded, auth...
258
-	 */
259
-	protected function emitShareAccessEvent(IShare $share, string $step = '', int $errorCode = 200, string $errorMessage = ''): void {
260
-		if ($step !== self::SHARE_ACCESS
261
-			&& $step !== self::SHARE_AUTH
262
-			&& $step !== self::SHARE_DOWNLOAD) {
263
-			return;
264
-		}
265
-		$this->eventDispatcher->dispatchTyped(new ShareLinkAccessedEvent($share, $step, $errorCode, $errorMessage));
266
-	}
267
-
268
-	/**
269
-	 * Validate the permissions of the share
270
-	 *
271
-	 * @param Share\IShare $share
272
-	 * @return bool
273
-	 */
274
-	private function validateShare(IShare $share) {
275
-		// If the owner is disabled no access to the link is granted
276
-		$owner = $this->userManager->get($share->getShareOwner());
277
-		if ($owner === null || !$owner->isEnabled()) {
278
-			return false;
279
-		}
280
-
281
-		// If the initiator of the share is disabled no access is granted
282
-		$initiator = $this->userManager->get($share->getSharedBy());
283
-		if ($initiator === null || !$initiator->isEnabled()) {
284
-			return false;
285
-		}
286
-
287
-		return $share->getNode()->isReadable() && $share->getNode()->isShareable();
288
-	}
289
-
290
-	/**
291
-	 * @param string $path
292
-	 * @return TemplateResponse
293
-	 * @throws NotFoundException
294
-	 * @throws \Exception
295
-	 */
296
-	#[PublicPage]
297
-	#[NoCSRFRequired]
298
-	public function showShare($path = ''): TemplateResponse {
299
-		\OC_User::setIncognitoMode(true);
300
-
301
-		// Check whether share exists
302
-		try {
303
-			$share = $this->shareManager->getShareByToken($this->getToken());
304
-		} catch (ShareNotFound $e) {
305
-			// The share does not exists, we do not emit an ShareLinkAccessedEvent
306
-			$this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
307
-			throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
308
-		}
309
-
310
-		if (!$this->validateShare($share)) {
311
-			throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
312
-		}
313
-
314
-		$shareNode = $share->getNode();
315
-
316
-		try {
317
-			$templateProvider = $this->publicShareTemplateFactory->getProvider($share);
318
-			$response = $templateProvider->renderPage($share, $this->getToken(), $path);
319
-		} catch (NotFoundException $e) {
320
-			$this->emitAccessShareHook($share, 404, 'Share not found');
321
-			$this->emitShareAccessEvent($share, ShareController::SHARE_ACCESS, 404, 'Share not found');
322
-			throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
323
-		}
324
-
325
-		// We can't get the path of a file share
326
-		try {
327
-			if ($shareNode instanceof File && $path !== '') {
328
-				$this->emitAccessShareHook($share, 404, 'Share not found');
329
-				$this->emitShareAccessEvent($share, self::SHARE_ACCESS, 404, 'Share not found');
330
-				throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
331
-			}
332
-		} catch (\Exception $e) {
333
-			$this->emitAccessShareHook($share, 404, 'Share not found');
334
-			$this->emitShareAccessEvent($share, self::SHARE_ACCESS, 404, 'Share not found');
335
-			throw $e;
336
-		}
337
-
338
-
339
-		$this->emitAccessShareHook($share);
340
-		$this->emitShareAccessEvent($share, self::SHARE_ACCESS);
341
-
342
-		return $response;
343
-	}
344
-
345
-	/**
346
-	 * @NoSameSiteCookieRequired
347
-	 *
348
-	 * @param string $token
349
-	 * @param string|null $files
350
-	 * @param string $path
351
-	 * @return void|Response
352
-	 * @throws NotFoundException
353
-	 * @deprecated 31.0.0 Users are encouraged to use the DAV endpoint
354
-	 */
355
-	#[PublicPage]
356
-	#[NoCSRFRequired]
357
-	public function downloadShare($token, $files = null, $path = '') {
358
-		\OC_User::setIncognitoMode(true);
359
-
360
-		$share = $this->shareManager->getShareByToken($token);
361
-
362
-		if (!($share->getPermissions() & Constants::PERMISSION_READ)) {
363
-			return new DataResponse('Share has no read permission');
364
-		}
365
-
366
-		$attributes = $share->getAttributes();
367
-		if ($attributes?->getAttribute('permissions', 'download') === false) {
368
-			return new DataResponse('Share has no download permission');
369
-		}
370
-
371
-		if (!$this->validateShare($share)) {
372
-			throw new NotFoundException();
373
-		}
374
-
375
-		$node = $share->getNode();
376
-		if ($node instanceof Folder) {
377
-			// Directory share
378
-
379
-			// Try to get the path
380
-			if ($path !== '') {
381
-				try {
382
-					$node = $node->get($path);
383
-				} catch (NotFoundException $e) {
384
-					$this->emitAccessShareHook($share, 404, 'Share not found');
385
-					$this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD, 404, 'Share not found');
386
-					return new NotFoundResponse();
387
-				}
388
-			}
389
-
390
-			if ($node instanceof Folder) {
391
-				if ($files === null || $files === '') {
392
-					if ($share->getHideDownload()) {
393
-						throw new NotFoundException('Downloading a folder');
394
-					}
395
-				}
396
-			}
397
-		}
398
-
399
-		$this->emitAccessShareHook($share);
400
-		$this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD);
401
-
402
-		$davUrl = '/public.php/dav/files/' . $token . '/?accept=zip';
403
-		if ($files !== null) {
404
-			$davUrl .= '&files=' . $files;
405
-		}
406
-		return new RedirectResponse($this->urlGenerator->getAbsoluteURL($davUrl));
407
-	}
54
+    protected ?IShare $share = null;
55
+
56
+    public const SHARE_ACCESS = 'access';
57
+    public const SHARE_AUTH = 'auth';
58
+    public const SHARE_DOWNLOAD = 'download';
59
+
60
+    public function __construct(
61
+        string $appName,
62
+        IRequest $request,
63
+        protected IConfig $config,
64
+        IURLGenerator $urlGenerator,
65
+        protected IUserManager $userManager,
66
+        protected \OCP\Activity\IManager $activityManager,
67
+        protected ShareManager $shareManager,
68
+        ISession $session,
69
+        protected IPreview $previewManager,
70
+        protected IRootFolder $rootFolder,
71
+        protected FederatedShareProvider $federatedShareProvider,
72
+        protected IAccountManager $accountManager,
73
+        protected IEventDispatcher $eventDispatcher,
74
+        protected IL10N $l10n,
75
+        protected ISecureRandom $secureRandom,
76
+        protected Defaults $defaults,
77
+        private IPublicShareTemplateFactory $publicShareTemplateFactory,
78
+    ) {
79
+        parent::__construct($appName, $request, $session, $urlGenerator);
80
+    }
81
+
82
+    /**
83
+     * Show the authentication page
84
+     * The form has to submit to the authenticate method route
85
+     */
86
+    #[PublicPage]
87
+    #[NoCSRFRequired]
88
+    public function showAuthenticate(): TemplateResponse {
89
+        $templateParameters = ['share' => $this->share];
90
+
91
+        $this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
92
+
93
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
94
+        if ($this->share->getSendPasswordByTalk()) {
95
+            $csp = new ContentSecurityPolicy();
96
+            $csp->addAllowedConnectDomain('*');
97
+            $csp->addAllowedMediaDomain('blob:');
98
+            $response->setContentSecurityPolicy($csp);
99
+        }
100
+
101
+        return $response;
102
+    }
103
+
104
+    /**
105
+     * The template to show when authentication failed
106
+     */
107
+    protected function showAuthFailed(): TemplateResponse {
108
+        $templateParameters = ['share' => $this->share, 'wrongpw' => true];
109
+
110
+        $this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
111
+
112
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
113
+        if ($this->share->getSendPasswordByTalk()) {
114
+            $csp = new ContentSecurityPolicy();
115
+            $csp->addAllowedConnectDomain('*');
116
+            $csp->addAllowedMediaDomain('blob:');
117
+            $response->setContentSecurityPolicy($csp);
118
+        }
119
+
120
+        return $response;
121
+    }
122
+
123
+    /**
124
+     * The template to show after user identification
125
+     */
126
+    protected function showIdentificationResult(bool $success = false): TemplateResponse {
127
+        $templateParameters = ['share' => $this->share, 'identityOk' => $success];
128
+
129
+        $this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
130
+
131
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
132
+        if ($this->share->getSendPasswordByTalk()) {
133
+            $csp = new ContentSecurityPolicy();
134
+            $csp->addAllowedConnectDomain('*');
135
+            $csp->addAllowedMediaDomain('blob:');
136
+            $response->setContentSecurityPolicy($csp);
137
+        }
138
+
139
+        return $response;
140
+    }
141
+
142
+    /**
143
+     * Validate the identity token of a public share
144
+     *
145
+     * @param ?string $identityToken
146
+     * @return bool
147
+     */
148
+    protected function validateIdentity(?string $identityToken = null): bool {
149
+        if ($this->share->getShareType() !== IShare::TYPE_EMAIL) {
150
+            return false;
151
+        }
152
+
153
+        if ($identityToken === null || $this->share->getSharedWith() === null) {
154
+            return false;
155
+        }
156
+
157
+        return $identityToken === $this->share->getSharedWith();
158
+    }
159
+
160
+    /**
161
+     * Generates a password for the share, respecting any password policy defined
162
+     */
163
+    protected function generatePassword(): void {
164
+        $event = new GenerateSecurePasswordEvent(PasswordContext::SHARING);
165
+        $this->eventDispatcher->dispatchTyped($event);
166
+        $password = $event->getPassword() ?? $this->secureRandom->generate(20);
167
+
168
+        $this->share->setPassword($password);
169
+        $this->shareManager->updateShare($this->share);
170
+    }
171
+
172
+    protected function verifyPassword(string $password): bool {
173
+        return $this->shareManager->checkPassword($this->share, $password);
174
+    }
175
+
176
+    protected function getPasswordHash(): ?string {
177
+        return $this->share->getPassword();
178
+    }
179
+
180
+    public function isValidToken(): bool {
181
+        try {
182
+            $this->share = $this->shareManager->getShareByToken($this->getToken());
183
+        } catch (ShareNotFound $e) {
184
+            return false;
185
+        }
186
+
187
+        return true;
188
+    }
189
+
190
+    protected function isPasswordProtected(): bool {
191
+        return $this->share->getPassword() !== null;
192
+    }
193
+
194
+    protected function authSucceeded() {
195
+        if ($this->share === null) {
196
+            throw new NotFoundException();
197
+        }
198
+
199
+        // For share this was always set so it is still used in other apps
200
+        $allowedShareIds = $this->session->get(PublicAuth::DAV_AUTHENTICATED);
201
+        if (!is_array($allowedShareIds)) {
202
+            $allowedShareIds = [];
203
+        }
204
+
205
+        $this->session->set(PublicAuth::DAV_AUTHENTICATED, array_merge($allowedShareIds, [$this->share->getId()]));
206
+    }
207
+
208
+    protected function authFailed() {
209
+        $this->emitAccessShareHook($this->share, 403, 'Wrong password');
210
+        $this->emitShareAccessEvent($this->share, self::SHARE_AUTH, 403, 'Wrong password');
211
+    }
212
+
213
+    /**
214
+     * throws hooks when a share is attempted to be accessed
215
+     *
216
+     * @param IShare|string $share the Share instance if available,
217
+     *                             otherwise token
218
+     * @param int $errorCode
219
+     * @param string $errorMessage
220
+     *
221
+     * @throws HintException
222
+     * @throws \OC\ServerNotAvailableException
223
+     *
224
+     * @deprecated use OCP\Files_Sharing\Event\ShareLinkAccessedEvent
225
+     */
226
+    protected function emitAccessShareHook($share, int $errorCode = 200, string $errorMessage = '') {
227
+        $itemType = $itemSource = $uidOwner = '';
228
+        $token = $share;
229
+        $exception = null;
230
+        if ($share instanceof IShare) {
231
+            try {
232
+                $token = $share->getToken();
233
+                $uidOwner = $share->getSharedBy();
234
+                $itemType = $share->getNodeType();
235
+                $itemSource = $share->getNodeId();
236
+            } catch (\Exception $e) {
237
+                // we log what we know and pass on the exception afterwards
238
+                $exception = $e;
239
+            }
240
+        }
241
+
242
+        \OC_Hook::emit(Share::class, 'share_link_access', [
243
+            'itemType' => $itemType,
244
+            'itemSource' => $itemSource,
245
+            'uidOwner' => $uidOwner,
246
+            'token' => $token,
247
+            'errorCode' => $errorCode,
248
+            'errorMessage' => $errorMessage
249
+        ]);
250
+
251
+        if (!is_null($exception)) {
252
+            throw $exception;
253
+        }
254
+    }
255
+
256
+    /**
257
+     * Emit a ShareLinkAccessedEvent event when a share is accessed, downloaded, auth...
258
+     */
259
+    protected function emitShareAccessEvent(IShare $share, string $step = '', int $errorCode = 200, string $errorMessage = ''): void {
260
+        if ($step !== self::SHARE_ACCESS
261
+            && $step !== self::SHARE_AUTH
262
+            && $step !== self::SHARE_DOWNLOAD) {
263
+            return;
264
+        }
265
+        $this->eventDispatcher->dispatchTyped(new ShareLinkAccessedEvent($share, $step, $errorCode, $errorMessage));
266
+    }
267
+
268
+    /**
269
+     * Validate the permissions of the share
270
+     *
271
+     * @param Share\IShare $share
272
+     * @return bool
273
+     */
274
+    private function validateShare(IShare $share) {
275
+        // If the owner is disabled no access to the link is granted
276
+        $owner = $this->userManager->get($share->getShareOwner());
277
+        if ($owner === null || !$owner->isEnabled()) {
278
+            return false;
279
+        }
280
+
281
+        // If the initiator of the share is disabled no access is granted
282
+        $initiator = $this->userManager->get($share->getSharedBy());
283
+        if ($initiator === null || !$initiator->isEnabled()) {
284
+            return false;
285
+        }
286
+
287
+        return $share->getNode()->isReadable() && $share->getNode()->isShareable();
288
+    }
289
+
290
+    /**
291
+     * @param string $path
292
+     * @return TemplateResponse
293
+     * @throws NotFoundException
294
+     * @throws \Exception
295
+     */
296
+    #[PublicPage]
297
+    #[NoCSRFRequired]
298
+    public function showShare($path = ''): TemplateResponse {
299
+        \OC_User::setIncognitoMode(true);
300
+
301
+        // Check whether share exists
302
+        try {
303
+            $share = $this->shareManager->getShareByToken($this->getToken());
304
+        } catch (ShareNotFound $e) {
305
+            // The share does not exists, we do not emit an ShareLinkAccessedEvent
306
+            $this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
307
+            throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
308
+        }
309
+
310
+        if (!$this->validateShare($share)) {
311
+            throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
312
+        }
313
+
314
+        $shareNode = $share->getNode();
315
+
316
+        try {
317
+            $templateProvider = $this->publicShareTemplateFactory->getProvider($share);
318
+            $response = $templateProvider->renderPage($share, $this->getToken(), $path);
319
+        } catch (NotFoundException $e) {
320
+            $this->emitAccessShareHook($share, 404, 'Share not found');
321
+            $this->emitShareAccessEvent($share, ShareController::SHARE_ACCESS, 404, 'Share not found');
322
+            throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
323
+        }
324
+
325
+        // We can't get the path of a file share
326
+        try {
327
+            if ($shareNode instanceof File && $path !== '') {
328
+                $this->emitAccessShareHook($share, 404, 'Share not found');
329
+                $this->emitShareAccessEvent($share, self::SHARE_ACCESS, 404, 'Share not found');
330
+                throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
331
+            }
332
+        } catch (\Exception $e) {
333
+            $this->emitAccessShareHook($share, 404, 'Share not found');
334
+            $this->emitShareAccessEvent($share, self::SHARE_ACCESS, 404, 'Share not found');
335
+            throw $e;
336
+        }
337
+
338
+
339
+        $this->emitAccessShareHook($share);
340
+        $this->emitShareAccessEvent($share, self::SHARE_ACCESS);
341
+
342
+        return $response;
343
+    }
344
+
345
+    /**
346
+     * @NoSameSiteCookieRequired
347
+     *
348
+     * @param string $token
349
+     * @param string|null $files
350
+     * @param string $path
351
+     * @return void|Response
352
+     * @throws NotFoundException
353
+     * @deprecated 31.0.0 Users are encouraged to use the DAV endpoint
354
+     */
355
+    #[PublicPage]
356
+    #[NoCSRFRequired]
357
+    public function downloadShare($token, $files = null, $path = '') {
358
+        \OC_User::setIncognitoMode(true);
359
+
360
+        $share = $this->shareManager->getShareByToken($token);
361
+
362
+        if (!($share->getPermissions() & Constants::PERMISSION_READ)) {
363
+            return new DataResponse('Share has no read permission');
364
+        }
365
+
366
+        $attributes = $share->getAttributes();
367
+        if ($attributes?->getAttribute('permissions', 'download') === false) {
368
+            return new DataResponse('Share has no download permission');
369
+        }
370
+
371
+        if (!$this->validateShare($share)) {
372
+            throw new NotFoundException();
373
+        }
374
+
375
+        $node = $share->getNode();
376
+        if ($node instanceof Folder) {
377
+            // Directory share
378
+
379
+            // Try to get the path
380
+            if ($path !== '') {
381
+                try {
382
+                    $node = $node->get($path);
383
+                } catch (NotFoundException $e) {
384
+                    $this->emitAccessShareHook($share, 404, 'Share not found');
385
+                    $this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD, 404, 'Share not found');
386
+                    return new NotFoundResponse();
387
+                }
388
+            }
389
+
390
+            if ($node instanceof Folder) {
391
+                if ($files === null || $files === '') {
392
+                    if ($share->getHideDownload()) {
393
+                        throw new NotFoundException('Downloading a folder');
394
+                    }
395
+                }
396
+            }
397
+        }
398
+
399
+        $this->emitAccessShareHook($share);
400
+        $this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD);
401
+
402
+        $davUrl = '/public.php/dav/files/' . $token . '/?accept=zip';
403
+        if ($files !== null) {
404
+            $davUrl .= '&files=' . $files;
405
+        }
406
+        return new RedirectResponse($this->urlGenerator->getAbsoluteURL($davUrl));
407
+    }
408 408
 }
Please login to merge, or discard this patch.
tests/lib/AppFramework/Controller/PublicShareControllerTest.php 1 patch
Indentation   +63 added lines, -63 removed lines patch added patch discarded remove patch
@@ -15,70 +15,70 @@
 block discarded – undo
15 15
 use PHPUnit\Framework\MockObject\MockObject;
16 16
 
17 17
 class TestController extends PublicShareController {
18
-	public function __construct(
19
-		string $appName,
20
-		IRequest $request,
21
-		ISession $session,
22
-		private string $hash,
23
-		private bool $isProtected,
24
-	) {
25
-		parent::__construct($appName, $request, $session);
26
-	}
27
-
28
-	protected function getPasswordHash(): string {
29
-		return $this->hash;
30
-	}
31
-
32
-	public function isValidToken(): bool {
33
-		return false;
34
-	}
35
-
36
-	protected function isPasswordProtected(): bool {
37
-		return $this->isProtected;
38
-	}
18
+    public function __construct(
19
+        string $appName,
20
+        IRequest $request,
21
+        ISession $session,
22
+        private string $hash,
23
+        private bool $isProtected,
24
+    ) {
25
+        parent::__construct($appName, $request, $session);
26
+    }
27
+
28
+    protected function getPasswordHash(): string {
29
+        return $this->hash;
30
+    }
31
+
32
+    public function isValidToken(): bool {
33
+        return false;
34
+    }
35
+
36
+    protected function isPasswordProtected(): bool {
37
+        return $this->isProtected;
38
+    }
39 39
 }
40 40
 
41 41
 class PublicShareControllerTest extends \Test\TestCase {
42
-	private IRequest&MockObject $request;
43
-	private ISession&MockObject $session;
44
-
45
-	protected function setUp(): void {
46
-		parent::setUp();
47
-
48
-		$this->request = $this->createMock(IRequest::class);
49
-		$this->session = $this->createMock(ISession::class);
50
-	}
51
-
52
-	public function testGetToken(): void {
53
-		$controller = new TestController('app', $this->request, $this->session, 'hash', false);
54
-
55
-		$controller->setToken('test');
56
-		$this->assertEquals('test', $controller->getToken());
57
-	}
58
-
59
-	public static function dataIsAuthenticated(): array {
60
-		return [
61
-			[false, 'token1', 'token1', 'hash1', 'hash1',  true],
62
-			[false, 'token1', 'token1', 'hash1', 'hash2',  true],
63
-			[false, 'token1', 'token2', 'hash1', 'hash1',  true],
64
-			[false, 'token1', 'token2', 'hash1', 'hash2',  true],
65
-			[ true, 'token1', 'token1', 'hash1', 'hash1',  true],
66
-			[ true, 'token1', 'token1', 'hash1', 'hash2', false],
67
-			[ true, 'token1', 'token2', 'hash1', 'hash1', false],
68
-			[ true, 'token1', 'token2', 'hash1', 'hash2', false],
69
-		];
70
-	}
71
-
72
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataIsAuthenticated')]
73
-	public function testIsAuthenticatedNotPasswordProtected(bool $protected, string $token1, string $token2, string $hash1, string $hash2, bool $expected): void {
74
-		$controller = new TestController('app', $this->request, $this->session, $hash2, $protected);
75
-
76
-		$this->session->method('get')
77
-			->with(PublicShareController::DAV_AUTHENTICATED_FRONTEND)
78
-			->willReturn("{\"$token1\":\"$hash1\"}");
79
-
80
-		$controller->setToken($token2);
81
-
82
-		$this->assertEquals($expected, $controller->isAuthenticated());
83
-	}
42
+    private IRequest&MockObject $request;
43
+    private ISession&MockObject $session;
44
+
45
+    protected function setUp(): void {
46
+        parent::setUp();
47
+
48
+        $this->request = $this->createMock(IRequest::class);
49
+        $this->session = $this->createMock(ISession::class);
50
+    }
51
+
52
+    public function testGetToken(): void {
53
+        $controller = new TestController('app', $this->request, $this->session, 'hash', false);
54
+
55
+        $controller->setToken('test');
56
+        $this->assertEquals('test', $controller->getToken());
57
+    }
58
+
59
+    public static function dataIsAuthenticated(): array {
60
+        return [
61
+            [false, 'token1', 'token1', 'hash1', 'hash1',  true],
62
+            [false, 'token1', 'token1', 'hash1', 'hash2',  true],
63
+            [false, 'token1', 'token2', 'hash1', 'hash1',  true],
64
+            [false, 'token1', 'token2', 'hash1', 'hash2',  true],
65
+            [ true, 'token1', 'token1', 'hash1', 'hash1',  true],
66
+            [ true, 'token1', 'token1', 'hash1', 'hash2', false],
67
+            [ true, 'token1', 'token2', 'hash1', 'hash1', false],
68
+            [ true, 'token1', 'token2', 'hash1', 'hash2', false],
69
+        ];
70
+    }
71
+
72
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataIsAuthenticated')]
73
+    public function testIsAuthenticatedNotPasswordProtected(bool $protected, string $token1, string $token2, string $hash1, string $hash2, bool $expected): void {
74
+        $controller = new TestController('app', $this->request, $this->session, $hash2, $protected);
75
+
76
+        $this->session->method('get')
77
+            ->with(PublicShareController::DAV_AUTHENTICATED_FRONTEND)
78
+            ->willReturn("{\"$token1\":\"$hash1\"}");
79
+
80
+        $controller->setToken($token2);
81
+
82
+        $this->assertEquals($expected, $controller->isAuthenticated());
83
+    }
84 84
 }
Please login to merge, or discard this patch.
tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php 2 patches
Indentation   +116 added lines, -116 removed lines patch added patch discarded remove patch
@@ -18,120 +18,120 @@
 block discarded – undo
18 18
 use PHPUnit\Framework\MockObject\MockObject;
19 19
 
20 20
 class AuthPublicShareControllerTest extends \Test\TestCase {
21
-	private IRequest&MockObject $request;
22
-	private ISession&MockObject $session;
23
-	private IURLGenerator&MockObject $urlGenerator;
24
-	private AuthPublicShareController&MockObject $controller;
25
-
26
-
27
-	protected function setUp(): void {
28
-		parent::setUp();
29
-
30
-		$this->request = $this->createMock(IRequest::class);
31
-		$this->session = $this->createMock(ISession::class);
32
-		$this->urlGenerator = $this->createMock(IURLGenerator::class);
33
-
34
-		$this->controller = $this->getMockBuilder(AuthPublicShareController::class)
35
-			->setConstructorArgs([
36
-				'app',
37
-				$this->request,
38
-				$this->session,
39
-				$this->urlGenerator
40
-			])->onlyMethods([
41
-				'authFailed',
42
-				'getPasswordHash',
43
-				'isAuthenticated',
44
-				'isPasswordProtected',
45
-				'isValidToken',
46
-				'showShare',
47
-				'verifyPassword',
48
-				'validateIdentity',
49
-				'generatePassword'
50
-			])->getMock();
51
-	}
52
-
53
-	public function testShowAuthenticate(): void {
54
-		$expects = new TemplateResponse('core', 'publicshareauth', [], 'guest');
55
-
56
-		$this->assertEquals($expects, $this->controller->showAuthenticate());
57
-	}
58
-
59
-	public function testAuthenticateAuthenticated(): void {
60
-		$this->controller->method('isAuthenticated')
61
-			->willReturn(true);
62
-
63
-		$this->controller->setToken('myToken');
64
-
65
-		$this->session->method('get')
66
-			->willReturnMap([
67
-				['public_link_authenticate_redirect', json_encode(['foo' => 'bar'])],
68
-			]);
69
-
70
-		$this->urlGenerator->method('linkToRoute')
71
-			->willReturn('myLink!');
72
-
73
-		$result = $this->controller->authenticate('password');
74
-		$this->assertInstanceOf(RedirectResponse::class, $result);
75
-		$this->assertSame('myLink!', $result->getRedirectURL());
76
-	}
77
-
78
-	public function testAuthenticateInvalidPassword(): void {
79
-		$this->controller->setToken('token');
80
-		$this->controller->method('isPasswordProtected')
81
-			->willReturn(true);
82
-
83
-		$this->controller->method('verifyPassword')
84
-			->with('password')
85
-			->willReturn(false);
86
-
87
-		$this->controller->expects($this->once())
88
-			->method('authFailed');
89
-
90
-		$expects = new TemplateResponse('core', 'publicshareauth', ['wrongpw' => true], 'guest');
91
-		$expects->throttle();
92
-
93
-		$result = $this->controller->authenticate('password');
94
-
95
-		$this->assertEquals($expects, $result);
96
-	}
97
-
98
-	public function testAuthenticateValidPassword(): void {
99
-		$this->controller->setToken('token');
100
-		$this->controller->method('isPasswordProtected')
101
-			->willReturn(true);
102
-		$this->controller->method('verifyPassword')
103
-			->with('password')
104
-			->willReturn(true);
105
-		$this->controller->method('getPasswordHash')
106
-			->willReturn('hash');
107
-
108
-		$this->session->expects($this->once())
109
-			->method('regenerateId');
110
-		$this->session->method('get')
111
-			->willReturnMap([
112
-				['public_link_authenticate_redirect', json_encode(['foo' => 'bar'])],
113
-			]);
114
-
115
-		$tokenStored = false;
116
-		$this->session
117
-			->method('set')
118
-			->willReturnCallback(function ($key, $value) use (&$tokenStored) {
119
-				if ($key === AuthPublicShareController::DAV_AUTHENTICATED_FRONTEND) {
120
-					$decoded = json_decode($value, true);
121
-					if (isset($decoded['token']) && $decoded['token'] === 'hash') {
122
-						$tokenStored = true;
123
-					}
124
-					return true;
125
-				}
126
-				return false;
127
-			});
128
-
129
-		$this->urlGenerator->method('linkToRoute')
130
-			->willReturn('myLink!');
131
-
132
-		$result = $this->controller->authenticate('password');
133
-		$this->assertInstanceOf(RedirectResponse::class, $result);
134
-		$this->assertSame('myLink!', $result->getRedirectURL());
135
-		$this->assertTrue($tokenStored);
136
-	}
21
+    private IRequest&MockObject $request;
22
+    private ISession&MockObject $session;
23
+    private IURLGenerator&MockObject $urlGenerator;
24
+    private AuthPublicShareController&MockObject $controller;
25
+
26
+
27
+    protected function setUp(): void {
28
+        parent::setUp();
29
+
30
+        $this->request = $this->createMock(IRequest::class);
31
+        $this->session = $this->createMock(ISession::class);
32
+        $this->urlGenerator = $this->createMock(IURLGenerator::class);
33
+
34
+        $this->controller = $this->getMockBuilder(AuthPublicShareController::class)
35
+            ->setConstructorArgs([
36
+                'app',
37
+                $this->request,
38
+                $this->session,
39
+                $this->urlGenerator
40
+            ])->onlyMethods([
41
+                'authFailed',
42
+                'getPasswordHash',
43
+                'isAuthenticated',
44
+                'isPasswordProtected',
45
+                'isValidToken',
46
+                'showShare',
47
+                'verifyPassword',
48
+                'validateIdentity',
49
+                'generatePassword'
50
+            ])->getMock();
51
+    }
52
+
53
+    public function testShowAuthenticate(): void {
54
+        $expects = new TemplateResponse('core', 'publicshareauth', [], 'guest');
55
+
56
+        $this->assertEquals($expects, $this->controller->showAuthenticate());
57
+    }
58
+
59
+    public function testAuthenticateAuthenticated(): void {
60
+        $this->controller->method('isAuthenticated')
61
+            ->willReturn(true);
62
+
63
+        $this->controller->setToken('myToken');
64
+
65
+        $this->session->method('get')
66
+            ->willReturnMap([
67
+                ['public_link_authenticate_redirect', json_encode(['foo' => 'bar'])],
68
+            ]);
69
+
70
+        $this->urlGenerator->method('linkToRoute')
71
+            ->willReturn('myLink!');
72
+
73
+        $result = $this->controller->authenticate('password');
74
+        $this->assertInstanceOf(RedirectResponse::class, $result);
75
+        $this->assertSame('myLink!', $result->getRedirectURL());
76
+    }
77
+
78
+    public function testAuthenticateInvalidPassword(): void {
79
+        $this->controller->setToken('token');
80
+        $this->controller->method('isPasswordProtected')
81
+            ->willReturn(true);
82
+
83
+        $this->controller->method('verifyPassword')
84
+            ->with('password')
85
+            ->willReturn(false);
86
+
87
+        $this->controller->expects($this->once())
88
+            ->method('authFailed');
89
+
90
+        $expects = new TemplateResponse('core', 'publicshareauth', ['wrongpw' => true], 'guest');
91
+        $expects->throttle();
92
+
93
+        $result = $this->controller->authenticate('password');
94
+
95
+        $this->assertEquals($expects, $result);
96
+    }
97
+
98
+    public function testAuthenticateValidPassword(): void {
99
+        $this->controller->setToken('token');
100
+        $this->controller->method('isPasswordProtected')
101
+            ->willReturn(true);
102
+        $this->controller->method('verifyPassword')
103
+            ->with('password')
104
+            ->willReturn(true);
105
+        $this->controller->method('getPasswordHash')
106
+            ->willReturn('hash');
107
+
108
+        $this->session->expects($this->once())
109
+            ->method('regenerateId');
110
+        $this->session->method('get')
111
+            ->willReturnMap([
112
+                ['public_link_authenticate_redirect', json_encode(['foo' => 'bar'])],
113
+            ]);
114
+
115
+        $tokenStored = false;
116
+        $this->session
117
+            ->method('set')
118
+            ->willReturnCallback(function ($key, $value) use (&$tokenStored) {
119
+                if ($key === AuthPublicShareController::DAV_AUTHENTICATED_FRONTEND) {
120
+                    $decoded = json_decode($value, true);
121
+                    if (isset($decoded['token']) && $decoded['token'] === 'hash') {
122
+                        $tokenStored = true;
123
+                    }
124
+                    return true;
125
+                }
126
+                return false;
127
+            });
128
+
129
+        $this->urlGenerator->method('linkToRoute')
130
+            ->willReturn('myLink!');
131
+
132
+        $result = $this->controller->authenticate('password');
133
+        $this->assertInstanceOf(RedirectResponse::class, $result);
134
+        $this->assertSame('myLink!', $result->getRedirectURL());
135
+        $this->assertTrue($tokenStored);
136
+    }
137 137
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -115,7 +115,7 @@
 block discarded – undo
115 115
 		$tokenStored = false;
116 116
 		$this->session
117 117
 			->method('set')
118
-			->willReturnCallback(function ($key, $value) use (&$tokenStored) {
118
+			->willReturnCallback(function($key, $value) use (&$tokenStored) {
119 119
 				if ($key === AuthPublicShareController::DAV_AUTHENTICATED_FRONTEND) {
120 120
 					$decoded = json_decode($value, true);
121 121
 					if (isset($decoded['token']) && $decoded['token'] === 'hash') {
Please login to merge, or discard this patch.