Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[stable25] Fix viewer in public albums #1630

Merged
merged 7 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions js/photos-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/photos-main.js.map

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/photos-public.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/photos-public.js.map

Large diffs are not rendered by default.

78 changes: 45 additions & 33 deletions lib/Sabre/PublicAlbumAuthBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,46 +21,58 @@
*/
namespace OCA\Photos\Sabre;

use OC\Security\Bruteforce\Throttler;
use OCA\Photos\Album\AlbumMapper;
use OCP\IRequest;
use Sabre\DAV\Auth\Backend\AbstractBasic;
use Sabre\DAV\Auth\Backend\BackendInterface;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;

class PublicAlbumAuthBackend extends AbstractBasic {
private const BRUTEFORCE_ACTION = 'publicphotos_webdav_auth';
private IRequest $request;
private AlbumMapper $albumMapper;
private Throttler $throttler;

public function __construct(
IRequest $request,
AlbumMapper $albumMapper,
Throttler $throttler
) {
$this->request = $request;
$this->albumMapper = $albumMapper;
$this->throttler = $throttler;
class PublicAlbumAuthBackend implements BackendInterface {
public function __construct() {
}

/**
* Validates the token.
* When this method is called, the backend must check if authentication was
* successful.
*
* The returned value must be one of the following
*
* [true, "principals/username"]
* [false, "reason for failure"]
*
* If authentication was successful, it's expected that the authentication
* backend returns a so-called principal url.
*
* Examples of a principal url:
*
* principals/admin
* principals/user1
* principals/users/joe
* principals/uid/123457
*
* If you don't use WebDAV ACL (RFC3744) we recommend that you simply
* return a string such as:
*
* principals/users/[username]
*
* @param string $username
* @return bool
* @throws \Sabre\DAV\Exception\NotAuthenticated
* @return array
*/
protected function validateUserPass($username, $password) {
$this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);

$albums = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($username, AlbumMapper::TYPE_LINK);

if (count($albums) !== 1) {
$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
return false;
}

public function check(RequestInterface $request, ResponseInterface $response) {
\OC_User::setIncognitoMode(true);
return [true, "principals/token"];
}

return true;
/**
* This method is called when a user could not be authenticated, and
* authentication was required for the current request.
*
* This gives you the opportunity to set authentication headers. The 401
* status code will already be set.
*
* Keep in mind that in the case of multiple authentication backends, other
* WWW-Authenticate headers may already have been set, and you'll want to
* append your own WWW-Authenticate header instead of overwriting the
* existing one.
*/
public function challenge(RequestInterface $request, ResponseInterface $response) {
// This is intended to be public - there is no need to set WWW-Authenticate header
}
}
14 changes: 13 additions & 1 deletion lib/Sabre/PublicRootCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,39 @@

namespace OCA\Photos\Sabre;

use OC\Security\Bruteforce\Throttler;
use OCA\Photos\Album\AlbumMapper;
use OCA\Photos\Sabre\Album\PublicAlbumRoot;
use OCA\Photos\Service\UserConfigService;
use OCP\IRequest;
use OCP\Files\IRootFolder;
use Sabre\DAVACL\AbstractPrincipalCollection;
use Sabre\DAVACL\PrincipalBackend;
use Sabre\DAV\Exception\NotFound;

class PublicRootCollection extends AbstractPrincipalCollection {
private const BRUTEFORCE_ACTION = 'publicphotos_webdav_auth';
private AlbumMapper $albumMapper;
private IRootFolder $rootFolder;
private UserConfigService $userConfigService;
private IRequest $request;
private Throttler $throttler;

public function __construct(
AlbumMapper $albumMapper,
IRootFolder $rootFolder,
PrincipalBackend\BackendInterface $principalBackend,
UserConfigService $userConfigService
UserConfigService $userConfigService,
IRequest $request,
Throttler $throttler
) {
parent::__construct($principalBackend, 'principals/token');

$this->albumMapper = $albumMapper;
$this->rootFolder = $rootFolder;
$this->userConfigService = $userConfigService;
$this->request = $request;
$this->throttler = $throttler;
}

public function getName(): string {
Expand All @@ -72,13 +81,16 @@ public function getChildForPrincipal(array $principalInfo): PublicAlbumRoot {
* @return DAV\INode
*/
public function getChild($token) {
$this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);

if (is_null($token)) {
throw new \Sabre\DAV\Exception\Forbidden();
}

$albums = $this->albumMapper->getSharedAlbumsForCollaboratorWithFiles($token, AlbumMapper::TYPE_LINK);

if (count($albums) !== 1) {
$this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
throw new NotFound("Unable to find public album");
}

Expand Down
9 changes: 8 additions & 1 deletion src/views/PublicAlbumContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ import Close from 'vue-material-design-icons/Close'
import { NcActions, NcActionButton, NcButton, NcEmptyContent, /** NcActionSeparator, */ isMobile } from '@nextcloud/vue'
import { showError } from '@nextcloud/dialogs'
import axios from '@nextcloud/axios'
import { generateRemoteUrl } from '@nextcloud/router'
import { generateUrl, generateRemoteUrl } from '@nextcloud/router'

import FetchFilesMixin from '../mixins/FetchFilesMixin.js'
import AbortControllerMixin from '../mixins/AbortControllerMixin.js'
Expand Down Expand Up @@ -262,6 +262,13 @@ export default {
const fileIds = fetchedFiles
.map(file => file.fileid.toString())

fetchedFiles.forEach(file => {
// Use custom preview URL to avoid authentication prompt
file.previewUrl = generateUrl(`/apps/photos/api/v1/publicPreview/${file.fileid}?x=2048&y=2048&token=${this.token}`)
// Disable use of generic file previews for public albums - for older versions of the Viewer app
file.hasPreview = false
})

this.appendFiles(fetchedFiles)

if (fetchedFiles.length > 0) {
Expand Down