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

Fix 1411 visibility propagation #1454

Closed
wants to merge 12 commits into from
Closed
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
145 changes: 123 additions & 22 deletions app/Actions/Album/SetProtectionPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
namespace App\Actions\Album;

use App\DTO\AlbumProtectionPolicy;
use App\Enums\PolicyPropagationEnum;
use App\Exceptions\Internal\FrameworkException;
use App\Exceptions\InvalidPropertyException;
use App\Exceptions\ModelDBException;
use App\Models\Album;
use App\Models\Extensions\BaseAlbum;
use Illuminate\Contracts\Container\BindingResolutionException;

Expand All @@ -26,38 +28,137 @@ class SetProtectionPolicy extends Action
* @throws ModelDBException
* @throws FrameworkException
*/
public function do(BaseAlbum $album, AlbumProtectionPolicy $protectionPolicy, bool $shallSetPassword, ?string $password): void
public function do(BaseAlbum $album, AlbumProtectionPolicy $protectionPolicy, bool $shallSetPassword, ?string $password, PolicyPropagationEnum $propagateToChildren): void
{
$this->setAlbumProtectionPolicy($album, $protectionPolicy);
$this->setPassword($album, $shallSetPassword, $password);
$album->save();

// Reset permissions for photos
if ($album->is_public) {
$album->photos()->update(['photos.is_public' => false]);
}

if (!$album instanceof Album) {
return;
}

// Do propagation.
switch ($propagateToChildren) {
case PolicyPropagationEnum::cut():
// We remove the inherit from parent from all the direct descendents
$album->children()->update([
'inherits_protection_policy' => false,
]);
break;

case PolicyPropagationEnum::force():
// we propagate the current parent change to all children.
// Note that we do not propagate the require_link attribute
$album->descendants()->update([
'inherits_protection_policy' => true,
'is_public' => $album->is_public,
'is_downloadable' => $album->is_downloadable,
'grants_full_photo' => $album->grants_full_photo,
'is_share_button_visible' => $album->is_share_button_visible,
]);
if ($album->is_public) {
$album->all_photos()->update(['photos.is_public' => false]);
}
break;

// Normal refresh of the children
default:
case PolicyPropagationEnum::refresh():
$descendansToUpdate = $album->descendants()->where('inherits_protection_policy', '=', '1')->with('parent')->orderBy('_lft', 'asc')->get();
foreach ($descendansToUpdate as $descendant) {
$this->setAlbumPolicyFromParent($descendant);
$descendant->save();
}
break;
}
}

/**
* Given a protection policy, apply changes or infer from parent.
*
* @param BaseAlbum $album
* @param AlbumProtectionPolicy $protectionPolicy
*
* @return void
*/
private function setAlbumProtectionPolicy(BaseAlbum $album, AlbumProtectionPolicy $protectionPolicy): void
{
// In order to apply inheritance we must verify:
// - that it is an album and not a tag album.
// - and that the album is not at the root.
$album->inherits_protection_policy = $album instanceof Album && $album->inherits_protection_policy && $album->parent_id !== null;

$album->requires_link = $protectionPolicy->requiresLink;

/** @var Album $album : this is is infered, but phpStan does not "remember" it */
if ($album->inherits_protection_policy) {
$this->setAlbumPolicyFromParent($album);

return;
}

$album->grants_full_photo = $protectionPolicy->grantsFullPhoto;
$album->is_public = $protectionPolicy->isPublic;
$album->requires_link = $protectionPolicy->requiresLink;
$album->is_nsfw = $protectionPolicy->isNSFW;
$album->is_downloadable = $protectionPolicy->isDownloadable;
$album->is_share_button_visible = $protectionPolicy->isShareButtonVisible;
}

// Set password if provided
if ($shallSetPassword) {
// password is provided => there is a change
if ($password !== null) {
// password is not null => we update the value with the hash
try {
$album->password = bcrypt($password);
} catch (\InvalidArgumentException $e) {
throw new InvalidPropertyException('Could not hash password', $e);
} catch (BindingResolutionException $e) {
throw new FrameworkException('Laravel\'s hashing component', $e);
}
} else {
// we remove the password
$album->password = null;
}
/**
* Given an album, inherit the protection policy from the parent.
*
* @param Album $album
*
* @return void
*/
private function setAlbumPolicyFromParent(Album &$album): void
{
$album->grants_full_photo = $album->parent->grants_full_photo;
$album->is_public = $album->parent->is_public;
$album->is_nsfw = $album->parent->is_nsfw;
$album->is_downloadable = $album->parent->is_downloadable;
$album->is_share_button_visible = $album->parent->is_share_button_visible;
}

/**
* Update password if necessary.
*
* @param BaseAlbum $album
* @param bool $shallSetPassword
* @param string|null $password
*
* @return void
*
* @throws InvalidPropertyException
* @throws FrameworkException
*/
private function setPassword(BaseAlbum $album, bool $shallSetPassword, ?string $password): void
{
// password is not provided => there is no change
if (!$shallSetPassword) {
return;
}

$album->save();
// password is null => we remove the password
if ($password === null) {
$album->password = null;

// Reset permissions for photos
if ($album->is_public) {
$album->photos()->update(['photos.is_public' => false]);
return;
}

// password is not null => we update the value with the hash
try {
$album->password = bcrypt($password);
} catch (\InvalidArgumentException $e) {
throw new InvalidPropertyException('Could not hash password', $e);
} catch (BindingResolutionException $e) {
throw new FrameworkException('Laravel\'s hashing component', $e);
}
}
}
8 changes: 7 additions & 1 deletion app/DTO/AlbumProtectionPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,33 @@ class AlbumProtectionPolicy extends DTO
public const IS_DOWNLOADABLE_ATTRIBUTE = 'is_downloadable';
public const IS_SHARE_BUTTON_VISIBLE_ATTRIBUTE = 'is_share_button_visible';
public const GRANTS_FULL_PHOTO_ATTRIBUTE = 'grants_full_photo';
public const INHERITS_PROTECTION_POLICY = 'inherits_protection_policy';
public const PROPAGATE_TO_CHILDREN = 'propagate_to_children';

public bool $isPublic;
public bool $requiresLink;
public bool $isNSFW;
public bool $isDownloadable;
public bool $isShareButtonVisible;
public bool $grantsFullPhoto;
public bool $inheritsProtectionPolicy;

public function __construct(
bool $isPublic,
bool $requiresLink,
bool $isNSFW,
bool $isDownloadable,
bool $isShareButtonVisible,
bool $grantsFullPhoto
bool $grantsFullPhoto,
bool $inheritsProtectionPolicy,
) {
$this->isPublic = $isPublic;
$this->requiresLink = $requiresLink;
$this->isNSFW = $isNSFW;
$this->isDownloadable = $isDownloadable;
$this->isShareButtonVisible = $isShareButtonVisible;
$this->grantsFullPhoto = $grantsFullPhoto;
$this->inheritsProtectionPolicy = $inheritsProtectionPolicy;
}

/**
Expand All @@ -46,6 +51,7 @@ public function toArray(): array
self::IS_DOWNLOADABLE_ATTRIBUTE => $this->isDownloadable,
self::IS_SHARE_BUTTON_VISIBLE_ATTRIBUTE => $this->isShareButtonVisible,
self::GRANTS_FULL_PHOTO_ATTRIBUTE => $this->grantsFullPhoto,
self::INHERITS_PROTECTION_POLICY => $this->inheritsProtectionPolicy,
];
}
}
25 changes: 25 additions & 0 deletions app/Enums/PolicyPropagationEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace App\Enums;

use Spatie\Enum\Laravel\Enum;

/**
* @method static self refresh()
* @method static self cut()
* @method static self force()
*/
final class PolicyPropagationEnum extends Enum
{
/**
* @return array<string, int|string>
*/
protected static function values(): array
{
return [
'refresh' => 0,
'cut' => 1,
'force' => 2,
];
}
}
2 changes: 1 addition & 1 deletion app/Facades/AccessControl.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* @method static bool is_logged_in()
* @method static bool is_admin()
* @method static bool can_upload()
* @method static int id()
* @method static int id()
* @method static User accessUserData()
* @method static User user()
* @method static bool is_current_user_or_admin(int $userId)
Expand Down
14 changes: 7 additions & 7 deletions app/Facades/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
* @method static string getDeviceType()
* @method static string trancateIf32(string $id, int $prevShortId = 0)
* @method static string getExtension(string $filename, bool $isURI = false)
* @method static bool hasPermissions(string $path)
* @method static bool hasFullPermissions(string $path)
* @method static int gcd(int $a, int $b)
* @method static bool hasPermissions(string $path)
* @method static bool hasFullPermissions(string $path)
* @method static int gcd(int $a, int $b)
* @method static string str_of_bool(bool $b)
* @method static int data_index()
* @method static int data_index_r()
* @method static void data_index_set(int $idx = 0)
* @method static array get_all_licenses()
* @method static int data_index()
* @method static int data_index_r()
* @method static void data_index_set(int $idx = 0)
* @method static array get_all_licenses()
*/
class Helpers extends Facade
{
Expand Down
8 changes: 4 additions & 4 deletions app/Facades/Lang.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
*
* Keep the list of documented method in sync with {@link \App\Locale\Lang}.
*
* @method static string get(string $string)
* @method static string get_code()
* @method static string[] get_lang()
* @method static string[] get_lang_available()
* @method static string get(string $string)
* @method static string get_code()
* @method static string[] get_lang()
* @method static string[] get_lang_available()
* @method static LangFactory factory()
*/
class Lang extends Facade
Expand Down
3 changes: 2 additions & 1 deletion app/Http/Controllers/AlbumController.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ public function setProtectionPolicy(SetAlbumProtectionPolicyRequest $request, Se
$request->album(),
$request->albumProtectionPolicy(),
$request->isPasswordProvided(),
$request->password()
$request->password(),
$request->getPropagateToChildren()
);
}

Expand Down
12 changes: 12 additions & 0 deletions app/Http/Requests/Album/SetAlbumProtectionPolicyRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Http\Requests\Album;

use App\DTO\AlbumProtectionPolicy;
use App\Enums\PolicyPropagationEnum;
use App\Http\Requests\BaseApiRequest;
use App\Http\Requests\Contracts\HasAbstractAlbum;
use App\Http\Requests\Contracts\HasBaseAlbum;
Expand All @@ -11,13 +12,15 @@
use App\Http\Requests\Traits\HasPasswordTrait;
use App\Rules\PasswordRule;
use App\Rules\RandomIDRule;
use Spatie\Enum\Laravel\Rules\EnumRule;

class SetAlbumProtectionPolicyRequest extends BaseApiRequest implements HasBaseAlbum, HasPassword
{
use HasBaseAlbumTrait;
use HasPasswordTrait;

protected bool $isPasswordProvided;
protected PolicyPropagationEnum $propagateToChildren;
protected AlbumProtectionPolicy $albumAccessSettings;

/**
Expand All @@ -42,6 +45,8 @@ public function rules(): array
AlbumProtectionPolicy::IS_DOWNLOADABLE_ATTRIBUTE => 'required|boolean',
AlbumProtectionPolicy::IS_SHARE_BUTTON_VISIBLE_ATTRIBUTE => 'required|boolean',
AlbumProtectionPolicy::GRANTS_FULL_PHOTO_ATTRIBUTE => 'required|boolean',
AlbumProtectionPolicy::INHERITS_PROTECTION_POLICY => 'required|boolean',
AlbumProtectionPolicy::PROPAGATE_TO_CHILDREN => new EnumRule(PolicyPropagationEnum::class),
];
}

Expand All @@ -60,9 +65,11 @@ protected function processValidatedValues(array $values, array $files): void
static::toBoolean($values[AlbumProtectionPolicy::IS_DOWNLOADABLE_ATTRIBUTE]),
static::toBoolean($values[AlbumProtectionPolicy::IS_SHARE_BUTTON_VISIBLE_ATTRIBUTE]),
static::toBoolean($values[AlbumProtectionPolicy::GRANTS_FULL_PHOTO_ATTRIBUTE]),
static::toBoolean($values[AlbumProtectionPolicy::INHERITS_PROTECTION_POLICY]),
);
$this->isPasswordProvided = array_key_exists(HasPassword::PASSWORD_ATTRIBUTE, $values);
$this->password = $this->isPasswordProvided ? $values[HasPassword::PASSWORD_ATTRIBUTE] : null;
$this->propagateToChildren = PolicyPropagationEnum::from($values[AlbumProtectionPolicy::PROPAGATE_TO_CHILDREN]);
}

/**
Expand All @@ -77,4 +84,9 @@ public function isPasswordProvided(): bool
{
return $this->isPasswordProvided;
}

public function getPropagateToChildren(): PolicyPropagationEnum
{
return $this->propagateToChildren;
}
}
3 changes: 3 additions & 0 deletions app/Models/BaseAlbumImpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
* @property bool $requires_link
* @property bool $is_downloadable
* @property bool $is_share_button_visible
* @property bool $inherits_protection_policy
* @property bool $is_nsfw
* @property Collection $shared_with
* @property string|null $password
Expand Down Expand Up @@ -149,6 +150,7 @@ class BaseAlbumImpl extends Model implements HasRandomID
'requires_link' => false,
'is_downloadable' => false,
'is_share_button_visible' => false,
'inherits_protection_policy' => false,
'is_nsfw' => false,
'password' => null,
'sorting_col' => null,
Expand All @@ -166,6 +168,7 @@ class BaseAlbumImpl extends Model implements HasRandomID
'is_public' => 'boolean',
'requires_link' => 'boolean',
'is_nsfw' => 'boolean',
'inherits_protection_policy' => 'boolean',
'owner_id' => 'integer',
];

Expand Down
1 change: 1 addition & 0 deletions app/Models/Extensions/BaseAlbum.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* @property string|null $description
* @property bool $is_nsfw
* @property bool $grants_full_photo
* @property bool $inherits_protection_policy
* @property int $owner_id
* @property User $owner
* @property Collection $shared_with
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"php-http/guzzle7-adapter": "^1.0",
"php-http/message": "^1.12",
"spatie/guzzle-rate-limiter-middleware": "^2.0",
"spatie/laravel-enum": "^3.0",
"spatie/laravel-feed": "^4.0",
"spatie/laravel-image-optimizer": "^1.6.2",
"symfony/cache": "^v6.0.0",
Expand Down
Loading