Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use OCA\Files\AppInfo\Application;
use OCP\L10N\IFactory;
use OCP\Sharing\Property\ABooleanSharePropertyType;
use OCP\Sharing\Share;

final class NodeGridViewSharePropertyType extends ABooleanSharePropertyType {
#[\Override]
Expand All @@ -35,12 +36,12 @@ public function isAdvanced(): bool {
}

#[\Override]
public function isRequired(): bool {
public function isRequired(Share $share): bool {
return false;
}

#[\Override]
public function getDefaultValue(): string {
public function getDefaultValue(Share $share): string {
return 'false';
}
}
133 changes: 78 additions & 55 deletions apps/sharing/lib/SharingBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -813,34 +813,6 @@ private function list(ShareAccessContext $accessContext, ?string $filterShareID,
}
}

foreach (array_keys($shares) as $id) {
foreach ($registryPropertyTypes as $propertyTypeClass => $propertyType) {
if (
!isset($shares[$id]['properties'][$propertyTypeClass])
&& isset($shareSourceTypeClasses[$id], $shareRecipientTypeClasses[$id])
&& array_intersect($registryPropertyTypeCompatibleSourceTypeClasses[$propertyTypeClass], array_keys($shareSourceTypeClasses[$id])) !== []
&& array_intersect($registryPropertyTypeCompatibleRecipientTypeClasses[$propertyTypeClass], array_keys($shareRecipientTypeClasses[$id])) !== []) {
$value = $propertyType->getDefaultValue();

$timestamp = $this->manager->generateTimestamp();
$this->setLastUpdated([(string)$id], $timestamp);

$qb = $this->connection->getQueryBuilder();
$qb
->insert('sharing_share_properties')
->values([
'share_id' => $qb->createNamedParameter($id),
'property_class' => $qb->createNamedParameter($propertyTypeClass),
'property_value' => $qb->createNamedParameter($propertyType instanceof ISharePropertyTypeModifyValue ? $propertyType->modifyValueOnSave(null, $value) : $value),
])
->executeStatement();

$shares[$id]['properties'][$propertyTypeClass] = new ShareProperty($propertyTypeClass, $value);
$shares[$id]['last_updated'] = $timestamp;
}
}
}

$registrySourceTypePermissionTypeClasses = $this->registry->getSourceTypePermissionTypeClasses();
$registryGenericPermissionTypeClasses = $this->registry->getGenericPermissionTypeClasses();

Expand Down Expand Up @@ -891,33 +863,6 @@ private function list(ShareAccessContext $accessContext, ?string $filterShareID,
}
}

$permissionTypes = $this->registry->getPermissionTypes();

foreach (array_keys($shares) as $id) {
foreach (array_keys($shareCompatiblePermissionTypeClasses[$id]) as $permissionTypeClass) {
$permissionType = $permissionTypes[$permissionTypeClass];
if (!isset($shares[$id]['permissions'][$permissionTypeClass])) {
$enabled = $permissionType->isEnabledByDefault();

$timestamp = $this->manager->generateTimestamp();
$this->setLastUpdated([(string)$id], $timestamp);

$qb = $this->connection->getQueryBuilder();
$qb
->insert('sharing_share_permissions')
->values([
'share_id' => $qb->createNamedParameter($id),
'permission_class' => $qb->createNamedParameter($permissionTypeClass),
'permission_enabled' => $qb->createNamedParameter($enabled, IQueryBuilder::PARAM_BOOL),
])
->executeStatement();

$shares[$id]['permissions'][$permissionTypeClass] = new SharePermission($permissionTypeClass, $enabled);
$shares[$id]['last_updated'] = $timestamp;
}
}
}

$shares = array_map(static fn (array $share): Share => new Share(
$share['id'],
$share['owner'],
Expand Down Expand Up @@ -948,6 +893,84 @@ private function list(ShareAccessContext $accessContext, ?string $filterShareID,
}
}

foreach (array_keys($shares) as $id) {
foreach ($registryPropertyTypes as $propertyTypeClass => $propertyType) {
$share = $shares[$id];
if (
!isset($share->properties[$propertyTypeClass])
&& isset($shareSourceTypeClasses[$id], $shareRecipientTypeClasses[$id])
&& array_intersect($registryPropertyTypeCompatibleSourceTypeClasses[$propertyTypeClass], array_keys($shareSourceTypeClasses[$id])) !== []
&& array_intersect($registryPropertyTypeCompatibleRecipientTypeClasses[$propertyTypeClass], array_keys($shareRecipientTypeClasses[$id])) !== []) {
$value = $propertyType->getDefaultValue($share);

$timestamp = $this->manager->generateTimestamp();
$this->setLastUpdated([(string)$id], $timestamp);

$qb = $this->connection->getQueryBuilder();
$qb
->insert('sharing_share_properties')
->values([
'share_id' => $qb->createNamedParameter($id),
'property_class' => $qb->createNamedParameter($propertyTypeClass),
'property_value' => $qb->createNamedParameter($propertyType instanceof ISharePropertyTypeModifyValue ? $propertyType->modifyValueOnSave(null, $value) : $value),
])
->executeStatement();

$properties = $share->properties;
$properties[$propertyTypeClass] = new ShareProperty($propertyTypeClass, $value);

$shares[$id] = new Share(
$share->id,
$share->owner,
$timestamp,
$share->state,
$share->sources,
$share->recipients,
$properties,
$share->permissions,
);
}
}
}

$permissionTypes = $this->registry->getPermissionTypes();
foreach (array_keys($shares) as $id) {
foreach (array_keys($shareCompatiblePermissionTypeClasses[$id]) as $permissionTypeClass) {
$share = $shares[$id];
if (!isset($share->permissions[$permissionTypeClass])) {
$permissionType = $permissionTypes[$permissionTypeClass];
$enabled = $permissionType->isEnabledByDefault();

$timestamp = $this->manager->generateTimestamp();
$this->setLastUpdated([(string)$id], $timestamp);

$qb = $this->connection->getQueryBuilder();
$qb
->insert('sharing_share_permissions')
->values([
'share_id' => $qb->createNamedParameter($id),
'permission_class' => $qb->createNamedParameter($permissionTypeClass),
'permission_enabled' => $qb->createNamedParameter($enabled, IQueryBuilder::PARAM_BOOL),
])
->executeStatement();

$permissions = $share->permissions;
$permissions[$permissionTypeClass] = new SharePermission($permissionTypeClass, $enabled);

$shares[$id] = new Share(
$share->id,
$share->owner,
$timestamp,
$share->state,
$share->sources,
$share->recipients,
$share->properties,
$permissions,
);
}
}
}

return array_values($shares);
}
}
63 changes: 47 additions & 16 deletions core/Sharing/Property/ExpirationDateSharePropertyType.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
use DateTimeImmutable;
use DateTimeInterface;
use OC\Core\AppInfo\Application;
use OC\Core\Sharing\Recipient\EmailShareRecipientType;
use OC\Core\Sharing\Recipient\TokenShareRecipientType;
use OCP\L10N\IFactory;
use OCP\Share\IManager;
use OCP\Sharing\Property\ADateSharePropertyType;
Expand Down Expand Up @@ -52,51 +54,50 @@ public function isAdvanced(): bool {
}

#[\Override]
public function isRequired(): bool {
if ($this->legacyManager->shareApiLinkDefaultExpireDateEnforced()) {
public function isRequired(Share $share): bool {
if ($this->hasTokenOrEmailRecipient($share) && $this->legacyManager->shareApiLinkDefaultExpireDateEnforced()) {
return true;
}

if ($this->legacyManager->shareApiRemoteDefaultExpireDateEnforced()) {
if ($this->hasRemoteRecipient($share) && $this->legacyManager->shareApiRemoteDefaultExpireDateEnforced()) {
return true;
}

return $this->legacyManager->shareApiInternalDefaultExpireDateEnforced();
return $this->hasLocalNonTokenAndEmailRecipient($share) && $this->legacyManager->shareApiInternalDefaultExpireDateEnforced();
}

#[\Override]
public function getDefaultValue(): ?string {
return $this->getMaxExpirationDate()?->format(DateTimeInterface::ATOM);
public function getDefaultValue(Share $share): ?string {
return $this->getMaxExpirationDate($share)?->format(DateTimeInterface::ATOM);
}

#[\Override]
public function getMinDate(): \DateTimeImmutable {
public function getMinDate(Share $share): \DateTimeImmutable {
// Ensure the expiration date is in the future.
return $this->now->add(new DateInterval('PT5M'));
}

#[\Override]
public function getMaxDate(): ?DateTimeImmutable {
if ($this->isRequired()) {
public function getMaxDate(Share $share): ?DateTimeImmutable {
if ($this->isRequired($share)) {
// Allow some time to pass between the user getting the max date and saving the date, as the time will shift in between.
return $this->getMaxExpirationDate()?->add(new DateInterval('PT5M'));
return $this->getMaxExpirationDate($share)?->add(new DateInterval('PT5M'));
}

return null;
}

private function getMaxExpirationDate(): ?DateTimeImmutable {
// We do not have any distinction between link/remote/internal, so we just apply the lowest expiration days count to be safe.
private function getMaxExpirationDate(Share $share): ?DateTimeImmutable {
$days = INF;
if ($this->legacyManager->shareApiLinkDefaultExpireDate()) {

if ($this->hasTokenOrEmailRecipient($share) && $this->legacyManager->shareApiLinkDefaultExpireDate()) {
$days = min($days, $this->legacyManager->shareApiLinkDefaultExpireDays());
}

if ($this->legacyManager->shareApiRemoteDefaultExpireDate()) {
if ($this->hasRemoteRecipient($share) && $this->legacyManager->shareApiRemoteDefaultExpireDate()) {
$days = min($days, $this->legacyManager->shareApiRemoteDefaultExpireDays());
}

if ($this->legacyManager->shareApiInternalDefaultExpireDate()) {
if ($this->hasLocalNonTokenAndEmailRecipient($share) && $this->legacyManager->shareApiInternalDefaultExpireDate()) {
$days = min($days, $this->legacyManager->shareApiInternalDefaultExpireDays());
}

Expand All @@ -120,4 +121,34 @@ public function isFiltered(ShareAccessContext $accessContext, Share $share): boo

return false;
}

private function hasTokenOrEmailRecipient(Share $share): bool {
foreach ($share->recipients as $recipient) {
if ($recipient->class === TokenShareRecipientType::class || $recipient->class === EmailShareRecipientType::class) {
return true;
}
}

return false;
}

private function hasRemoteRecipient(Share $share): bool {
foreach ($share->recipients as $recipient) {
if ($recipient->instance !== null) {
return true;
}
}

return false;
}

private function hasLocalNonTokenAndEmailRecipient(Share $share): bool {
foreach ($share->recipients as $recipient) {
if ($recipient->instance === null && $recipient->class !== TokenShareRecipientType::class && $recipient->class !== EmailShareRecipientType::class) {
return true;
}
}

return false;
}
}
5 changes: 3 additions & 2 deletions core/Sharing/Property/LabelSharePropertyType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use OC\Core\AppInfo\Application;
use OCP\L10N\IFactory;
use OCP\Sharing\Property\AStringSharePropertyType;
use OCP\Sharing\Share;

final class LabelSharePropertyType extends AStringSharePropertyType {
#[\Override]
Expand All @@ -35,12 +36,12 @@ public function isAdvanced(): bool {
}

#[\Override]
public function isRequired(): bool {
public function isRequired(Share $share): bool {
return false;
}

#[\Override]
public function getDefaultValue(): ?string {
public function getDefaultValue(Share $share): ?string {
return null;
}

Expand Down
5 changes: 3 additions & 2 deletions core/Sharing/Property/NoteSharePropertyType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use OC\Core\AppInfo\Application;
use OCP\L10N\IFactory;
use OCP\Sharing\Property\AStringSharePropertyType;
use OCP\Sharing\Share;

final class NoteSharePropertyType extends AStringSharePropertyType {
#[\Override]
Expand All @@ -35,12 +36,12 @@ public function isAdvanced(): bool {
}

#[\Override]
public function isRequired(): bool {
public function isRequired(Share $share): bool {
return false;
}

#[\Override]
public function getDefaultValue(): ?string {
public function getDefaultValue(Share $share): ?string {
return null;
}

Expand Down
6 changes: 3 additions & 3 deletions core/Sharing/Property/PasswordSharePropertyType.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ public function isAdvanced(): bool {
}

#[\Override]
public function isRequired(): bool {
public function isRequired(Share $share): bool {
// TODO: Enable group memberships check based on the owner.
return $this->legacyManager->shareApiLinkEnforcePassword(false);
}

#[\Override]
public function getDefaultValue(): ?string {
if (!$this->isRequired()) {
public function getDefaultValue(Share $share): ?string {
if (!$this->isRequired($share)) {
return null;
}

Expand Down
10 changes: 7 additions & 3 deletions lib/private/Sharing/SharingManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,11 @@ public function updateShareProperty(ShareAccessContext $accessContext, string $i
throw new RuntimeException('The property is not registered: ' . $property->class);
}

if ($property->value !== null && ($message = $propertyType->validateValue($this->l10nFactory, $property->value)) !== true) {
throw new ShareInvalidException('Invalid property value: ' . $property->value . ' ' . $property->class, $message);
if ($property->value !== null) {
$share = $this->getShare($accessContext, $id);
if (($message = $propertyType->validateValue($this->l10nFactory, $share, $property->value)) !== true) {
throw new ShareInvalidException('Invalid property value: ' . $property->value . ' ' . $property->class, $message);
}
}

$backend->updateShareProperty($id, $property);
Expand Down Expand Up @@ -547,6 +550,7 @@ private function getBackend(?string $id): ISharingBackend {
}

// TODO: Support IShareOwnerlessMount

/**
* @throws ShareOperationForbiddenException
*/
Expand Down Expand Up @@ -668,7 +672,7 @@ private function assertShareCanBeActive(Share $share): void {
$propertyTypes = $this->registry->getPropertyTypes();
foreach ($share->properties as $propertyTypeClass => $property) {
$propertyType = $propertyTypes[$propertyTypeClass];
if ($property->value === null && $propertyType->isRequired()) {
if ($property->value === null && $propertyType->isRequired($share)) {
throw new ShareInvalidException('Missing value for required property: ' . $propertyTypeClass, $this->l10n->t('You need to set a value for the %s', [$propertyType->getDisplayName($this->l10nFactory)]));
}
}
Expand Down
4 changes: 0 additions & 4 deletions lib/private/Sharing/SharingRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,6 @@ public function getPropertyTypeCompatibleRecipientTypes(): array {
public function registerPropertyType(ISharePropertyType $propertyType): void {
$class = $propertyType::class;

if ($propertyType->isRequired() && $propertyType->getDefaultValue() === null) {
throw new RuntimeException('Share property type ' . $class . ' is required, but has no default value.');
}

if (isset($this->propertyTypes[$class])) {
throw new RuntimeException('Share property type ' . $class . ' is already registered');
}
Expand Down
Loading
Loading