mirror of
https://github.com/nextcloud/server.git
synced 2026-02-16 17:30:55 -05:00
Merge pull request #55962 from nextcloud/backport/55955/stable31
Some checks failed
Integration sqlite / changes (push) Waiting to run
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, --tags ~@large files_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, capabilities_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, collaboration_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, comments_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, dav_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, federation_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, file_conversions) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, filesdrop_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, ldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, openldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, openldap_numerical_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, remoteapi_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, setup_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, sharees_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, sharing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, theming_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, videoverification_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite-summary (push) Blocked by required conditions
Psalm static code analysis / static-code-analysis (push) Has been cancelled
Psalm static code analysis / static-code-analysis-security (push) Has been cancelled
Psalm static code analysis / static-code-analysis-ocp (push) Has been cancelled
Psalm static code analysis / static-code-analysis-ncu (push) Has been cancelled
Some checks failed
Integration sqlite / changes (push) Waiting to run
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, --tags ~@large files_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, capabilities_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, collaboration_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, comments_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, dav_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, federation_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, file_conversions) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, filesdrop_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, ldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, openldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, openldap_numerical_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, remoteapi_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, setup_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, sharees_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, sharing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, theming_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, videoverification_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite-summary (push) Blocked by required conditions
Psalm static code analysis / static-code-analysis (push) Has been cancelled
Psalm static code analysis / static-code-analysis-security (push) Has been cancelled
Psalm static code analysis / static-code-analysis-ocp (push) Has been cancelled
Psalm static code analysis / static-code-analysis-ncu (push) Has been cancelled
[stable31] fix(dav): allow multiple link shares token in session
This commit is contained in:
commit
63fb254b6b
9 changed files with 94 additions and 38 deletions
|
|
@ -54,8 +54,11 @@ class Auth extends AbstractBasic {
|
|||
* @see https://github.com/owncloud/core/issues/13245
|
||||
*/
|
||||
public function isDavAuthenticated(string $username): bool {
|
||||
return !is_null($this->session->get(self::DAV_AUTHENTICATED)) &&
|
||||
$this->session->get(self::DAV_AUTHENTICATED) === $username;
|
||||
if (is_null($this->session->get(self::DAV_AUTHENTICATED))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->session->get(self::DAV_AUTHENTICATED) === $username;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -134,8 +134,7 @@ class PublicAuth extends AbstractBasic {
|
|||
\OC_User::setIncognitoMode(true);
|
||||
|
||||
// If already authenticated
|
||||
if ($this->session->exists(self::DAV_AUTHENTICATED)
|
||||
&& $this->session->get(self::DAV_AUTHENTICATED) === $share->getId()) {
|
||||
if ($this->isShareInSession($share)) {
|
||||
return [true, $this->principalPrefix . $token];
|
||||
}
|
||||
|
||||
|
|
@ -177,17 +176,17 @@ class PublicAuth extends AbstractBasic {
|
|||
if ($share->getShareType() === IShare::TYPE_LINK
|
||||
|| $share->getShareType() === IShare::TYPE_EMAIL
|
||||
|| $share->getShareType() === IShare::TYPE_CIRCLE) {
|
||||
// Validate password if provided
|
||||
if ($this->shareManager->checkPassword($share, $password)) {
|
||||
// If not set, set authenticated session cookie
|
||||
if (!$this->session->exists(self::DAV_AUTHENTICATED)
|
||||
|| $this->session->get(self::DAV_AUTHENTICATED) !== $share->getId()) {
|
||||
$this->session->set(self::DAV_AUTHENTICATED, $share->getId());
|
||||
if (!$this->isShareInSession($share)) {
|
||||
$this->addShareToSession($share);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->session->exists(PublicAuth::DAV_AUTHENTICATED)
|
||||
&& $this->session->get(PublicAuth::DAV_AUTHENTICATED) === $share->getId()) {
|
||||
// We are already authenticated for this share in the session
|
||||
if ($this->isShareInSession($share)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -221,4 +220,27 @@ class PublicAuth extends AbstractBasic {
|
|||
|
||||
return $this->share;
|
||||
}
|
||||
|
||||
private function addShareToSession(IShare $share): void {
|
||||
$allowedShareIds = $this->session->get(self::DAV_AUTHENTICATED) ?? [];
|
||||
if (!is_array($allowedShareIds)) {
|
||||
$allowedShareIds = [];
|
||||
}
|
||||
|
||||
$allowedShareIds[] = $share->getId();
|
||||
$this->session->set(self::DAV_AUTHENTICATED, $allowedShareIds);
|
||||
}
|
||||
|
||||
private function isShareInSession(IShare $share): bool {
|
||||
if (!$this->session->exists(self::DAV_AUTHENTICATED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$allowedShareIds = $this->session->get(self::DAV_AUTHENTICATED);
|
||||
if (!is_array($allowedShareIds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($share->getId(), $allowedShareIds);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ class PublicAuthTest extends \Test\TestCase {
|
|||
)->willReturn(false);
|
||||
|
||||
$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
|
||||
$this->session->method('get')->with('public_link_authenticated')->willReturn('42');
|
||||
$this->session->method('get')->with('public_link_authenticated')->willReturn(['42']);
|
||||
|
||||
$result = $this->invokePrivate($this->auth, 'validateUserPass', ['username', 'password']);
|
||||
|
||||
|
|
|
|||
|
|
@ -89,9 +89,15 @@ class MountPublicLinkController extends Controller {
|
|||
}
|
||||
|
||||
// make sure that user is authenticated in case of a password protected link
|
||||
$allowedShareIds = $this->session->get(PublicAuth::DAV_AUTHENTICATED);
|
||||
if (!is_array($allowedShareIds)) {
|
||||
$allowedShareIds = [];
|
||||
}
|
||||
|
||||
$authenticated = in_array($share->getId(), $allowedShareIds)
|
||||
|| $this->shareManager->checkPassword($share, $password);
|
||||
|
||||
$storedPassword = $share->getPassword();
|
||||
$authenticated = $this->session->get(PublicAuth::DAV_AUTHENTICATED) === $share->getId() ||
|
||||
$this->shareManager->checkPassword($share, $password);
|
||||
if (!empty($storedPassword) && !$authenticated) {
|
||||
$response = new JSONResponse(
|
||||
['message' => 'No permission to access the share'],
|
||||
|
|
|
|||
|
|
@ -196,7 +196,12 @@ class ShareController extends AuthPublicShareController {
|
|||
}
|
||||
|
||||
// For share this was always set so it is still used in other apps
|
||||
$this->session->set(PublicAuth::DAV_AUTHENTICATED, $this->share->getId());
|
||||
$allowedShareIds = $this->session->get(PublicAuth::DAV_AUTHENTICATED);
|
||||
if (!is_array($allowedShareIds)) {
|
||||
$allowedShareIds = [];
|
||||
}
|
||||
|
||||
$this->session->set(PublicAuth::DAV_AUTHENTICATED, array_merge($allowedShareIds, [$this->share->getId()]));
|
||||
}
|
||||
|
||||
protected function authFailed() {
|
||||
|
|
|
|||
|
|
@ -158,8 +158,7 @@ abstract class AuthPublicShareController extends PublicShareController {
|
|||
$this->session->regenerateId(true, true);
|
||||
$response = $this->getRedirect();
|
||||
|
||||
$this->session->set('public_link_authenticated_token', $this->getToken());
|
||||
$this->session->set('public_link_authenticated_password_hash', $this->getPasswordHash());
|
||||
$this->storeTokenSession($this->getToken(), $this->getPasswordHash());
|
||||
|
||||
$this->authSucceeded();
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ use OCP\ISession;
|
|||
* @since 14.0.0
|
||||
*/
|
||||
abstract class PublicShareController extends Controller {
|
||||
/** @var ISession */
|
||||
protected $session;
|
||||
|
||||
public const DAV_AUTHENTICATED_FRONTEND = 'public_link_authenticated_frontend';
|
||||
|
||||
/** @var string */
|
||||
private $token;
|
||||
|
|
@ -34,12 +34,12 @@ abstract class PublicShareController extends Controller {
|
|||
/**
|
||||
* @since 14.0.0
|
||||
*/
|
||||
public function __construct(string $appName,
|
||||
public function __construct(
|
||||
string $appName,
|
||||
IRequest $request,
|
||||
ISession $session) {
|
||||
protected ISession $session,
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -98,8 +98,7 @@ abstract class PublicShareController extends Controller {
|
|||
}
|
||||
|
||||
// If we are authenticated properly
|
||||
if ($this->session->get('public_link_authenticated_token') === $this->getToken() &&
|
||||
$this->session->get('public_link_authenticated_password_hash') === $this->getPasswordHash()) {
|
||||
if ($this->validateTokenSession($this->getToken(), $this->getPasswordHash())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -116,4 +115,31 @@ abstract class PublicShareController extends Controller {
|
|||
*/
|
||||
public function shareNotFound() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the token and password hash stored in session
|
||||
*/
|
||||
protected function validateTokenSession(string $token, string $passwordHash): bool {
|
||||
$allowedTokensJSON = $this->session->get(self::DAV_AUTHENTICATED_FRONTEND) ?? '[]';
|
||||
$allowedTokens = json_decode($allowedTokensJSON, true);
|
||||
if (!is_array($allowedTokens)) {
|
||||
$allowedTokens = [];
|
||||
}
|
||||
|
||||
return ($allowedTokens[$token] ?? '') === $passwordHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the token and password hash in session
|
||||
*/
|
||||
protected function storeTokenSession(string $token, string $passwordHash = ''): void {
|
||||
$allowedTokensJSON = $this->session->get(self::DAV_AUTHENTICATED_FRONTEND) ?? '[]';
|
||||
$allowedTokens = json_decode($allowedTokensJSON, true);
|
||||
if (!is_array($allowedTokens)) {
|
||||
$allowedTokens = [];
|
||||
}
|
||||
|
||||
$allowedTokens[$token] = $passwordHash;
|
||||
$this->session->set(self::DAV_AUTHENTICATED_FRONTEND, json_encode($allowedTokens));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,17 +109,15 @@ class AuthPublicShareControllerTest extends \Test\TestCase {
|
|||
$this->session->method('get')
|
||||
->willReturnMap(['public_link_authenticate_redirect', ['foo' => 'bar']]);
|
||||
|
||||
$tokenSet = false;
|
||||
$hashSet = false;
|
||||
$tokenStored = false;
|
||||
$this->session
|
||||
->method('set')
|
||||
->willReturnCallback(function ($key, $value) use (&$tokenSet, &$hashSet) {
|
||||
if ($key === 'public_link_authenticated_token' && $value === 'token') {
|
||||
$tokenSet = true;
|
||||
return true;
|
||||
}
|
||||
if ($key === 'public_link_authenticated_password_hash' && $value === 'hash') {
|
||||
$hashSet = true;
|
||||
->willReturnCallback(function ($key, $value) use (&$tokenStored) {
|
||||
if ($key === AuthPublicShareController::DAV_AUTHENTICATED_FRONTEND) {
|
||||
$decoded = json_decode($value, true);
|
||||
if (isset($decoded['token']) && $decoded['token'] === 'hash') {
|
||||
$tokenStored = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -131,7 +129,6 @@ class AuthPublicShareControllerTest extends \Test\TestCase {
|
|||
$result = $this->controller->authenticate('password');
|
||||
$this->assertInstanceOf(RedirectResponse::class, $result);
|
||||
$this->assertSame('myLink!', $result->getRedirectURL());
|
||||
$this->assertTrue($tokenSet);
|
||||
$this->assertTrue($hashSet);
|
||||
$this->assertTrue($tokenStored);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,10 +77,8 @@ class PublicShareControllerTest extends \Test\TestCase {
|
|||
$controller = new TestController('app', $this->request, $this->session, $hash2, $protected);
|
||||
|
||||
$this->session->method('get')
|
||||
->willReturnMap([
|
||||
['public_link_authenticated_token', $token1],
|
||||
['public_link_authenticated_password_hash', $hash1],
|
||||
]);
|
||||
->with(PublicShareController::DAV_AUTHENTICATED_FRONTEND)
|
||||
->willReturn("{\"$token1\":\"$hash1\"}");
|
||||
|
||||
$controller->setToken($token2);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue