Skip to content
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
6 changes: 4 additions & 2 deletions .lycheeignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,14 @@ https://securetoken.google.com/

file:///home/runner/work/solidui/solidui/lib/src/utils/scheme
https://pods.example.au/john-doe/profile/card#me
https://anushkavidanage.github.io/solidpod/example/client-profile.jsonld
https://anushkavidanage.github.io/solidpod/example/redirect.html
https://anusii.github.io/solidpodeg/client-profile.jsonld
https://github.com/gjwgit/myapp/blob/main/README.md
https://github.com/anusii/solidpod/blob/main/solidpodeg/README.md
https://server/POD_NAME/APP_NAME/data/FILE_PATH
https://server/alice/
https://server/alice/myapp/
https://anusii.github.io/solidpodeg/client-profile.jsonld

# 20260605 gjw Failing solid servers

Expand All @@ -97,4 +99,4 @@ https://api.open-meteo.com/v1/forecast

# 20260619 gjw konapod failures

https://blog.kumo.dev/2024/05/22/reverse_engineering_hkg_apps.html
https://blog.kumo.dev/2024/05/22/reverse_engineering_hkg_apps.html
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Visit the package at [pub.dev](https://pub.dev/packages/solidpod).

## 1.0

+ Switch public to private sharing [1.0.17 20260811 jesscmoore]
+ Preserve encryption state on overwrite [1.0.16 20260811 jesscmoore]
+ Fix deleting a large file stored as a single chunk [1.0.15 20260809 gjw]
+ Remove debug output when reading a large file [1.0.14 20260809 gjw]
Expand Down
55 changes: 55 additions & 0 deletions lib/src/solid/grant_permission.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ import 'package:solidpod/src/solid/api/rest_api.dart';
import 'package:solidpod/src/solid/constants/common.dart';
import 'package:solidpod/src/solid/constants/web_acl.dart';
import 'package:solidpod/src/solid/models/log_entry.dart';
import 'package:solidpod/src/solid/read_permission.dart'
show getUserClassPermissions;
import 'package:solidpod/src/solid/revoke_permission.dart'
show revokePermission;
import 'package:solidpod/src/solid/solid_func_call_status.dart';
import 'package:solidpod/src/solid/utils/exceptions.dart';
import 'package:solidpod/src/solid/utils/get_url_helper.dart';
Expand Down Expand Up @@ -70,6 +74,17 @@ import 'package:solidpod/src/solid/utils/permission.dart' show genAclTurtle;
/// - [isFile] Optional flag describing whether the resources is a file or
/// not.
/// - [groupName] - Optional name of the group permission.
/// - [revokePublicAccessOnSpecificGrant] - When [recipientType] is
/// individual or group and the resource currently has a Public or
/// Authenticated User class grant (and is therefore plaintext on the
/// server, per the decryption step this function performs for those
/// recipient classes), revoke that grant and re-encrypt the resource
/// before proceeding. Without this, the resource would end up both still
/// readable by the previously-granted class *and* holding a stale
/// individual key that does not match the (still plaintext) content.
/// Defaults to `true`; set to `false` to keep today's behaviour where
/// granting to a specific recipient never touches an existing
/// public/authUser grant.

Future<SolidFunctionCallStatus> grantPermission({
required String fileName,
Expand All @@ -81,6 +96,7 @@ Future<SolidFunctionCallStatus> grantPermission({
bool isFile = true,
bool isExternalRes = false,
String? groupName,
bool revokePublicAccessOnSpecificGrant = true,
}) async {
if (!await isUserLoggedIn()) {
throw NotLoggedInException(
Expand Down Expand Up @@ -150,6 +166,45 @@ Future<SolidFunctionCallStatus> grantPermission({
// if recipient pods have been initialised
if (allRecipientsInitialised || !hasSpecificRecipients) {
if (resStatus == ResourceStatus.exist) {
// Sharing to a specific individual/group assumes the resource is
// ciphertext under an individual key (see the `fileHasIndKey`
// branch below). If it's currently also granted to the Public or
// Authenticated User class, it's plaintext on the server (that
// class has no key of its own to decrypt with) — so revoke that
// grant and re-encrypt first. Otherwise the resource would end up
// both still openly readable *and* holding a stale individual key
// that doesn't match the (still plaintext) bytes. Must run before
// `setPermissionAcl` below, so `revokePermission`'s own ACL read
// still sees the pre-existing grant.
if (hasSpecificRecipients && revokePublicAccessOnSpecificGrant) {
final existingClassPerms = await getUserClassPermissions(
fileName: resourceUrl,
isFile: isFile,
isFileUrl: true,
isExternalRes: isExternalRes,
);
for (final classType in existingClassPerms.keys) {
final classAgent = classType == RecipientType.public
? publicAgent
: authenticatedAgent;
debugPrint(
'[grantPermission] revoking existing $classType access on '
'"$resourceUrl" before granting to $recipientType',
);
await revokePermission(
fileName: resourceUrl,
isFileUrl: true,
permissionList: existingClassPerms[classType]!,
recipientIndOrGroupWebId: classAgent.value,
recipientType: classType,
ownerWebId: ownerWebId,
granterWebId: granterWebId,
isFile: isFile,
isExternalRes: isExternalRes,
);
}
}

// Add the permission line to the relevant ACL file
await setPermissionAcl(
resourceUrl,
Expand Down
44 changes: 44 additions & 0 deletions lib/src/solid/read_permission.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ library;

import 'dart:core';

import 'package:rdflib/rdflib.dart';

import 'package:solidpod/src/solid/constants/common.dart'
show agentClassPred, agentStr, permStr;
import 'package:solidpod/src/solid/constants/web_acl.dart'
show RecipientType, authenticatedAgent, publicAgent;
import 'package:solidpod/src/solid/utils/get_url_helper.dart';
import 'package:solidpod/src/solid/utils/misc.dart';
import 'package:solidpod/src/solid/utils/permission.dart';
Expand Down Expand Up @@ -70,3 +76,41 @@ Future<Map<dynamic, dynamic>> readPermission({

return permMap;
}

/// The Public/Authenticated-User access modes currently granted on
/// [fileName], keyed by [RecipientType.public]/[RecipientType.authUser].
/// A class with no current grant is omitted from the result.
///
/// Single source of truth for "does this resource currently have a
/// Public/AuthenticatedUser grant" — used both by [grantPermission] (to
/// decide whether an individual/group grant must first revoke and
/// re-encrypt) and by solidui's confirmation dialog for the same action.

Future<Map<RecipientType, List<String>>> getUserClassPermissions({
required String fileName,
required bool isFile,
bool isFileUrl = false,
bool isExternalRes = false,
}) async {
final permMap = await readPermission(
fileName: fileName,
isFile: isFile,
isFileUrl: isFileUrl,
isExternalRes: isExternalRes,
);

final result = <RecipientType, List<String>>{};
for (final receiverId in permMap.keys) {
if (receiverId is! String ||
permMap[receiverId][agentStr] != agentClassPred) {
continue;
}
final perms = (permMap[receiverId][permStr] as List).cast<String>();
if (URIRef(receiverId) == publicAgent) {
result[RecipientType.public] = perms;
} else if (URIRef(receiverId) == authenticatedAgent) {
result[RecipientType.authUser] = perms;
}
}
return result;
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: solidpod
description: Support access to private data from PODs on Solid servers.
version: 1.0.16
version: 1.0.17
homepage: https://github.com/anusii/solidpod

environment:
Expand Down
Loading