solid-logic 6.0.1-3 → 6.0.1-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acl/accessControlSubjects.cjs.js +1 -1
- package/dist/acl/accessControlSubjects.cjs.js.map +1 -1
- package/dist/acl/accessControlSubjects.d.ts.map +1 -1
- package/dist/acl/accessControlSubjects.esm.js +38 -37
- package/dist/acl/accessControlSubjects.esm.js.map +1 -1
- package/dist/acl/aclLogic.cjs.js +1 -1
- package/dist/acl/aclLogic.cjs.js.map +1 -1
- package/dist/acl/aclLogic.esm.js +2 -6
- package/dist/acl/aclLogic.esm.js.map +1 -1
- package/dist/authSession/authSession.cjs.js.map +1 -1
- package/dist/authSession/authSession.esm.js.map +1 -1
- package/dist/authSession/events.cjs.js.map +1 -1
- package/dist/authSession/events.esm.js.map +1 -1
- package/dist/authSession/flagAuthorizationOnTransitions.cjs.js.map +1 -1
- package/dist/authSession/flagAuthorizationOnTransitions.esm.js.map +1 -1
- package/dist/authSession/identityState.cjs.js +1 -1
- package/dist/authSession/identityState.cjs.js.map +1 -1
- package/dist/authSession/identityState.esm.js +3 -17
- package/dist/authSession/identityState.esm.js.map +1 -1
- package/dist/authSession/issuer.cjs.js.map +1 -1
- package/dist/authSession/issuer.esm.js.map +1 -1
- package/dist/authSession/session.cjs.js.map +1 -1
- package/dist/authSession/session.esm.js.map +1 -1
- package/dist/authn/SolidAuthnLogic.cjs.js.map +1 -1
- package/dist/authn/SolidAuthnLogic.esm.js.map +1 -1
- package/dist/authn/authUtil.cjs.js.map +1 -1
- package/dist/authn/authUtil.esm.js.map +1 -1
- package/dist/authn/serverLogout.cjs.js.map +1 -1
- package/dist/authn/serverLogout.esm.js.map +1 -1
- package/dist/chat/chatLogic.cjs.js.map +1 -1
- package/dist/chat/chatLogic.esm.js.map +1 -1
- package/dist/directory/catalogDirectory.cjs.js.map +1 -1
- package/dist/directory/catalogDirectory.esm.js.map +1 -1
- package/dist/directory/contactsDirectory.cjs.js.map +1 -1
- package/dist/directory/contactsDirectory.esm.js.map +1 -1
- package/dist/directory/directoryHelpers.cjs.js.map +1 -1
- package/dist/directory/directoryHelpers.esm.js.map +1 -1
- package/dist/directory/directoryLogic.cjs.js.map +1 -1
- package/dist/directory/directoryLogic.esm.js.map +1 -1
- package/dist/directory/friendsDirectory.cjs.js.map +1 -1
- package/dist/directory/friendsDirectory.esm.js.map +1 -1
- package/dist/inbox/inboxLogic.cjs.js.map +1 -1
- package/dist/inbox/inboxLogic.esm.js.map +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/issuer/issuerLogic.cjs.js.map +1 -1
- package/dist/issuer/issuerLogic.esm.js.map +1 -1
- package/dist/logic/CustomError.cjs.js.map +1 -1
- package/dist/logic/CustomError.esm.js.map +1 -1
- package/dist/logic/solidLogic.cjs.js.map +1 -1
- package/dist/logic/solidLogic.esm.js.map +1 -1
- package/dist/logic/solidLogicSingleton.cjs.js +1 -1
- package/dist/logic/solidLogicSingleton.cjs.js.map +1 -1
- package/dist/logic/solidLogicSingleton.esm.js +1 -1
- package/dist/logic/solidLogicSingleton.esm.js.map +1 -1
- package/dist/profile/profileAclDocuments.cjs.js.map +1 -1
- package/dist/profile/profileAclDocuments.esm.js.map +1 -1
- package/dist/profile/profileDocuments.cjs.js.map +1 -1
- package/dist/profile/profileDocuments.esm.js.map +1 -1
- package/dist/profile/profileLogic.cjs.js.map +1 -1
- package/dist/profile/profileLogic.esm.js.map +1 -1
- package/dist/resource/resourceHttp.cjs.js.map +1 -1
- package/dist/resource/resourceHttp.esm.js.map +1 -1
- package/dist/resource/resourceLogic.cjs.js +1 -1
- package/dist/resource/resourceLogic.cjs.js.map +1 -1
- package/dist/resource/resourceLogic.d.ts.map +1 -1
- package/dist/resource/resourceLogic.esm.js +80 -34
- package/dist/resource/resourceLogic.esm.js.map +1 -1
- package/dist/resource/resourceMetadata.cjs.js +1 -1
- package/dist/resource/resourceMetadata.cjs.js.map +1 -1
- package/dist/resource/resourceMetadata.d.ts +2 -0
- package/dist/resource/resourceMetadata.d.ts.map +1 -1
- package/dist/resource/resourceMetadata.esm.js +6 -2
- package/dist/resource/resourceMetadata.esm.js.map +1 -1
- package/dist/typeIndex/typeIndexAclDocuments.cjs.js.map +1 -1
- package/dist/typeIndex/typeIndexAclDocuments.esm.js.map +1 -1
- package/dist/typeIndex/typeIndexDocuments.cjs.js.map +1 -1
- package/dist/typeIndex/typeIndexDocuments.esm.js.map +1 -1
- package/dist/typeIndex/typeIndexLogic.cjs.js +1 -1
- package/dist/typeIndex/typeIndexLogic.cjs.js.map +1 -1
- package/dist/typeIndex/typeIndexLogic.esm.js +5 -10
- package/dist/typeIndex/typeIndexLogic.esm.js.map +1 -1
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/containerLogic.cjs.js.map +1 -1
- package/dist/util/containerLogic.esm.js.map +1 -1
- package/dist/util/debug.cjs.js +1 -1
- package/dist/util/debug.cjs.js.map +1 -1
- package/dist/util/debug.esm.js +4 -1
- package/dist/util/debug.esm.js.map +1 -1
- package/dist/util/ns.cjs.js.map +1 -1
- package/dist/util/ns.esm.js.map +1 -1
- package/dist/util/utilityLogic.cjs.js.map +1 -1
- package/dist/util/utilityLogic.esm.js.map +1 -1
- package/dist/util/utils.cjs.js.map +1 -1
- package/dist/util/utils.esm.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aclLogic.esm.js","names":[],"sources":["../../src/acl/aclLogic.ts"],"sourcesContent":["import { ACL, namedNode, quad, applyPlan as applyAuthorizationPlan, Authenticated, findEffectiveACL, planGrant as planAuthorizationGrant, planPublicRead as planAuthorizationPublicRead, planRevoke as planAuthorizationRevoke, Public, type AccessMode, type AccessSubject, type ACLContext, type Authorization, type PatchPlan } from '@dokieli/web-access-control'\nimport { graph, NamedNode, Namespace, serialize, sym } from 'rdflib'\nimport type { AclLogic } from '../types'\nimport { ns as namespace } from '../util/ns'\nimport * as accessControlSubjects from './accessControlSubjects'\n\nexport const ACCESS_ROLES = ['Owner', 'Editor', 'No Access', 'Viewer', 'Poster', 'Submitter'] as const\nexport const PUBLIC_ACCESS_ROLES = ['No Access', 'Viewer'] as const\n/**\n * A Control grant without Write is the legacy \"write locked\" owner grant.\n *\n * It is intentionally not part of ACCESS_ROLES yet: that list is consumed by\n * the existing UI, whose selector will be updated in a later change.\n */\nexport const WRITE_LOCKED_OWNER_ROLE = 'Owner (write locked)' as const\nexport { Authenticated, Public } from '@dokieli/web-access-control'\n\nexport type AccessRole = typeof ACCESS_ROLES[number] | typeof WRITE_LOCKED_OWNER_ROLE\nexport type PublicAccessRole = typeof PUBLIC_ACCESS_ROLES[number]\nexport type ContainerAccessScope = 'folder+contents' | 'folder' | 'contents'\nexport interface ContainerAccessScopeResult {\n distinctScopes: ContainerAccessScope[]\n primaryScope: ContainerAccessScope\n mixed: boolean\n}\nexport function classifyAuthorizationScope (\n resource: string,\n authorization: Authorization,\n inherited = false\n): ContainerAccessScope | undefined {\n // Effective ACL discovery keeps inherited `acl:default` targets on the\n // parent container. They govern both this container and its contents\n // (legacy adoptACLDefault copies them as accessTo + default).\n const inheritedDefault = inherited && (authorization.default?.length ?? 0) > 0\n const hasAccessTo = inheritedDefault || (authorization.accessTo?.includes(resource) ?? false)\n const hasDefault = inheritedDefault || (authorization.default?.includes(resource) ?? false)\n if (hasAccessTo && hasDefault) return 'folder+contents'\n if (hasAccessTo) return 'folder'\n if (hasDefault) return 'contents'\n return undefined\n}\n\nexport function filterAuthorizationsByContainerScope (\n resource: string,\n authorizations: Authorization[],\n scope: ContainerAccessScope,\n inherited = false\n): Authorization[] {\n return authorizations.filter(authorization =>\n classifyAuthorizationScope(resource, authorization, inherited) === scope\n )\n}\n\n/**\n * Classify the direct ACL predicates for a container. Mixed grants are\n * conservatively treated as covering both scopes.\n */\nexport function classifyContainerAccessScope (context: ACLContext): ContainerAccessScope {\n return analyzeContainerAccessScope(context).primaryScope\n}\n\nexport function analyzeContainerAccessScope (context: ACLContext): ContainerAccessScopeResult {\n const scopes = new Set<ContainerAccessScope>()\n for (const authorization of context.authorizations ?? []) {\n const scope = classifyAuthorizationScope(context.resource, authorization, context.inherited)\n if (scope) scopes.add(scope)\n }\n // Older callers may provide only the graph. Preserve their classification\n // while keeping the authorization-entry semantics for parsed contexts.\n if (!scopes.size) {\n const entries = new Map<string, { accessTo: boolean, default: boolean }>()\n for (const statement of context.dataset ?? []) {\n if (statement.object.value !== context.resource) continue\n if (statement.predicate.value !== ACL + 'accessTo' && statement.predicate.value !== ACL + 'default') continue\n const entry = entries.get(statement.subject.value) ?? { accessTo: false, default: false }\n entry.accessTo ||= statement.predicate.value === ACL + 'accessTo'\n entry.default ||= statement.predicate.value === ACL + 'default'\n entries.set(statement.subject.value, entry)\n }\n for (const entry of entries.values()) {\n if (entry.accessTo && entry.default) scopes.add('folder+contents')\n else if (entry.accessTo) scopes.add('folder')\n else if (entry.default) scopes.add('contents')\n }\n }\n const distinctScopes = [...scopes]\n const primaryScope = distinctScopes.includes('folder+contents')\n ? 'folder+contents'\n : distinctScopes.includes('contents')\n ? 'contents'\n : distinctScopes.includes('folder')\n ? 'folder'\n : 'folder+contents'\n return { distinctScopes, primaryScope, mixed: distinctScopes.length > 1 }\n}\n\nexport const ACL_LINK = sym(\n 'http://www.iana.org/assignments/link-relations/acl'\n)\n\nexport function createAclLogic(store): AclLogic {\n\n const ns = namespace\n const ACCESS_ROLE_RULES: Array<{ label: AccessRole, modes: AccessMode[] }> = [\n { label: 'Owner', modes: ['Read', 'Write', 'Control'] },\n { label: WRITE_LOCKED_OWNER_ROLE, modes: ['Read', 'Control'] },\n { label: 'Editor', modes: ['Read', 'Write'] },\n { label: 'Poster', modes: ['Append', 'Read'] },\n { label: 'Submitter', modes: ['Append'] },\n { label: 'Viewer', modes: ['Read'] },\n { label: 'No Access', modes: [] }\n ]\n const PUBLIC_ROLE_RULES: Array<{ label: PublicAccessRole, modes: AccessMode[] }> = [\n { label: 'Viewer', modes: ['Read'] },\n { label: 'No Access', modes: [] }\n ]\n\n function roleFromModes (modes: Iterable<AccessMode>): AccessRole {\n const modeSet = new Set(modes)\n const matchingRule = ACCESS_ROLE_RULES.find(rule =>\n rule.modes.length === modeSet.size &&\n rule.modes.every(mode => modeSet.has(mode)))\n\n return matchingRule?.label ?? 'No Access'\n }\n\n function modesFromRole (role: AccessRole): AccessMode[] {\n return [...(ACCESS_ROLE_RULES.find(rule => rule.label === role)?.modes ?? [])]\n }\n\n function publicRoleFromModes (modes: Iterable<AccessMode>): PublicAccessRole {\n const modeSet = new Set(modes)\n const matchingRule = PUBLIC_ROLE_RULES.find(rule => rule.label !== 'No Access' && rule.modes.every(mode => modeSet.has(mode)))\n\n return matchingRule?.label ?? 'No Access'\n }\n\n function modesFromPublicRole (role: PublicAccessRole): AccessMode[] {\n return [...(PUBLIC_ROLE_RULES.find(rule => rule.label === role)?.modes ?? [])]\n }\n\n function getFetch() {\n const fetcher = store.fetcher as {\n _fetch?: (url: string, init?: RequestInit) => Promise<Response>\n fetch?: (url: string, init?: RequestInit) => Promise<Response>\n } | undefined\n const fetch = fetcher?._fetch ?? fetcher?.fetch\n if (!fetch) {\n throw new Error('Cannot find effective ACL, store has no fetcher')\n }\n return fetch.bind(fetcher)\n }\n \n async function findAclDocUrl(url: NamedNode) {\n await store.fetcher.load(url)\n const docNode = store.any(url, ACL_LINK)\n if (!docNode) {\n throw new Error(`No ACL link discovered for ${url}`)\n }\n return docNode.value\n }\n\n async function findEffectiveAcl(resourceURL: string | NamedNode): Promise<ACLContext> {\n const url = typeof resourceURL === 'string' ? resourceURL : resourceURL.value\n return findEffectiveACL(url, { fetch: getFetch() })\n }\n\n async function findAccessGrants(resourceURL: string | NamedNode): Promise<Authorization[]> {\n const context = await findEffectiveAcl(resourceURL)\n return context.authorizations\n }\n\n function classifyContainerScope(context: ACLContext): ContainerAccessScope {\n return classifyContainerAccessScope(context)\n }\n\n function isProtectedResource(resourceURL: string | NamedNode): boolean {\n const resource = sym(typeof resourceURL === 'string' ? resourceURL : resourceURL.value)\n return resource.value === resource.site().value ||\n (typeof store.holds === 'function' &&\n store.holds(resource, ns.rdf('type'), ns.space('Storage'), resource.doc()))\n }\n\n function subjectMatchesAuthorization(subject: AccessSubject, authorization: Authorization): boolean {\n const predicateByType = {\n agent: 'agent',\n agentGroup: 'agentGroup',\n agentClass: 'agentClass',\n origin: 'origin'\n } as const\n return authorization[predicateByType[subject.type]]?.includes(subject.iri) ?? false\n }\n\n function assertProtectedControlPreserved(context: ACLContext, plan: PatchPlan): void {\n if (!isProtectedResource(context.resource)) {\n return\n }\n\n const deletedAuthorizationIds = new Set(plan.deletes.map(statement => statement.subject.value))\n const insertedAuthorizations = new Map<string, { modes: string[], accessTo: string[] }>()\n for (const statement of plan.inserts) {\n const authorization = insertedAuthorizations.get(statement.subject.value) ?? { modes: [], accessTo: [] }\n if (statement.predicate.value === ACL + 'mode') {\n authorization.modes.push(statement.object.value)\n } else if (statement.predicate.value === ACL + 'accessTo') {\n authorization.accessTo.push(statement.object.value)\n }\n insertedAuthorizations.set(statement.subject.value, authorization)\n }\n\n const survivingControl = !context.inherited && context.authorizations.some(authorization =>\n !deletedAuthorizationIds.has(authorization.id) &&\n authorization.mode.includes('Control') &&\n authorization.accessTo.includes(context.resource)\n ) || [...insertedAuthorizations.values()].some(authorization =>\n authorization.modes.includes(ACL + 'Control') &&\n authorization.accessTo.includes(context.resource)\n )\n\n if (!survivingControl) {\n throw new Error('The protected storage resource must retain at least one Control authorization.')\n }\n }\n\n function withContainerDefaults(context: ACLContext, plan: PatchPlan): PatchPlan {\n if (!new URL(context.resource).pathname.endsWith('/')) {\n return plan\n }\n\n // Materializing an inherited container ACL must not cut off its contents.\n // The upstream resource planner emits accessTo only, even for containers.\n const defaults = plan.inserts\n .filter(statement => statement.predicate.value === ACL + 'accessTo' && statement.object.value === context.resource)\n .filter(statement => !plan.inserts.some(existing => existing.subject.equals(statement.subject) && existing.predicate.value === ACL + 'default' && existing.object.value === context.resource))\n .map(statement => quad(statement.subject, namedNode(ACL + 'default'), statement.object))\n\n return { ...plan, inserts: [...plan.inserts, ...defaults] }\n }\n\n async function planGrant(resourceURL: string | NamedNode, subject: AccessSubject, modes: AccessMode[]): Promise<PatchPlan> {\n const context = await findEffectiveAcl(resourceURL)\n if (context.inherited && new URL(context.resource).pathname.endsWith('/')) {\n const plan = materializeInheritedContainer(context, subject, modes, 'folder')\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n const plan = planAuthorizationGrant(context, subject, modes)\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n\n function assertContainer(context: ACLContext): void {\n if (!new URL(context.resource).pathname.endsWith('/')) {\n throw new Error(`Expected a container URL ending in \"/\": ${context.resource}`)\n }\n }\n\n const subjectKeys = ['agent', 'agentClass', 'agentGroup', 'origin'] as const\n const rdfType = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'\n const supportedConditionPredicates = new Set([\n rdfType,\n ACL + 'client',\n ACL + 'clientClass',\n ACL + 'clientGroup',\n ACL + 'issuer',\n ACL + 'issuerClass',\n ACL + 'issuerGroup',\n ACL + 'description',\n // Some older ACL contexts expose the condition type as a property key\n // rather than retaining it under rdf:type.\n ACL + 'ClientCondition',\n ACL + 'IssuerCondition'\n ])\n const supportedConditionTypes = new Set([\n ACL + 'ClientCondition',\n ACL + 'IssuerCondition'\n ])\n\n function createAuthorizationIdAllocator(context: ACLContext): (base: string) => string {\n const takenIds = new Set<string>([\n ...(context.dataset ?? []).map(statement => statement.subject.value),\n ...context.authorizations.flatMap(authorization => [\n authorization.id,\n ...(authorization.condition ?? []).map(condition => condition.id)\n ])\n ])\n\n return (base: string): string => {\n let id = base\n let suffix = 1\n while (takenIds.has(id)) {\n id = `${base}-${suffix++}`\n }\n takenIds.add(id)\n return id\n }\n }\n\n function assertSupportedConditions(context: ACLContext): void {\n for (const authorization of context.authorizations) {\n for (const condition of authorization.condition ?? []) {\n const conditionTypes = condition.properties[rdfType] ?? []\n if (conditionTypes.length > 1 ||\n (conditionTypes.length === 1 && !supportedConditionTypes.has(conditionTypes[0]))) {\n throw new Error(`Unsupported ACL condition type on ${condition.id}`)\n }\n for (const predicate of Object.keys(condition.properties)) {\n if (!supportedConditionPredicates.has(predicate)) {\n throw new Error(`Unsupported ACL condition property: ${predicate}`)\n }\n }\n }\n }\n }\n\n function materializeInheritedContainer (\n context: ACLContext,\n subject: AccessSubject,\n modes: AccessMode[] | undefined,\n scope: ContainerAccessScope,\n revoke = false\n ): PatchPlan {\n const inserts: PatchPlan['inserts'] = []\n const subjectKey = subject.type\n const freshId = createAuthorizationIdAllocator(context)\n assertSupportedConditions(context)\n const addRule = (\n authorization: Authorization,\n baseId: string,\n targets: { accessTo: boolean, default: boolean },\n modeOverride?: AccessMode[]\n ) => {\n const subjects = subjectKeys.flatMap(key => authorization[key].map(iri => ({ key, iri })))\n if (!subjects.length || (!targets.accessTo && !targets.default)) return\n const id = freshId(baseId)\n const rule = namedNode(id)\n inserts.push(\n quad(rule, namedNode(rdfType), namedNode(ACL + 'Authorization')),\n ...(targets.accessTo ? [quad(rule, namedNode(ACL + 'accessTo'), namedNode(context.resource))] : []),\n ...(targets.default ? [quad(rule, namedNode(ACL + 'default'), namedNode(context.resource))] : []),\n ...(modeOverride ?? authorization.mode).map(mode => quad(rule, namedNode(ACL + 'mode'), namedNode(`http://www.w3.org/ns/auth/acl#${mode}`))),\n ...subjects.map(({ key, iri }) => quad(rule, namedNode(ACL + key), namedNode(iri))),\n ...(authorization.condition ?? []).flatMap(condition => {\n const conditionId = freshId(`${id}-condition`)\n const conditionNode = namedNode(conditionId)\n const conditionStatements = context.dataset\n .filter(statement => statement.subject.value === condition.id)\n .map(statement => quad(\n conditionNode,\n statement.predicate,\n statement.object\n ))\n return [\n quad(rule, namedNode(ACL + 'condition'), conditionNode),\n ...conditionStatements\n ]\n })\n )\n }\n const withoutSubject = (authorization: Authorization): Authorization => ({\n ...authorization,\n [subjectKey]: authorization[subjectKey].filter(iri => iri !== subject.iri)\n })\n const onlySubject = (authorization: Authorization): Authorization => ({\n ...authorization,\n agent: [], agentClass: [], agentGroup: [], origin: [],\n [subjectKey]: [subject.iri]\n })\n context.authorizations.forEach((authorization, index) => {\n const baseId = `${context.defaultACLResource}#inherited-${index + 1}`\n // Legacy adoptACLDefault parity: an inherited acl:default governs\n // this container itself and its contents, so it is copied as\n // accessTo + default. Parent accessTo-only rules do not apply here.\n const inheritedDefault = (authorization.default?.length ?? 0) > 0\n const targets = {\n accessTo: inheritedDefault || authorization.accessTo.includes(context.resource),\n default: inheritedDefault\n }\n const selected = authorization[subjectKey]?.includes(subject.iri) ?? false\n if (!selected) {\n addRule(authorization, baseId, targets)\n return\n }\n addRule(withoutSubject(authorization), baseId, targets)\n // Keep the subject's inherited access for the scope not being changed.\n addRule(onlySubject(authorization), `${baseId}-retained`, {\n accessTo: targets.accessTo && scope === 'contents',\n default: targets.default && scope === 'folder'\n })\n })\n if (!revoke) {\n const selectedAuthorization = context.authorizations.find(authorization =>\n authorization[subjectKey]?.includes(subject.iri) &&\n (authorization.default?.length ?? 0) > 0)\n const newAuthorization: Authorization = {\n id: `${context.defaultACLResource}#inherited-new-source`,\n blankNode: false,\n accessTo: [], default: [], agent: [], agentClass: [], agentGroup: [], origin: [], mode: [],\n condition: selectedAuthorization?.condition ?? []\n }\n addRule(onlySubject(newAuthorization), `${context.defaultACLResource}#inherited-new`, {\n accessTo: scope !== 'contents',\n default: scope !== 'folder'\n }, modes ?? [])\n }\n return { target: context.defaultACLResource, deletes: [], inserts }\n }\n\n async function planGrantContainer(containerURL: string | NamedNode, subject: AccessSubject, modes: AccessMode[]): Promise<PatchPlan> {\n const context = await findEffectiveAcl(containerURL)\n assertContainer(context)\n const plan = context.inherited\n ? materializeInheritedContainer(context, subject, modes, 'folder')\n : planAuthorizationGrant(context, subject, modes)\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n\n async function planGrantContainerAndContents(containerURL: string | NamedNode, subject: AccessSubject, modes: AccessMode[]): Promise<PatchPlan> {\n const context = await findEffectiveAcl(containerURL)\n assertContainer(context)\n const plan = context.inherited\n ? materializeInheritedContainer(context, subject, modes, 'folder+contents')\n : withContainerDefaults(context, planAuthorizationGrant(context, subject, modes))\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n\n async function planGrantContainerContents(containerURL: string | NamedNode, subject: AccessSubject, modes: AccessMode[]): Promise<PatchPlan> {\n const context = await findEffectiveAcl(containerURL)\n assertContainer(context)\n if (isProtectedResource(context.resource)) {\n throw new Error('Applying access to the contents of a protected storage resource is not allowed.')\n }\n if (context.inherited) return materializeInheritedContainer(context, subject, modes, 'contents')\n const matchingAuthorizations = context.authorizations.filter(authorization =>\n authorization.default?.includes(context.resource) &&\n subjectMatchesAuthorization(subject, authorization))\n\n if (matchingAuthorizations.length) {\n return splitDirectContainerContentsAuthorizations(context, matchingAuthorizations, subject, modes)\n }\n\n const subjectKey = subject.type\n const id = createAuthorizationIdAllocator(context)(`${context.defaultACLResource}#contents-1`)\n const rule = namedNode(id)\n const subjectQuad = quad(rule, namedNode(ACL + subjectKey), namedNode(subject.iri))\n const fresh = [\n quad(rule, namedNode(rdfType), namedNode(ACL + 'Authorization')),\n quad(rule, namedNode(ACL + 'default'), namedNode(context.resource)),\n ...modes.map(mode => quad(rule, namedNode(ACL + 'mode'), namedNode(`http://www.w3.org/ns/auth/acl#${mode}`))),\n subjectQuad\n ]\n\n // A folder-only authorization is unrelated and must remain untouched.\n return { target: context.defaultACLResource, deletes: [], inserts: fresh }\n }\n\n async function planRevokeContainerContents(containerURL: string | NamedNode, subject: AccessSubject): Promise<PatchPlan> {\n const context = await findEffectiveAcl(containerURL)\n assertContainer(context)\n if (isProtectedResource(context.resource)) {\n throw new Error('Removing access from the contents of a protected storage resource is not allowed.')\n }\n\n const defaultPredicate = ACL + 'default'\n if (!context.inherited) {\n const matchingAuthorizations = context.authorizations.filter(authorization =>\n authorization.default?.includes(context.resource) &&\n subjectMatchesAuthorization(subject, authorization))\n\n if (matchingAuthorizations.length) {\n return splitDirectContainerContentsAuthorizations(context, matchingAuthorizations, subject)\n }\n\n const deletes = context.dataset.filter(statement =>\n statement.predicate.value === defaultPredicate &&\n statement.object.value === context.resource &&\n context.authorizations.some(authorization =>\n authorization.id === statement.subject.value &&\n authorization[subject.type].includes(subject.iri)))\n if (!deletes.length) {\n throw new Error(`No default authorization found for ${subject.iri}`)\n }\n return { target: context.defaultACLResource, deletes, inserts: [] }\n }\n\n const hasInheritedDefault = context.authorizations.some(authorization =>\n authorization.default?.includes(context.resource) &&\n subjectMatchesAuthorization(subject, authorization))\n if (!hasInheritedDefault) {\n throw new Error(`No default authorization found for ${subject.iri}`)\n }\n const plan = materializeInheritedContainer(context, subject, undefined, 'contents', true)\n return plan\n }\n\n function splitDirectContainerContentsAuthorizations (\n context: ACLContext,\n authorizations: Authorization[],\n subject: AccessSubject,\n modes?: AccessMode[]\n ): PatchPlan {\n const freshId = createAuthorizationIdAllocator(context)\n const inserts: PatchPlan['inserts'] = []\n const addRule = (\n rule: ReturnType<typeof namedNode>,\n ruleSubjects: Array<{ key: typeof subjectKeys[number], iri: string }>,\n ruleModes: AccessMode[],\n accessTo: string[],\n defaults: string[],\n conditions: string[]\n ) => {\n if (!ruleSubjects.length || (!accessTo.length && !defaults.length)) return\n inserts.push(\n quad(rule, namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), namedNode(ACL + 'Authorization')),\n ...accessTo.map(target => quad(rule, namedNode(ACL + 'accessTo'), namedNode(target))),\n ...defaults.map(target => quad(rule, namedNode(ACL + 'default'), namedNode(target))),\n ...ruleModes.map(mode => quad(rule, namedNode(ACL + 'mode'), namedNode(`http://www.w3.org/ns/auth/acl#${mode}`))),\n ...conditions.map(condition => quad(rule, namedNode(ACL + 'condition'), namedNode(condition))),\n ...ruleSubjects.map(({ key, iri }) => quad(rule, namedNode(ACL + key), namedNode(iri)))\n )\n }\n\n const deletes: PatchPlan['deletes'] = []\n authorizations.forEach((authorization, index) => {\n const subjects = subjectKeys.flatMap(key =>\n authorization[key].map(iri => ({ key, iri }))\n )\n const remainingSubjects = subjects.filter(({ key, iri }) =>\n !(key === subject.type && iri === subject.iri))\n const baseId = `${context.defaultACLResource}#split-${index + 1}`\n\n if (!modes && subjects.length === 1 && remainingSubjects.length === 0) {\n deletes.push(...context.dataset.filter(statement =>\n statement.subject.value === authorization.id &&\n statement.predicate.value === ACL + 'default' &&\n statement.object.value === context.resource))\n return\n }\n\n deletes.push(...context.dataset.filter(statement => statement.subject.value === authorization.id))\n const conditions = (authorization.condition ?? []).map(condition => condition.id)\n addRule(namedNode(freshId(`${context.defaultACLResource}#retained-${index + 1}`)), remainingSubjects, authorization.mode, authorization.accessTo, authorization.default, conditions)\n const retainedDefaults = authorization.default.filter(target => target !== context.resource)\n if (authorization.accessTo.length || retainedDefaults.length) {\n addRule(\n namedNode(freshId(`${baseId}-retained`)),\n [{ key: subject.type, iri: subject.iri }],\n authorization.mode,\n authorization.accessTo,\n retainedDefaults,\n conditions\n )\n }\n if (modes) {\n addRule(\n namedNode(freshId(`${baseId}`)),\n [{ key: subject.type, iri: subject.iri }],\n modes,\n [],\n authorization.default.filter(target => target === context.resource),\n conditions\n )\n }\n })\n\n return {\n target: context.defaultACLResource,\n deletes,\n inserts\n }\n }\n\n async function planRevoke(resourceURL: string | NamedNode, subject: AccessSubject): Promise<PatchPlan> {\n const context = await findEffectiveAcl(resourceURL)\n if (context.inherited && new URL(context.resource).pathname.endsWith('/')) {\n const plan = materializeInheritedContainer(context, subject, undefined, 'folder+contents', true)\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n const plan = withContainerDefaults(context, planAuthorizationRevoke(context, subject))\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n\n async function planPublicRead(resourceURL: string | NamedNode, enabled: boolean): Promise<PatchPlan> {\n const context = await findEffectiveAcl(resourceURL)\n return planAuthorizationPublicRead(context, enabled)\n }\n\n async function resetToDefault(resourceURL: string | NamedNode): Promise<Response> {\n const context = await findEffectiveAcl(resourceURL)\n if (isProtectedResource(context.resource)) {\n throw new Error('The protected storage resource cannot be reset to a default ACL.')\n }\n const response = await getFetch()(context.defaultACLResource, { method: 'DELETE' })\n\n if (!response.ok && response.status !== 404) {\n throw new Error(`Unable to remove ACL ${context.defaultACLResource}: HTTP ${response.status}`)\n }\n\n return response\n }\n\n async function applyPlan(plan: PatchPlan): Promise<Response> {\n return applyAuthorizationPlan(plan, { fetch: getFetch() })\n }\n\n async function classifyAccessControlSubject(principle: string) {\n return accessControlSubjects.classifyAccessControlSubject(store, principle)\n }\n /**\n * Simple Access Control\n *\n * This function sets up a simple default ACL for a resource, with\n * RWC for the owner, and a specified access (default none) for the public.\n * In all cases owner has read write control.\n * Parameter lists modes allowed to public\n *\n * @param options\n * @param options.public eg ['Read', 'Write']\n *\n * @returns Resolves with aclDoc uri on successful write\n */\n function setACLUserPublic ( \n docURI: string,\n me: NamedNode,\n options: {\n defaultForNew?: boolean,\n public?: []\n }\n ): Promise<NamedNode> {\n const aclDoc = store.any(\n store.sym(docURI),\n ACL_LINK\n )\n\n return Promise.resolve()\n .then(() => {\n if (aclDoc) {\n return aclDoc as NamedNode\n }\n\n return fetchACLRel(docURI).catch(err => {\n throw new Error(`Error fetching rel=ACL header for ${docURI}: ${err}`)\n })\n })\n .then(aclDoc => {\n const aclText = genACLText(docURI, me, aclDoc.uri, options)\n if (!store.fetcher) {\n throw new Error('Cannot PUT this, store has no fetcher')\n }\n return store.fetcher\n .webOperation('PUT', aclDoc.uri, {\n data: aclText,\n contentType: 'text/turtle'\n })\n .then(result => {\n if (!result.ok) {\n throw new Error('Error writing ACL text: ' + result.error)\n }\n\n return aclDoc\n })\n })\n }\n\n /**\n * @param docURI\n * @returns\n */\n function fetchACLRel (docURI: string): Promise<NamedNode> {\n const fetcher = store.fetcher\n if (!fetcher) {\n throw new Error('Cannot fetch ACL rel, store has no fetcher')\n }\n\n return fetcher.load(docURI).then(result => {\n if (!result.ok) {\n throw new Error('fetchACLRel: While loading:' + (result as any).error)\n }\n\n const aclDoc = store.any(\n store.sym(docURI),\n ACL_LINK\n )\n\n if (!aclDoc) {\n throw new Error('fetchACLRel: No Link rel=ACL header for ' + docURI)\n }\n\n return aclDoc as NamedNode\n })\n }\n\n /**\n * @param docURI\n * @param me\n * @param aclURI\n * @param options\n *\n * @returns Serialized ACL\n */\n function genACLText (\n docURI: string,\n me: NamedNode,\n aclURI: string,\n options: {\n defaultForNew?: boolean,\n public?: []\n } = {}\n ): string | undefined {\n const optPublic = options.public || []\n const g = graph()\n const auth = Namespace('http://www.w3.org/ns/auth/acl#')\n let a = g.sym(`${aclURI}#a1`)\n const acl = g.sym(aclURI)\n const doc = g.sym(docURI)\n g.add(a, ns.rdf('type'), auth('Authorization'), acl)\n g.add(a, auth('accessTo'), doc, acl)\n if (options.defaultForNew) {\n g.add(a, auth('default'), doc, acl)\n }\n g.add(a, auth('agent'), me, acl)\n g.add(a, auth('mode'), auth('Read'), acl)\n g.add(a, auth('mode'), auth('Write'), acl)\n g.add(a, auth('mode'), auth('Control'), acl)\n\n if (optPublic.length) {\n a = g.sym(`${aclURI}#a2`)\n g.add(a, ns.rdf('type'), auth('Authorization'), acl)\n g.add(a, auth('accessTo'), doc, acl)\n g.add(a, auth('agentClass'), ns.foaf('Agent'), acl)\n for (let p = 0; p < optPublic.length; p++) {\n g.add(a, auth('mode'), auth(optPublic[p]), acl) // Like 'Read' etc\n }\n }\n return serialize(acl, g, aclURI)\n }\n return {\n findAclDocUrl,\n findEffectiveAcl,\n findAccessGrants,\n classifyContainerScope,\n isProtectedResource,\n roleFromModes,\n modesFromRole,\n publicRoleFromModes,\n modesFromPublicRole,\n planGrant,\n planGrantContainer,\n planGrantContainerAndContents,\n planGrantContainerContents,\n planRevokeContainerContents,\n planRevoke,\n planPublicRead,\n resetToDefault,\n applyPlan,\n classifyAccessControlSubject,\n setACLUserPublic,\n genACLText,\n Authenticated,\n Public,\n PUBLIC_ACCESS_ROLES\n }\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"aclLogic.esm.js","names":["ns","namespace","namedNode","planGrant","planAuthorizationGrant","planRevoke","planAuthorizationRevoke","planPublicRead","planAuthorizationPublicRead","applyPlan","applyAuthorizationPlan","classifyAccessControlSubject","accessControlSubjects.classifyAccessControlSubject","Authenticated$1","Public$1","namedNode$1","ACCESS_ROLES","PUBLIC_ACCESS_ROLES","WRITE_LOCKED_OWNER_ROLE","classifyAuthorizationScope","inheritedDefault","inherited","authorization","hasAccessTo","resource","hasDefault","filterAuthorizationsByContainerScope","authorizations","scope","classifyContainerAccessScope","analyzeContainerAccessScope","context","scopes","entries","statement","ACL","entry","distinctScopes","ACL_LINK","sym","createAclLogic","ns$1","ns","ACCESS_ROLE_RULES","PUBLIC_ROLE_RULES","roleFromModes","modeSet","modes","rule","mode","modesFromRole","role","publicRoleFromModes","modesFromPublicRole","getFetch","fetcher","store","fetch","findAclDocUrl","url","docNode","findEffectiveAcl","resourceURL","findEffectiveACL","findAccessGrants","classifyContainerScope","isProtectedResource","subjectMatchesAuthorization","subject","assertProtectedControlPreserved","deletedAuthorizationIds","plan","insertedAuthorizations","withContainerDefaults","defaults","existing","quad","planGrant$1","materializeInheritedContainer","planGrant","assertContainer","subjectKeys","rdfType","supportedConditionPredicates","supportedConditionTypes","createAuthorizationIdAllocator","takenIds","condition","id","base","suffix","assertSupportedConditions","conditionTypes","predicate","inserts","subjectKey","freshId","addRule","subjects","key","targets","baseId","modeOverride","iri","conditionId","conditionNode","conditionStatements","withoutSubject","onlySubject","index","revoke","selectedAuthorization","planGrantContainer","containerURL","planGrantContainerAndContents","planGrantContainerContents","matchingAuthorizations","splitDirectContainerContentsAuthorizations","subjectQuad","fresh","planRevokeContainerContents","defaultPredicate","deletes","ruleSubjects","accessTo","target","ruleModes","conditions","remainingSubjects","retainedDefaults","planRevoke$1","planRevoke","planPublicRead$1","planPublicRead","enabled","resetToDefault","response","applyPlan$1","applyPlan","classifyAccessControlSubject$1","classifyAccessControlSubject","principle","setACLUserPublic","aclDoc","docURI","fetchACLRel","err","aclText","genACLText","me","options","result","optPublic","g","graph","auth","Namespace","a","aclURI","acl","doc","p","serialize"],"sources":["../../src/acl/aclLogic.ts"],"sourcesContent":["import { ACL, namedNode, quad, applyPlan as applyAuthorizationPlan, Authenticated, findEffectiveACL, planGrant as planAuthorizationGrant, planPublicRead as planAuthorizationPublicRead, planRevoke as planAuthorizationRevoke, Public, type AccessMode, type AccessSubject, type ACLContext, type Authorization, type PatchPlan } from '@dokieli/web-access-control'\nimport { graph, NamedNode, Namespace, serialize, sym } from 'rdflib'\nimport type { AclLogic } from '../types'\nimport { ns as namespace } from '../util/ns'\nimport * as accessControlSubjects from './accessControlSubjects'\n\nexport const ACCESS_ROLES = ['Owner', 'Editor', 'No Access', 'Viewer', 'Poster', 'Submitter'] as const\nexport const PUBLIC_ACCESS_ROLES = ['No Access', 'Viewer'] as const\n/**\n * A Control grant without Write is the legacy \"write locked\" owner grant.\n *\n * It is intentionally not part of ACCESS_ROLES yet: that list is consumed by\n * the existing UI, whose selector will be updated in a later change.\n */\nexport const WRITE_LOCKED_OWNER_ROLE = 'Owner (write locked)' as const\nexport { Authenticated, Public } from '@dokieli/web-access-control'\n\nexport type AccessRole = typeof ACCESS_ROLES[number] | typeof WRITE_LOCKED_OWNER_ROLE\nexport type PublicAccessRole = typeof PUBLIC_ACCESS_ROLES[number]\nexport type ContainerAccessScope = 'folder+contents' | 'folder' | 'contents'\nexport interface ContainerAccessScopeResult {\n distinctScopes: ContainerAccessScope[]\n primaryScope: ContainerAccessScope\n mixed: boolean\n}\nexport function classifyAuthorizationScope (\n resource: string,\n authorization: Authorization,\n inherited = false\n): ContainerAccessScope | undefined {\n // Effective ACL discovery keeps inherited `acl:default` targets on the\n // parent container. They govern both this container and its contents\n // (legacy adoptACLDefault copies them as accessTo + default).\n const inheritedDefault = inherited && (authorization.default?.length ?? 0) > 0\n const hasAccessTo = inheritedDefault || (authorization.accessTo?.includes(resource) ?? false)\n const hasDefault = inheritedDefault || (authorization.default?.includes(resource) ?? false)\n if (hasAccessTo && hasDefault) return 'folder+contents'\n if (hasAccessTo) return 'folder'\n if (hasDefault) return 'contents'\n return undefined\n}\n\nexport function filterAuthorizationsByContainerScope (\n resource: string,\n authorizations: Authorization[],\n scope: ContainerAccessScope,\n inherited = false\n): Authorization[] {\n return authorizations.filter(authorization =>\n classifyAuthorizationScope(resource, authorization, inherited) === scope\n )\n}\n\n/**\n * Classify the direct ACL predicates for a container. Mixed grants are\n * conservatively treated as covering both scopes.\n */\nexport function classifyContainerAccessScope (context: ACLContext): ContainerAccessScope {\n return analyzeContainerAccessScope(context).primaryScope\n}\n\nexport function analyzeContainerAccessScope (context: ACLContext): ContainerAccessScopeResult {\n const scopes = new Set<ContainerAccessScope>()\n for (const authorization of context.authorizations ?? []) {\n const scope = classifyAuthorizationScope(context.resource, authorization, context.inherited)\n if (scope) scopes.add(scope)\n }\n // Older callers may provide only the graph. Preserve their classification\n // while keeping the authorization-entry semantics for parsed contexts.\n if (!scopes.size) {\n const entries = new Map<string, { accessTo: boolean, default: boolean }>()\n for (const statement of context.dataset ?? []) {\n if (statement.object.value !== context.resource) continue\n if (statement.predicate.value !== ACL + 'accessTo' && statement.predicate.value !== ACL + 'default') continue\n const entry = entries.get(statement.subject.value) ?? { accessTo: false, default: false }\n entry.accessTo ||= statement.predicate.value === ACL + 'accessTo'\n entry.default ||= statement.predicate.value === ACL + 'default'\n entries.set(statement.subject.value, entry)\n }\n for (const entry of entries.values()) {\n if (entry.accessTo && entry.default) scopes.add('folder+contents')\n else if (entry.accessTo) scopes.add('folder')\n else if (entry.default) scopes.add('contents')\n }\n }\n const distinctScopes = [...scopes]\n const primaryScope = distinctScopes.includes('folder+contents')\n ? 'folder+contents'\n : distinctScopes.includes('contents')\n ? 'contents'\n : distinctScopes.includes('folder')\n ? 'folder'\n : 'folder+contents'\n return { distinctScopes, primaryScope, mixed: distinctScopes.length > 1 }\n}\n\nexport const ACL_LINK = sym(\n 'http://www.iana.org/assignments/link-relations/acl'\n)\n\nexport function createAclLogic(store): AclLogic {\n\n const ns = namespace\n const ACCESS_ROLE_RULES: Array<{ label: AccessRole, modes: AccessMode[] }> = [\n { label: 'Owner', modes: ['Read', 'Write', 'Control'] },\n { label: WRITE_LOCKED_OWNER_ROLE, modes: ['Read', 'Control'] },\n { label: 'Editor', modes: ['Read', 'Write'] },\n { label: 'Poster', modes: ['Append', 'Read'] },\n { label: 'Submitter', modes: ['Append'] },\n { label: 'Viewer', modes: ['Read'] },\n { label: 'No Access', modes: [] }\n ]\n const PUBLIC_ROLE_RULES: Array<{ label: PublicAccessRole, modes: AccessMode[] }> = [\n { label: 'Viewer', modes: ['Read'] },\n { label: 'No Access', modes: [] }\n ]\n\n function roleFromModes (modes: Iterable<AccessMode>): AccessRole {\n const modeSet = new Set(modes)\n const matchingRule = ACCESS_ROLE_RULES.find(rule =>\n rule.modes.length === modeSet.size &&\n rule.modes.every(mode => modeSet.has(mode)))\n\n return matchingRule?.label ?? 'No Access'\n }\n\n function modesFromRole (role: AccessRole): AccessMode[] {\n return [...(ACCESS_ROLE_RULES.find(rule => rule.label === role)?.modes ?? [])]\n }\n\n function publicRoleFromModes (modes: Iterable<AccessMode>): PublicAccessRole {\n const modeSet = new Set(modes)\n const matchingRule = PUBLIC_ROLE_RULES.find(rule => rule.label !== 'No Access' && rule.modes.every(mode => modeSet.has(mode)))\n\n return matchingRule?.label ?? 'No Access'\n }\n\n function modesFromPublicRole (role: PublicAccessRole): AccessMode[] {\n return [...(PUBLIC_ROLE_RULES.find(rule => rule.label === role)?.modes ?? [])]\n }\n\n function getFetch() {\n const fetcher = store.fetcher as {\n _fetch?: (url: string, init?: RequestInit) => Promise<Response>\n fetch?: (url: string, init?: RequestInit) => Promise<Response>\n } | undefined\n const fetch = fetcher?._fetch ?? fetcher?.fetch\n if (!fetch) {\n throw new Error('Cannot find effective ACL, store has no fetcher')\n }\n return fetch.bind(fetcher)\n }\n \n async function findAclDocUrl(url: NamedNode) {\n await store.fetcher.load(url)\n const docNode = store.any(url, ACL_LINK)\n if (!docNode) {\n throw new Error(`No ACL link discovered for ${url}`)\n }\n return docNode.value\n }\n\n async function findEffectiveAcl(resourceURL: string | NamedNode): Promise<ACLContext> {\n const url = typeof resourceURL === 'string' ? resourceURL : resourceURL.value\n return findEffectiveACL(url, { fetch: getFetch() })\n }\n\n async function findAccessGrants(resourceURL: string | NamedNode): Promise<Authorization[]> {\n const context = await findEffectiveAcl(resourceURL)\n return context.authorizations\n }\n\n function classifyContainerScope(context: ACLContext): ContainerAccessScope {\n return classifyContainerAccessScope(context)\n }\n\n function isProtectedResource(resourceURL: string | NamedNode): boolean {\n const resource = sym(typeof resourceURL === 'string' ? resourceURL : resourceURL.value)\n return resource.value === resource.site().value ||\n (typeof store.holds === 'function' &&\n store.holds(resource, ns.rdf('type'), ns.space('Storage'), resource.doc()))\n }\n\n function subjectMatchesAuthorization(subject: AccessSubject, authorization: Authorization): boolean {\n const predicateByType = {\n agent: 'agent',\n agentGroup: 'agentGroup',\n agentClass: 'agentClass',\n origin: 'origin'\n } as const\n return authorization[predicateByType[subject.type]]?.includes(subject.iri) ?? false\n }\n\n function assertProtectedControlPreserved(context: ACLContext, plan: PatchPlan): void {\n if (!isProtectedResource(context.resource)) {\n return\n }\n\n const deletedAuthorizationIds = new Set(plan.deletes.map(statement => statement.subject.value))\n const insertedAuthorizations = new Map<string, { modes: string[], accessTo: string[] }>()\n for (const statement of plan.inserts) {\n const authorization = insertedAuthorizations.get(statement.subject.value) ?? { modes: [], accessTo: [] }\n if (statement.predicate.value === ACL + 'mode') {\n authorization.modes.push(statement.object.value)\n } else if (statement.predicate.value === ACL + 'accessTo') {\n authorization.accessTo.push(statement.object.value)\n }\n insertedAuthorizations.set(statement.subject.value, authorization)\n }\n\n const survivingControl = !context.inherited && context.authorizations.some(authorization =>\n !deletedAuthorizationIds.has(authorization.id) &&\n authorization.mode.includes('Control') &&\n authorization.accessTo.includes(context.resource)\n ) || [...insertedAuthorizations.values()].some(authorization =>\n authorization.modes.includes(ACL + 'Control') &&\n authorization.accessTo.includes(context.resource)\n )\n\n if (!survivingControl) {\n throw new Error('The protected storage resource must retain at least one Control authorization.')\n }\n }\n\n function withContainerDefaults(context: ACLContext, plan: PatchPlan): PatchPlan {\n if (!new URL(context.resource).pathname.endsWith('/')) {\n return plan\n }\n\n // Materializing an inherited container ACL must not cut off its contents.\n // The upstream resource planner emits accessTo only, even for containers.\n const defaults = plan.inserts\n .filter(statement => statement.predicate.value === ACL + 'accessTo' && statement.object.value === context.resource)\n .filter(statement => !plan.inserts.some(existing => existing.subject.equals(statement.subject) && existing.predicate.value === ACL + 'default' && existing.object.value === context.resource))\n .map(statement => quad(statement.subject, namedNode(ACL + 'default'), statement.object))\n\n return { ...plan, inserts: [...plan.inserts, ...defaults] }\n }\n\n async function planGrant(resourceURL: string | NamedNode, subject: AccessSubject, modes: AccessMode[]): Promise<PatchPlan> {\n const context = await findEffectiveAcl(resourceURL)\n if (context.inherited && new URL(context.resource).pathname.endsWith('/')) {\n const plan = materializeInheritedContainer(context, subject, modes, 'folder')\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n const plan = planAuthorizationGrant(context, subject, modes)\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n\n function assertContainer(context: ACLContext): void {\n if (!new URL(context.resource).pathname.endsWith('/')) {\n throw new Error(`Expected a container URL ending in \"/\": ${context.resource}`)\n }\n }\n\n const subjectKeys = ['agent', 'agentClass', 'agentGroup', 'origin'] as const\n const rdfType = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'\n const supportedConditionPredicates = new Set([\n rdfType,\n ACL + 'client',\n ACL + 'clientClass',\n ACL + 'clientGroup',\n ACL + 'issuer',\n ACL + 'issuerClass',\n ACL + 'issuerGroup',\n ACL + 'description',\n // Some older ACL contexts expose the condition type as a property key\n // rather than retaining it under rdf:type.\n ACL + 'ClientCondition',\n ACL + 'IssuerCondition'\n ])\n const supportedConditionTypes = new Set([\n ACL + 'ClientCondition',\n ACL + 'IssuerCondition'\n ])\n\n function createAuthorizationIdAllocator(context: ACLContext): (base: string) => string {\n const takenIds = new Set<string>([\n ...(context.dataset ?? []).map(statement => statement.subject.value),\n ...context.authorizations.flatMap(authorization => [\n authorization.id,\n ...(authorization.condition ?? []).map(condition => condition.id)\n ])\n ])\n\n return (base: string): string => {\n let id = base\n let suffix = 1\n while (takenIds.has(id)) {\n id = `${base}-${suffix++}`\n }\n takenIds.add(id)\n return id\n }\n }\n\n function assertSupportedConditions(context: ACLContext): void {\n for (const authorization of context.authorizations) {\n for (const condition of authorization.condition ?? []) {\n const conditionTypes = condition.properties[rdfType] ?? []\n if (conditionTypes.length > 1 ||\n (conditionTypes.length === 1 && !supportedConditionTypes.has(conditionTypes[0]))) {\n throw new Error(`Unsupported ACL condition type on ${condition.id}`)\n }\n for (const predicate of Object.keys(condition.properties)) {\n if (!supportedConditionPredicates.has(predicate)) {\n throw new Error(`Unsupported ACL condition property: ${predicate}`)\n }\n }\n }\n }\n }\n\n function materializeInheritedContainer (\n context: ACLContext,\n subject: AccessSubject,\n modes: AccessMode[] | undefined,\n scope: ContainerAccessScope,\n revoke = false\n ): PatchPlan {\n const inserts: PatchPlan['inserts'] = []\n const subjectKey = subject.type\n const freshId = createAuthorizationIdAllocator(context)\n assertSupportedConditions(context)\n const addRule = (\n authorization: Authorization,\n baseId: string,\n targets: { accessTo: boolean, default: boolean },\n modeOverride?: AccessMode[]\n ) => {\n const subjects = subjectKeys.flatMap(key => authorization[key].map(iri => ({ key, iri })))\n if (!subjects.length || (!targets.accessTo && !targets.default)) return\n const id = freshId(baseId)\n const rule = namedNode(id)\n inserts.push(\n quad(rule, namedNode(rdfType), namedNode(ACL + 'Authorization')),\n ...(targets.accessTo ? [quad(rule, namedNode(ACL + 'accessTo'), namedNode(context.resource))] : []),\n ...(targets.default ? [quad(rule, namedNode(ACL + 'default'), namedNode(context.resource))] : []),\n ...(modeOverride ?? authorization.mode).map(mode => quad(rule, namedNode(ACL + 'mode'), namedNode(`http://www.w3.org/ns/auth/acl#${mode}`))),\n ...subjects.map(({ key, iri }) => quad(rule, namedNode(ACL + key), namedNode(iri))),\n ...(authorization.condition ?? []).flatMap(condition => {\n const conditionId = freshId(`${id}-condition`)\n const conditionNode = namedNode(conditionId)\n const conditionStatements = context.dataset\n .filter(statement => statement.subject.value === condition.id)\n .map(statement => quad(\n conditionNode,\n statement.predicate,\n statement.object\n ))\n return [\n quad(rule, namedNode(ACL + 'condition'), conditionNode),\n ...conditionStatements\n ]\n })\n )\n }\n const withoutSubject = (authorization: Authorization): Authorization => ({\n ...authorization,\n [subjectKey]: authorization[subjectKey].filter(iri => iri !== subject.iri)\n })\n const onlySubject = (authorization: Authorization): Authorization => ({\n ...authorization,\n agent: [], agentClass: [], agentGroup: [], origin: [],\n [subjectKey]: [subject.iri]\n })\n context.authorizations.forEach((authorization, index) => {\n const baseId = `${context.defaultACLResource}#inherited-${index + 1}`\n // Legacy adoptACLDefault parity: an inherited acl:default governs\n // this container itself and its contents, so it is copied as\n // accessTo + default. Parent accessTo-only rules do not apply here.\n const inheritedDefault = (authorization.default?.length ?? 0) > 0\n const targets = {\n accessTo: inheritedDefault || authorization.accessTo.includes(context.resource),\n default: inheritedDefault\n }\n const selected = authorization[subjectKey]?.includes(subject.iri) ?? false\n if (!selected) {\n addRule(authorization, baseId, targets)\n return\n }\n addRule(withoutSubject(authorization), baseId, targets)\n // Keep the subject's inherited access for the scope not being changed.\n addRule(onlySubject(authorization), `${baseId}-retained`, {\n accessTo: targets.accessTo && scope === 'contents',\n default: targets.default && scope === 'folder'\n })\n })\n if (!revoke) {\n const selectedAuthorization = context.authorizations.find(authorization =>\n authorization[subjectKey]?.includes(subject.iri) &&\n (authorization.default?.length ?? 0) > 0)\n const newAuthorization: Authorization = {\n id: `${context.defaultACLResource}#inherited-new-source`,\n blankNode: false,\n accessTo: [], default: [], agent: [], agentClass: [], agentGroup: [], origin: [], mode: [],\n condition: selectedAuthorization?.condition ?? []\n }\n addRule(onlySubject(newAuthorization), `${context.defaultACLResource}#inherited-new`, {\n accessTo: scope !== 'contents',\n default: scope !== 'folder'\n }, modes ?? [])\n }\n return { target: context.defaultACLResource, deletes: [], inserts }\n }\n\n async function planGrantContainer(containerURL: string | NamedNode, subject: AccessSubject, modes: AccessMode[]): Promise<PatchPlan> {\n const context = await findEffectiveAcl(containerURL)\n assertContainer(context)\n const plan = context.inherited\n ? materializeInheritedContainer(context, subject, modes, 'folder')\n : planAuthorizationGrant(context, subject, modes)\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n\n async function planGrantContainerAndContents(containerURL: string | NamedNode, subject: AccessSubject, modes: AccessMode[]): Promise<PatchPlan> {\n const context = await findEffectiveAcl(containerURL)\n assertContainer(context)\n const plan = context.inherited\n ? materializeInheritedContainer(context, subject, modes, 'folder+contents')\n : withContainerDefaults(context, planAuthorizationGrant(context, subject, modes))\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n\n async function planGrantContainerContents(containerURL: string | NamedNode, subject: AccessSubject, modes: AccessMode[]): Promise<PatchPlan> {\n const context = await findEffectiveAcl(containerURL)\n assertContainer(context)\n if (isProtectedResource(context.resource)) {\n throw new Error('Applying access to the contents of a protected storage resource is not allowed.')\n }\n if (context.inherited) return materializeInheritedContainer(context, subject, modes, 'contents')\n const matchingAuthorizations = context.authorizations.filter(authorization =>\n authorization.default?.includes(context.resource) &&\n subjectMatchesAuthorization(subject, authorization))\n\n if (matchingAuthorizations.length) {\n return splitDirectContainerContentsAuthorizations(context, matchingAuthorizations, subject, modes)\n }\n\n const subjectKey = subject.type\n const id = createAuthorizationIdAllocator(context)(`${context.defaultACLResource}#contents-1`)\n const rule = namedNode(id)\n const subjectQuad = quad(rule, namedNode(ACL + subjectKey), namedNode(subject.iri))\n const fresh = [\n quad(rule, namedNode(rdfType), namedNode(ACL + 'Authorization')),\n quad(rule, namedNode(ACL + 'default'), namedNode(context.resource)),\n ...modes.map(mode => quad(rule, namedNode(ACL + 'mode'), namedNode(`http://www.w3.org/ns/auth/acl#${mode}`))),\n subjectQuad\n ]\n\n // A folder-only authorization is unrelated and must remain untouched.\n return { target: context.defaultACLResource, deletes: [], inserts: fresh }\n }\n\n async function planRevokeContainerContents(containerURL: string | NamedNode, subject: AccessSubject): Promise<PatchPlan> {\n const context = await findEffectiveAcl(containerURL)\n assertContainer(context)\n if (isProtectedResource(context.resource)) {\n throw new Error('Removing access from the contents of a protected storage resource is not allowed.')\n }\n\n const defaultPredicate = ACL + 'default'\n if (!context.inherited) {\n const matchingAuthorizations = context.authorizations.filter(authorization =>\n authorization.default?.includes(context.resource) &&\n subjectMatchesAuthorization(subject, authorization))\n\n if (matchingAuthorizations.length) {\n return splitDirectContainerContentsAuthorizations(context, matchingAuthorizations, subject)\n }\n\n const deletes = context.dataset.filter(statement =>\n statement.predicate.value === defaultPredicate &&\n statement.object.value === context.resource &&\n context.authorizations.some(authorization =>\n authorization.id === statement.subject.value &&\n authorization[subject.type].includes(subject.iri)))\n if (!deletes.length) {\n throw new Error(`No default authorization found for ${subject.iri}`)\n }\n return { target: context.defaultACLResource, deletes, inserts: [] }\n }\n\n const hasInheritedDefault = context.authorizations.some(authorization =>\n authorization.default?.includes(context.resource) &&\n subjectMatchesAuthorization(subject, authorization))\n if (!hasInheritedDefault) {\n throw new Error(`No default authorization found for ${subject.iri}`)\n }\n const plan = materializeInheritedContainer(context, subject, undefined, 'contents', true)\n return plan\n }\n\n function splitDirectContainerContentsAuthorizations (\n context: ACLContext,\n authorizations: Authorization[],\n subject: AccessSubject,\n modes?: AccessMode[]\n ): PatchPlan {\n const freshId = createAuthorizationIdAllocator(context)\n const inserts: PatchPlan['inserts'] = []\n const addRule = (\n rule: ReturnType<typeof namedNode>,\n ruleSubjects: Array<{ key: typeof subjectKeys[number], iri: string }>,\n ruleModes: AccessMode[],\n accessTo: string[],\n defaults: string[],\n conditions: string[]\n ) => {\n if (!ruleSubjects.length || (!accessTo.length && !defaults.length)) return\n inserts.push(\n quad(rule, namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), namedNode(ACL + 'Authorization')),\n ...accessTo.map(target => quad(rule, namedNode(ACL + 'accessTo'), namedNode(target))),\n ...defaults.map(target => quad(rule, namedNode(ACL + 'default'), namedNode(target))),\n ...ruleModes.map(mode => quad(rule, namedNode(ACL + 'mode'), namedNode(`http://www.w3.org/ns/auth/acl#${mode}`))),\n ...conditions.map(condition => quad(rule, namedNode(ACL + 'condition'), namedNode(condition))),\n ...ruleSubjects.map(({ key, iri }) => quad(rule, namedNode(ACL + key), namedNode(iri)))\n )\n }\n\n const deletes: PatchPlan['deletes'] = []\n authorizations.forEach((authorization, index) => {\n const subjects = subjectKeys.flatMap(key =>\n authorization[key].map(iri => ({ key, iri }))\n )\n const remainingSubjects = subjects.filter(({ key, iri }) =>\n !(key === subject.type && iri === subject.iri))\n const baseId = `${context.defaultACLResource}#split-${index + 1}`\n\n if (!modes && subjects.length === 1 && remainingSubjects.length === 0) {\n deletes.push(...context.dataset.filter(statement =>\n statement.subject.value === authorization.id &&\n statement.predicate.value === ACL + 'default' &&\n statement.object.value === context.resource))\n return\n }\n\n deletes.push(...context.dataset.filter(statement => statement.subject.value === authorization.id))\n const conditions = (authorization.condition ?? []).map(condition => condition.id)\n addRule(namedNode(freshId(`${context.defaultACLResource}#retained-${index + 1}`)), remainingSubjects, authorization.mode, authorization.accessTo, authorization.default, conditions)\n const retainedDefaults = authorization.default.filter(target => target !== context.resource)\n if (authorization.accessTo.length || retainedDefaults.length) {\n addRule(\n namedNode(freshId(`${baseId}-retained`)),\n [{ key: subject.type, iri: subject.iri }],\n authorization.mode,\n authorization.accessTo,\n retainedDefaults,\n conditions\n )\n }\n if (modes) {\n addRule(\n namedNode(freshId(`${baseId}`)),\n [{ key: subject.type, iri: subject.iri }],\n modes,\n [],\n authorization.default.filter(target => target === context.resource),\n conditions\n )\n }\n })\n\n return {\n target: context.defaultACLResource,\n deletes,\n inserts\n }\n }\n\n async function planRevoke(resourceURL: string | NamedNode, subject: AccessSubject): Promise<PatchPlan> {\n const context = await findEffectiveAcl(resourceURL)\n if (context.inherited && new URL(context.resource).pathname.endsWith('/')) {\n const plan = materializeInheritedContainer(context, subject, undefined, 'folder+contents', true)\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n const plan = withContainerDefaults(context, planAuthorizationRevoke(context, subject))\n assertProtectedControlPreserved(context, plan)\n return plan\n }\n\n async function planPublicRead(resourceURL: string | NamedNode, enabled: boolean): Promise<PatchPlan> {\n const context = await findEffectiveAcl(resourceURL)\n return planAuthorizationPublicRead(context, enabled)\n }\n\n async function resetToDefault(resourceURL: string | NamedNode): Promise<Response> {\n const context = await findEffectiveAcl(resourceURL)\n if (isProtectedResource(context.resource)) {\n throw new Error('The protected storage resource cannot be reset to a default ACL.')\n }\n const response = await getFetch()(context.defaultACLResource, { method: 'DELETE' })\n\n if (!response.ok && response.status !== 404) {\n throw new Error(`Unable to remove ACL ${context.defaultACLResource}: HTTP ${response.status}`)\n }\n\n return response\n }\n\n async function applyPlan(plan: PatchPlan): Promise<Response> {\n return applyAuthorizationPlan(plan, { fetch: getFetch() })\n }\n\n async function classifyAccessControlSubject(principle: string) {\n return accessControlSubjects.classifyAccessControlSubject(store, principle)\n }\n /**\n * Simple Access Control\n *\n * This function sets up a simple default ACL for a resource, with\n * RWC for the owner, and a specified access (default none) for the public.\n * In all cases owner has read write control.\n * Parameter lists modes allowed to public\n *\n * @param options\n * @param options.public eg ['Read', 'Write']\n *\n * @returns Resolves with aclDoc uri on successful write\n */\n function setACLUserPublic ( \n docURI: string,\n me: NamedNode,\n options: {\n defaultForNew?: boolean,\n public?: []\n }\n ): Promise<NamedNode> {\n const aclDoc = store.any(\n store.sym(docURI),\n ACL_LINK\n )\n\n return Promise.resolve()\n .then(() => {\n if (aclDoc) {\n return aclDoc as NamedNode\n }\n\n return fetchACLRel(docURI).catch(err => {\n throw new Error(`Error fetching rel=ACL header for ${docURI}: ${err}`)\n })\n })\n .then(aclDoc => {\n const aclText = genACLText(docURI, me, aclDoc.uri, options)\n if (!store.fetcher) {\n throw new Error('Cannot PUT this, store has no fetcher')\n }\n return store.fetcher\n .webOperation('PUT', aclDoc.uri, {\n data: aclText,\n contentType: 'text/turtle'\n })\n .then(result => {\n if (!result.ok) {\n throw new Error('Error writing ACL text: ' + result.error)\n }\n\n return aclDoc\n })\n })\n }\n\n /**\n * @param docURI\n * @returns\n */\n function fetchACLRel (docURI: string): Promise<NamedNode> {\n const fetcher = store.fetcher\n if (!fetcher) {\n throw new Error('Cannot fetch ACL rel, store has no fetcher')\n }\n\n return fetcher.load(docURI).then(result => {\n if (!result.ok) {\n throw new Error('fetchACLRel: While loading:' + (result as any).error)\n }\n\n const aclDoc = store.any(\n store.sym(docURI),\n ACL_LINK\n )\n\n if (!aclDoc) {\n throw new Error('fetchACLRel: No Link rel=ACL header for ' + docURI)\n }\n\n return aclDoc as NamedNode\n })\n }\n\n /**\n * @param docURI\n * @param me\n * @param aclURI\n * @param options\n *\n * @returns Serialized ACL\n */\n function genACLText (\n docURI: string,\n me: NamedNode,\n aclURI: string,\n options: {\n defaultForNew?: boolean,\n public?: []\n } = {}\n ): string | undefined {\n const optPublic = options.public || []\n const g = graph()\n const auth = Namespace('http://www.w3.org/ns/auth/acl#')\n let a = g.sym(`${aclURI}#a1`)\n const acl = g.sym(aclURI)\n const doc = g.sym(docURI)\n g.add(a, ns.rdf('type'), auth('Authorization'), acl)\n g.add(a, auth('accessTo'), doc, acl)\n if (options.defaultForNew) {\n g.add(a, auth('default'), doc, acl)\n }\n g.add(a, auth('agent'), me, acl)\n g.add(a, auth('mode'), auth('Read'), acl)\n g.add(a, auth('mode'), auth('Write'), acl)\n g.add(a, auth('mode'), auth('Control'), acl)\n\n if (optPublic.length) {\n a = g.sym(`${aclURI}#a2`)\n g.add(a, ns.rdf('type'), auth('Authorization'), acl)\n g.add(a, auth('accessTo'), doc, acl)\n g.add(a, auth('agentClass'), ns.foaf('Agent'), acl)\n for (let p = 0; p < optPublic.length; p++) {\n g.add(a, auth('mode'), auth(optPublic[p]), acl) // Like 'Read' etc\n }\n }\n return serialize(acl, g, aclURI)\n }\n return {\n findAclDocUrl,\n findEffectiveAcl,\n findAccessGrants,\n classifyContainerScope,\n isProtectedResource,\n roleFromModes,\n modesFromRole,\n publicRoleFromModes,\n modesFromPublicRole,\n planGrant,\n planGrantContainer,\n planGrantContainerAndContents,\n planGrantContainerContents,\n planRevokeContainerContents,\n planRevoke,\n planPublicRead,\n resetToDefault,\n applyPlan,\n classifyAccessControlSubject,\n setACLUserPublic,\n genACLText,\n Authenticated,\n Public,\n PUBLIC_ACCESS_ROLES\n }\n}"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authSession.cjs.js","names":[],"sources":["../../src/authSession/authSession.ts"],"sourcesContent":["/**\n * Auth session wiring.\n *\n * Takes the raw OIDC session from session.ts and layers on:\n * - Login compatibility shim (normalises legacy call-site signatures\n * and resolves the canonical issuer)\n * - SessionEvents shim (legacy EventEmitter-style API)\n * - Logout listener (emits 'logout' on session deactivation)\n *\n * Exports the fully assembled authSession.\n */\n\nimport type { Session as OidcSession } from '@uvdsl/solid-oidc-client-browser/core'\nimport { _session } from './session'\nimport { resolveIssuerForLogin } from './issuer'\nimport { SessionEvents } from './events'\nimport { legacySessionInfo, restoreSession, subscribeIdentity, type SessionLike } from './identityState'\n\ntype SessionCompatibilityShape = {\n webId?: string\n isActive?: boolean\n info?: {\n webId?: string\n isLoggedIn?: boolean\n }\n fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n authFetch?: (input: string | URL | Request, init?: RequestInit, dpopPayload?: any) => Promise<Response>\n}\n\ntype LoginOptionsLegacy = {\n oidcIssuer?: string\n idp?: string\n issuer?: string\n redirectUrl?: string\n redirect_uri?: string\n redirectUri?: string\n}\n\ntype LoginCompat = {\n (issuer: string, redirectUrl: string): Promise<unknown>\n (options: LoginOptionsLegacy): Promise<unknown>\n}\n\nexport type SessionWithLegacyEvents = Omit<OidcSession, 'login'> & SessionCompatibilityShape & {\n login: LoginCompat\n events: SessionEvents\n}\n\n// ---------------------------------------------------------------------------\n// Login compatibility shim\n// ---------------------------------------------------------------------------\n// Wraps _session.login() so that call sites with different calling\n// conventions all work. The underlying session expects:\n// login(issuer: string, redirectUrl: string)\n//\n// idpOrOptions can be:\n// - a string (issuer URL) — passed through with less resolution\n// - an options object with any of these field-name variants:\n// issuer: oidcIssuer | idp | issuer\n// redirect: redirectUrl | redirect_uri | redirectUri\n// (all redirect field names map to the same value: the URL the IdP\n// should send the browser back to after authentication)\n// - anything else — passed through to the underlying session as-is\n//\n// In all cases the issuer is resolved through\n// /.well-known/openid-configuration before redirect so the canonical\n// issuer host is used.\n\nconst sessionAny = _session as any\nconst originalLogin = typeof sessionAny.login === 'function'\n ? sessionAny.login.bind(_session)\n : undefined\n\nif (originalLogin) {\n const compatLogin = async (idpOrOptions: any, redirectUri?: string): Promise<unknown> => {\n if (idpOrOptions && typeof idpOrOptions === 'object' && !Array.isArray(idpOrOptions)) {\n const oidcIssuer = idpOrOptions.oidcIssuer ?? idpOrOptions.idp ?? idpOrOptions.issuer\n const redirectUrl = idpOrOptions.redirectUrl ?? idpOrOptions.redirect_uri ?? idpOrOptions.redirectUri\n if (typeof oidcIssuer === 'string' && typeof redirectUrl === 'string') {\n return originalLogin(await resolveIssuerForLogin(oidcIssuer), redirectUrl)\n }\n }\n if (typeof idpOrOptions === 'string') {\n return originalLogin(await resolveIssuerForLogin(idpOrOptions), redirectUri)\n }\n return originalLogin(idpOrOptions, redirectUri)\n }\n sessionAny.login = compatLogin as LoginCompat\n}\n\n// ---------------------------------------------------------------------------\n// Legacy event layer\n// ---------------------------------------------------------------------------\n\nconst events = new SessionEvents()\n\n// 'login' and 'sessionRestore' are emitted in SolidAuthnLogic.checkUser()\n// because only that call site knows which path activated the session. Every\n// other identity transition is reported by the identity state, which watches\n// the session itself: 'logout' when the session goes inactive, 'sessionChange'\n// when the identity changes some other way — including a login/logout made in\n// another tab, which uvdsl does not broadcast as a state change and which is\n// noticed when this tab is refocused — and 'identityReplaced' when an\n// established identity is gone.\n//\n// The resync maps a backing store that no longer holds a session (a cross-tab\n// logout) to 'cleared'; the state bounds the wait and drops an answer that\n// belongs to an identity the session has left.\nconst resyncSession = (): unknown => {\n const restoring = restoreSession(_session as unknown as SessionLike)\n if (!restoring) return undefined\n return restoring.then(() => 'changed', (error: unknown) => {\n // A transient refresh/network failure is compared as it stands; a store\n // that has no session to restore means this tab's identity is gone.\n const message = error instanceof Error ? error.message : String(error)\n return /no session to restore/i.test(message) ? 'cleared' : 'changed'\n })\n}\nsubscribeIdentity(_session as unknown as SessionLike, {\n onEvent: (event) => events.emit(event),\n resync: resyncSession\n})\n\nexport const authSession: SessionWithLegacyEvents = Object.assign(\n _session as Omit<OidcSession, 'login'> & { login: LoginCompat },\n { events }\n)\n\n// Legacy `info` compatibility shape.\n// The uvdsl session stores state on `webId_`/`isActive_` and exposes them via\n// `webId`/`isActive` getters, but legacy consumers (e.g. solid-ui's\n// `loginStatusBox` widget, `SolidAuthnLogic.currentUser()`'s fallback path)\n// read `authSession.info.webId` / `authSession.info.isLoggedIn`. Expose those\n// as a derived value — and keep it derived: consumers snapshot and restore\n// `info`, and a retained value would report the previous identity after a\n// login/logout. Assignment is accepted and ignored so ordinary property\n// writes cannot throw; a test that needs to fake `info` redefines it.\nObject.defineProperty(authSession, 'info', {\n enumerable: true,\n configurable: true,\n get (): { webId?: string, isLoggedIn?: boolean } {\n return legacySessionInfo(_session as unknown as SessionLike)\n },\n set (_value: { webId?: string, isLoggedIn?: boolean } | undefined): void {\n // Accepted for legacy code that assigns snapshots; reads stay derived.\n }\n})\n "],"mappings":"kIAoEA,
|
|
1
|
+
{"version":3,"file":"authSession.cjs.js","names":["_session","resolveIssuerForLogin","SessionEvents","restoreSession","subscribeIdentity","legacySessionInfo","require_session","require_issuer","require_events","require_identityState","sessionAny","originalLogin","idpOrOptions","oidcIssuer","redirectUrl","redirectUri","events","event","restoring","message","error","authSession"],"sources":["../../src/authSession/authSession.ts"],"sourcesContent":["/**\n * Auth session wiring.\n *\n * Takes the raw OIDC session from session.ts and layers on:\n * - Login compatibility shim (normalises legacy call-site signatures\n * and resolves the canonical issuer)\n * - SessionEvents shim (legacy EventEmitter-style API)\n * - Logout listener (emits 'logout' on session deactivation)\n *\n * Exports the fully assembled authSession.\n */\n\nimport type { Session as OidcSession } from '@uvdsl/solid-oidc-client-browser/core'\nimport { _session } from './session'\nimport { resolveIssuerForLogin } from './issuer'\nimport { SessionEvents } from './events'\nimport { legacySessionInfo, restoreSession, subscribeIdentity, type SessionLike } from './identityState'\n\ntype SessionCompatibilityShape = {\n webId?: string\n isActive?: boolean\n info?: {\n webId?: string\n isLoggedIn?: boolean\n }\n fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n authFetch?: (input: string | URL | Request, init?: RequestInit, dpopPayload?: any) => Promise<Response>\n}\n\ntype LoginOptionsLegacy = {\n oidcIssuer?: string\n idp?: string\n issuer?: string\n redirectUrl?: string\n redirect_uri?: string\n redirectUri?: string\n}\n\ntype LoginCompat = {\n (issuer: string, redirectUrl: string): Promise<unknown>\n (options: LoginOptionsLegacy): Promise<unknown>\n}\n\nexport type SessionWithLegacyEvents = Omit<OidcSession, 'login'> & SessionCompatibilityShape & {\n login: LoginCompat\n events: SessionEvents\n}\n\n// ---------------------------------------------------------------------------\n// Login compatibility shim\n// ---------------------------------------------------------------------------\n// Wraps _session.login() so that call sites with different calling\n// conventions all work. The underlying session expects:\n// login(issuer: string, redirectUrl: string)\n//\n// idpOrOptions can be:\n// - a string (issuer URL) — passed through with less resolution\n// - an options object with any of these field-name variants:\n// issuer: oidcIssuer | idp | issuer\n// redirect: redirectUrl | redirect_uri | redirectUri\n// (all redirect field names map to the same value: the URL the IdP\n// should send the browser back to after authentication)\n// - anything else — passed through to the underlying session as-is\n//\n// In all cases the issuer is resolved through\n// /.well-known/openid-configuration before redirect so the canonical\n// issuer host is used.\n\nconst sessionAny = _session as any\nconst originalLogin = typeof sessionAny.login === 'function'\n ? sessionAny.login.bind(_session)\n : undefined\n\nif (originalLogin) {\n const compatLogin = async (idpOrOptions: any, redirectUri?: string): Promise<unknown> => {\n if (idpOrOptions && typeof idpOrOptions === 'object' && !Array.isArray(idpOrOptions)) {\n const oidcIssuer = idpOrOptions.oidcIssuer ?? idpOrOptions.idp ?? idpOrOptions.issuer\n const redirectUrl = idpOrOptions.redirectUrl ?? idpOrOptions.redirect_uri ?? idpOrOptions.redirectUri\n if (typeof oidcIssuer === 'string' && typeof redirectUrl === 'string') {\n return originalLogin(await resolveIssuerForLogin(oidcIssuer), redirectUrl)\n }\n }\n if (typeof idpOrOptions === 'string') {\n return originalLogin(await resolveIssuerForLogin(idpOrOptions), redirectUri)\n }\n return originalLogin(idpOrOptions, redirectUri)\n }\n sessionAny.login = compatLogin as LoginCompat\n}\n\n// ---------------------------------------------------------------------------\n// Legacy event layer\n// ---------------------------------------------------------------------------\n\nconst events = new SessionEvents()\n\n// 'login' and 'sessionRestore' are emitted in SolidAuthnLogic.checkUser()\n// because only that call site knows which path activated the session. Every\n// other identity transition is reported by the identity state, which watches\n// the session itself: 'logout' when the session goes inactive, 'sessionChange'\n// when the identity changes some other way — including a login/logout made in\n// another tab, which uvdsl does not broadcast as a state change and which is\n// noticed when this tab is refocused — and 'identityReplaced' when an\n// established identity is gone.\n//\n// The resync maps a backing store that no longer holds a session (a cross-tab\n// logout) to 'cleared'; the state bounds the wait and drops an answer that\n// belongs to an identity the session has left.\nconst resyncSession = (): unknown => {\n const restoring = restoreSession(_session as unknown as SessionLike)\n if (!restoring) return undefined\n return restoring.then(() => 'changed', (error: unknown) => {\n // A transient refresh/network failure is compared as it stands; a store\n // that has no session to restore means this tab's identity is gone.\n const message = error instanceof Error ? error.message : String(error)\n return /no session to restore/i.test(message) ? 'cleared' : 'changed'\n })\n}\nsubscribeIdentity(_session as unknown as SessionLike, {\n onEvent: (event) => events.emit(event),\n resync: resyncSession\n})\n\nexport const authSession: SessionWithLegacyEvents = Object.assign(\n _session as Omit<OidcSession, 'login'> & { login: LoginCompat },\n { events }\n)\n\n// Legacy `info` compatibility shape.\n// The uvdsl session stores state on `webId_`/`isActive_` and exposes them via\n// `webId`/`isActive` getters, but legacy consumers (e.g. solid-ui's\n// `loginStatusBox` widget, `SolidAuthnLogic.currentUser()`'s fallback path)\n// read `authSession.info.webId` / `authSession.info.isLoggedIn`. Expose those\n// as a derived value — and keep it derived: consumers snapshot and restore\n// `info`, and a retained value would report the previous identity after a\n// login/logout. Assignment is accepted and ignored so ordinary property\n// writes cannot throw; a test that needs to fake `info` redefines it.\nObject.defineProperty(authSession, 'info', {\n enumerable: true,\n configurable: true,\n get (): { webId?: string, isLoggedIn?: boolean } {\n return legacySessionInfo(_session as unknown as SessionLike)\n },\n set (_value: { webId?: string, isLoggedIn?: boolean } | undefined): void {\n // Accepted for legacy code that assigns snapshots; reads stay derived.\n }\n})\n "],"mappings":"kIAoEA,IAAMU,EAAaV,EAAAA,SACbW,EAAgB,OAAOD,EAAW,OAAU,WAC9CA,EAAW,MAAM,KAAKV,EAAAA,QAAQ,EAC9B,IAAA,GAEAW,IAcF,EAAW,MAAQ,MAbQ,EAAmB,IAA2C,CACvF,GAAIC,GAAgB,OAAOA,GAAiB,UAAY,CAAC,MAAM,QAAQA,CAAY,EAAG,CACpF,IAAMC,EAAaD,EAAa,YAAcA,EAAa,KAAOA,EAAa,OACzEE,EAAcF,EAAa,aAAeA,EAAa,cAAgBA,EAAa,YAC1F,GAAI,OAAOC,GAAe,UAAY,OAAOC,GAAgB,SAC3D,OAAOH,EAAc,MAAMV,EAAAA,sBAAsBY,CAAU,EAAGC,CAAW,CAE7E,CAIA,OAFSH,EADL,OAAOC,GAAiB,SACL,MAAMX,EAAAA,sBAAsBW,CAAY,EAE1CA,EAF6CG,CAAW,CAG/E,GAQF,IAAMC,EAAS,IAAId,EAAAA,cAwBnBE,EAAAA,kBAAkBJ,EAAAA,SAAoC,CACpD,QAAU,GAAUgB,EAAO,KAAKC,CAAK,EACrC,WAZmC,CACnC,IAAMC,EAAYf,EAAAA,eAAeH,EAAAA,QAAkC,EACnE,GAAKkB,EACL,OAAOA,EAAU,SAAW,UAAY,GAAmB,CAGzD,IAAMC,EAAUC,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACrE,MAAO,yBAAyB,KAAKD,CAAO,EAAI,UAAY,SAC9D,CAAC,CACH,CAIA,CAAC,EAED,IAAaE,EAAuC,OAAO,OACzDrB,EAAAA,SACA,CAAE,QAAO,CACX,EAWA,OAAO,eAAeqB,EAAa,OAAQ,CACzC,WAAY,GACZ,aAAc,GACd,KAAiD,CAC/C,OAAOhB,EAAAA,kBAAkBL,EAAAA,QAAkC,CAC7D,EACA,IAAK,EAAoE,CAEzE,CACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authSession.esm.js","names":[],"sources":["../../src/authSession/authSession.ts"],"sourcesContent":["/**\n * Auth session wiring.\n *\n * Takes the raw OIDC session from session.ts and layers on:\n * - Login compatibility shim (normalises legacy call-site signatures\n * and resolves the canonical issuer)\n * - SessionEvents shim (legacy EventEmitter-style API)\n * - Logout listener (emits 'logout' on session deactivation)\n *\n * Exports the fully assembled authSession.\n */\n\nimport type { Session as OidcSession } from '@uvdsl/solid-oidc-client-browser/core'\nimport { _session } from './session'\nimport { resolveIssuerForLogin } from './issuer'\nimport { SessionEvents } from './events'\nimport { legacySessionInfo, restoreSession, subscribeIdentity, type SessionLike } from './identityState'\n\ntype SessionCompatibilityShape = {\n webId?: string\n isActive?: boolean\n info?: {\n webId?: string\n isLoggedIn?: boolean\n }\n fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n authFetch?: (input: string | URL | Request, init?: RequestInit, dpopPayload?: any) => Promise<Response>\n}\n\ntype LoginOptionsLegacy = {\n oidcIssuer?: string\n idp?: string\n issuer?: string\n redirectUrl?: string\n redirect_uri?: string\n redirectUri?: string\n}\n\ntype LoginCompat = {\n (issuer: string, redirectUrl: string): Promise<unknown>\n (options: LoginOptionsLegacy): Promise<unknown>\n}\n\nexport type SessionWithLegacyEvents = Omit<OidcSession, 'login'> & SessionCompatibilityShape & {\n login: LoginCompat\n events: SessionEvents\n}\n\n// ---------------------------------------------------------------------------\n// Login compatibility shim\n// ---------------------------------------------------------------------------\n// Wraps _session.login() so that call sites with different calling\n// conventions all work. The underlying session expects:\n// login(issuer: string, redirectUrl: string)\n//\n// idpOrOptions can be:\n// - a string (issuer URL) — passed through with less resolution\n// - an options object with any of these field-name variants:\n// issuer: oidcIssuer | idp | issuer\n// redirect: redirectUrl | redirect_uri | redirectUri\n// (all redirect field names map to the same value: the URL the IdP\n// should send the browser back to after authentication)\n// - anything else — passed through to the underlying session as-is\n//\n// In all cases the issuer is resolved through\n// /.well-known/openid-configuration before redirect so the canonical\n// issuer host is used.\n\nconst sessionAny = _session as any\nconst originalLogin = typeof sessionAny.login === 'function'\n ? sessionAny.login.bind(_session)\n : undefined\n\nif (originalLogin) {\n const compatLogin = async (idpOrOptions: any, redirectUri?: string): Promise<unknown> => {\n if (idpOrOptions && typeof idpOrOptions === 'object' && !Array.isArray(idpOrOptions)) {\n const oidcIssuer = idpOrOptions.oidcIssuer ?? idpOrOptions.idp ?? idpOrOptions.issuer\n const redirectUrl = idpOrOptions.redirectUrl ?? idpOrOptions.redirect_uri ?? idpOrOptions.redirectUri\n if (typeof oidcIssuer === 'string' && typeof redirectUrl === 'string') {\n return originalLogin(await resolveIssuerForLogin(oidcIssuer), redirectUrl)\n }\n }\n if (typeof idpOrOptions === 'string') {\n return originalLogin(await resolveIssuerForLogin(idpOrOptions), redirectUri)\n }\n return originalLogin(idpOrOptions, redirectUri)\n }\n sessionAny.login = compatLogin as LoginCompat\n}\n\n// ---------------------------------------------------------------------------\n// Legacy event layer\n// ---------------------------------------------------------------------------\n\nconst events = new SessionEvents()\n\n// 'login' and 'sessionRestore' are emitted in SolidAuthnLogic.checkUser()\n// because only that call site knows which path activated the session. Every\n// other identity transition is reported by the identity state, which watches\n// the session itself: 'logout' when the session goes inactive, 'sessionChange'\n// when the identity changes some other way — including a login/logout made in\n// another tab, which uvdsl does not broadcast as a state change and which is\n// noticed when this tab is refocused — and 'identityReplaced' when an\n// established identity is gone.\n//\n// The resync maps a backing store that no longer holds a session (a cross-tab\n// logout) to 'cleared'; the state bounds the wait and drops an answer that\n// belongs to an identity the session has left.\nconst resyncSession = (): unknown => {\n const restoring = restoreSession(_session as unknown as SessionLike)\n if (!restoring) return undefined\n return restoring.then(() => 'changed', (error: unknown) => {\n // A transient refresh/network failure is compared as it stands; a store\n // that has no session to restore means this tab's identity is gone.\n const message = error instanceof Error ? error.message : String(error)\n return /no session to restore/i.test(message) ? 'cleared' : 'changed'\n })\n}\nsubscribeIdentity(_session as unknown as SessionLike, {\n onEvent: (event) => events.emit(event),\n resync: resyncSession\n})\n\nexport const authSession: SessionWithLegacyEvents = Object.assign(\n _session as Omit<OidcSession, 'login'> & { login: LoginCompat },\n { events }\n)\n\n// Legacy `info` compatibility shape.\n// The uvdsl session stores state on `webId_`/`isActive_` and exposes them via\n// `webId`/`isActive` getters, but legacy consumers (e.g. solid-ui's\n// `loginStatusBox` widget, `SolidAuthnLogic.currentUser()`'s fallback path)\n// read `authSession.info.webId` / `authSession.info.isLoggedIn`. Expose those\n// as a derived value — and keep it derived: consumers snapshot and restore\n// `info`, and a retained value would report the previous identity after a\n// login/logout. Assignment is accepted and ignored so ordinary property\n// writes cannot throw; a test that needs to fake `info` redefines it.\nObject.defineProperty(authSession, 'info', {\n enumerable: true,\n configurable: true,\n get (): { webId?: string, isLoggedIn?: boolean } {\n return legacySessionInfo(_session as unknown as SessionLike)\n },\n set (_value: { webId?: string, isLoggedIn?: boolean } | undefined): void {\n // Accepted for legacy code that assigns snapshots; reads stay derived.\n }\n})\n "],"mappings":";;;;;AAoEA,
|
|
1
|
+
{"version":3,"file":"authSession.esm.js","names":["sessionAny","_session","originalLogin","idpOrOptions","oidcIssuer","redirectUrl","resolveIssuerForLogin","redirectUri","events","SessionEvents","subscribeIdentity","event","restoring","restoreSession","message","error","authSession","legacySessionInfo"],"sources":["../../src/authSession/authSession.ts"],"sourcesContent":["/**\n * Auth session wiring.\n *\n * Takes the raw OIDC session from session.ts and layers on:\n * - Login compatibility shim (normalises legacy call-site signatures\n * and resolves the canonical issuer)\n * - SessionEvents shim (legacy EventEmitter-style API)\n * - Logout listener (emits 'logout' on session deactivation)\n *\n * Exports the fully assembled authSession.\n */\n\nimport type { Session as OidcSession } from '@uvdsl/solid-oidc-client-browser/core'\nimport { _session } from './session'\nimport { resolveIssuerForLogin } from './issuer'\nimport { SessionEvents } from './events'\nimport { legacySessionInfo, restoreSession, subscribeIdentity, type SessionLike } from './identityState'\n\ntype SessionCompatibilityShape = {\n webId?: string\n isActive?: boolean\n info?: {\n webId?: string\n isLoggedIn?: boolean\n }\n fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>\n authFetch?: (input: string | URL | Request, init?: RequestInit, dpopPayload?: any) => Promise<Response>\n}\n\ntype LoginOptionsLegacy = {\n oidcIssuer?: string\n idp?: string\n issuer?: string\n redirectUrl?: string\n redirect_uri?: string\n redirectUri?: string\n}\n\ntype LoginCompat = {\n (issuer: string, redirectUrl: string): Promise<unknown>\n (options: LoginOptionsLegacy): Promise<unknown>\n}\n\nexport type SessionWithLegacyEvents = Omit<OidcSession, 'login'> & SessionCompatibilityShape & {\n login: LoginCompat\n events: SessionEvents\n}\n\n// ---------------------------------------------------------------------------\n// Login compatibility shim\n// ---------------------------------------------------------------------------\n// Wraps _session.login() so that call sites with different calling\n// conventions all work. The underlying session expects:\n// login(issuer: string, redirectUrl: string)\n//\n// idpOrOptions can be:\n// - a string (issuer URL) — passed through with less resolution\n// - an options object with any of these field-name variants:\n// issuer: oidcIssuer | idp | issuer\n// redirect: redirectUrl | redirect_uri | redirectUri\n// (all redirect field names map to the same value: the URL the IdP\n// should send the browser back to after authentication)\n// - anything else — passed through to the underlying session as-is\n//\n// In all cases the issuer is resolved through\n// /.well-known/openid-configuration before redirect so the canonical\n// issuer host is used.\n\nconst sessionAny = _session as any\nconst originalLogin = typeof sessionAny.login === 'function'\n ? sessionAny.login.bind(_session)\n : undefined\n\nif (originalLogin) {\n const compatLogin = async (idpOrOptions: any, redirectUri?: string): Promise<unknown> => {\n if (idpOrOptions && typeof idpOrOptions === 'object' && !Array.isArray(idpOrOptions)) {\n const oidcIssuer = idpOrOptions.oidcIssuer ?? idpOrOptions.idp ?? idpOrOptions.issuer\n const redirectUrl = idpOrOptions.redirectUrl ?? idpOrOptions.redirect_uri ?? idpOrOptions.redirectUri\n if (typeof oidcIssuer === 'string' && typeof redirectUrl === 'string') {\n return originalLogin(await resolveIssuerForLogin(oidcIssuer), redirectUrl)\n }\n }\n if (typeof idpOrOptions === 'string') {\n return originalLogin(await resolveIssuerForLogin(idpOrOptions), redirectUri)\n }\n return originalLogin(idpOrOptions, redirectUri)\n }\n sessionAny.login = compatLogin as LoginCompat\n}\n\n// ---------------------------------------------------------------------------\n// Legacy event layer\n// ---------------------------------------------------------------------------\n\nconst events = new SessionEvents()\n\n// 'login' and 'sessionRestore' are emitted in SolidAuthnLogic.checkUser()\n// because only that call site knows which path activated the session. Every\n// other identity transition is reported by the identity state, which watches\n// the session itself: 'logout' when the session goes inactive, 'sessionChange'\n// when the identity changes some other way — including a login/logout made in\n// another tab, which uvdsl does not broadcast as a state change and which is\n// noticed when this tab is refocused — and 'identityReplaced' when an\n// established identity is gone.\n//\n// The resync maps a backing store that no longer holds a session (a cross-tab\n// logout) to 'cleared'; the state bounds the wait and drops an answer that\n// belongs to an identity the session has left.\nconst resyncSession = (): unknown => {\n const restoring = restoreSession(_session as unknown as SessionLike)\n if (!restoring) return undefined\n return restoring.then(() => 'changed', (error: unknown) => {\n // A transient refresh/network failure is compared as it stands; a store\n // that has no session to restore means this tab's identity is gone.\n const message = error instanceof Error ? error.message : String(error)\n return /no session to restore/i.test(message) ? 'cleared' : 'changed'\n })\n}\nsubscribeIdentity(_session as unknown as SessionLike, {\n onEvent: (event) => events.emit(event),\n resync: resyncSession\n})\n\nexport const authSession: SessionWithLegacyEvents = Object.assign(\n _session as Omit<OidcSession, 'login'> & { login: LoginCompat },\n { events }\n)\n\n// Legacy `info` compatibility shape.\n// The uvdsl session stores state on `webId_`/`isActive_` and exposes them via\n// `webId`/`isActive` getters, but legacy consumers (e.g. solid-ui's\n// `loginStatusBox` widget, `SolidAuthnLogic.currentUser()`'s fallback path)\n// read `authSession.info.webId` / `authSession.info.isLoggedIn`. Expose those\n// as a derived value — and keep it derived: consumers snapshot and restore\n// `info`, and a retained value would report the previous identity after a\n// login/logout. Assignment is accepted and ignored so ordinary property\n// writes cannot throw; a test that needs to fake `info` redefines it.\nObject.defineProperty(authSession, 'info', {\n enumerable: true,\n configurable: true,\n get (): { webId?: string, isLoggedIn?: boolean } {\n return legacySessionInfo(_session as unknown as SessionLike)\n },\n set (_value: { webId?: string, isLoggedIn?: boolean } | undefined): void {\n // Accepted for legacy code that assigns snapshots; reads stay derived.\n }\n})\n "],"mappings":";;;;;AAoEA,IAAMA,IAAaC,GACbC,IAAgB,OAAOF,EAAW,SAAU,aAC9CA,EAAW,MAAM,KAAKC,CAAQ,IAC9B,KAAA;AAEAC,MAcF,EAAW,QAAQ,OAbQ,GAAmB,MAA2C;CACvF,IAAIC,KAAgB,OAAOA,KAAiB,YAAY,CAAC,MAAM,QAAQA,CAAY,GAAG;EACpF,IAAMC,IAAaD,EAAa,cAAcA,EAAa,OAAOA,EAAa,QACzEE,IAAcF,EAAa,eAAeA,EAAa,gBAAgBA,EAAa;EAC1F,IAAI,OAAOC,KAAe,YAAY,OAAOC,KAAgB,UAC3D,OAAOH,EAAc,MAAMI,EAAsBF,CAAU,GAAGC,CAAW;CAE7E;CAIA,OAFSH,EADL,OAAOC,KAAiB,WACL,MAAMG,EAAsBH,CAAY,IAE1CA,GAF6CI,CAAW;AAG/E;AAQF,IAAMC,IAAS,IAAIC,EAAc;AAwBjCC,EAAkBT,GAAoC;CACpD,UAAU,MAAUO,EAAO,KAAKG,CAAK;CACrC,cAZmC;EACnC,IAAMC,IAAYC,EAAeZ,CAAkC;EACnE,IAAKW,GACL,OAAOA,EAAU,WAAW,YAAY,MAAmB;GAGzD,IAAME,IAAUC,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;GACrE,OAAO,yBAAyB,KAAKD,CAAO,IAAI,YAAY;EAC9D,CAAC;CACH;AAIA,CAAC;AAED,IAAaE,IAAuC,OAAO,OACzDf,GACA,EAAE,UAAO,CACX;AAWA,OAAO,eAAee,GAAa,QAAQ;CACzC,YAAY;CACZ,cAAc;CACd,MAAiD;EAC/C,OAAOC,EAAkBhB,CAAkC;CAC7D;CACA,IAAK,GAAoE,CAEzE;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.cjs.js","names":[],"sources":["../../src/authSession/events.ts"],"sourcesContent":["/**\n * Legacy event compatibility layer.\n *\n * Pure EventEmitter-style shim — no side effects, no uvdsl dependencies.\n * Wired into the auth session by authSession.ts.\n */\n\nexport type LegacyEventName = 'identityReplaced' | 'login' | 'logout' | 'sessionChange' | 'sessionRestore'\ntype LegacyEventHandler = (...args: unknown[]) => void\n\n/**\n * Minimal EventEmitter-style shim so that existing consumers using\n * `authSession.events.on('login' | 'logout' | 'sessionRestore', handler)`\n * continue working without modification.\n *\n * Events are emitted by SolidAuthnLogic.checkUser() (login/sessionRestore)\n * and by the identity state in identityState.ts ('logout', 'sessionChange',\n * 'identityReplaced' — the event the reload helper subscribes to).\n */\nexport class SessionEvents {\n private readonly listeners: Map<string, Set<LegacyEventHandler>> = new Map()\n\n on (event: LegacyEventName, handler: LegacyEventHandler): void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set())\n this.listeners.get(event)!.add(handler)\n }\n\n off (event: LegacyEventName, handler: LegacyEventHandler): void {\n this.listeners.get(event)?.delete(handler)\n }\n\n emit (event: LegacyEventName, ...args: unknown[]): void {\n this.listeners.get(event)?.forEach(h => h(...args))\n }\n}\n"],"mappings":"AAmBA,
|
|
1
|
+
{"version":3,"file":"events.cjs.js","names":["SessionEvents","event","handler","args","h"],"sources":["../../src/authSession/events.ts"],"sourcesContent":["/**\n * Legacy event compatibility layer.\n *\n * Pure EventEmitter-style shim — no side effects, no uvdsl dependencies.\n * Wired into the auth session by authSession.ts.\n */\n\nexport type LegacyEventName = 'identityReplaced' | 'login' | 'logout' | 'sessionChange' | 'sessionRestore'\ntype LegacyEventHandler = (...args: unknown[]) => void\n\n/**\n * Minimal EventEmitter-style shim so that existing consumers using\n * `authSession.events.on('login' | 'logout' | 'sessionRestore', handler)`\n * continue working without modification.\n *\n * Events are emitted by SolidAuthnLogic.checkUser() (login/sessionRestore)\n * and by the identity state in identityState.ts ('logout', 'sessionChange',\n * 'identityReplaced' — the event the reload helper subscribes to).\n */\nexport class SessionEvents {\n private readonly listeners: Map<string, Set<LegacyEventHandler>> = new Map()\n\n on (event: LegacyEventName, handler: LegacyEventHandler): void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set())\n this.listeners.get(event)!.add(handler)\n }\n\n off (event: LegacyEventName, handler: LegacyEventHandler): void {\n this.listeners.get(event)?.delete(handler)\n }\n\n emit (event: LegacyEventName, ...args: unknown[]): void {\n this.listeners.get(event)?.forEach(h => h(...args))\n }\n}\n"],"mappings":"AAmBA,IAAaA,EAAb,KAA2B,eAC0C,KAAA,UAAA,IAAI,IAEvE,GAAI,EAAwB,EAAmC,CACxD,KAAK,UAAU,IAAIC,CAAK,GAAG,KAAK,UAAU,IAAIA,EAAO,IAAI,GAAK,EACnE,KAAK,UAAU,IAAIA,CAAK,CAAC,CAAE,IAAIC,CAAO,CACxC,CAEA,IAAK,EAAwB,EAAmC,CAC9D,KAAK,UAAU,IAAID,CAAK,CAAC,EAAE,OAAOC,CAAO,CAC3C,CAEA,KAAM,EAAwB,GAAGC,EAAuB,CACtD,KAAK,UAAU,IAAIF,CAAK,CAAC,EAAE,QAAQ,GAAKG,EAAE,GAAGD,CAAI,CAAC,CACpD,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.esm.js","names":[],"sources":["../../src/authSession/events.ts"],"sourcesContent":["/**\n * Legacy event compatibility layer.\n *\n * Pure EventEmitter-style shim — no side effects, no uvdsl dependencies.\n * Wired into the auth session by authSession.ts.\n */\n\nexport type LegacyEventName = 'identityReplaced' | 'login' | 'logout' | 'sessionChange' | 'sessionRestore'\ntype LegacyEventHandler = (...args: unknown[]) => void\n\n/**\n * Minimal EventEmitter-style shim so that existing consumers using\n * `authSession.events.on('login' | 'logout' | 'sessionRestore', handler)`\n * continue working without modification.\n *\n * Events are emitted by SolidAuthnLogic.checkUser() (login/sessionRestore)\n * and by the identity state in identityState.ts ('logout', 'sessionChange',\n * 'identityReplaced' — the event the reload helper subscribes to).\n */\nexport class SessionEvents {\n private readonly listeners: Map<string, Set<LegacyEventHandler>> = new Map()\n\n on (event: LegacyEventName, handler: LegacyEventHandler): void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set())\n this.listeners.get(event)!.add(handler)\n }\n\n off (event: LegacyEventName, handler: LegacyEventHandler): void {\n this.listeners.get(event)?.delete(handler)\n }\n\n emit (event: LegacyEventName, ...args: unknown[]): void {\n this.listeners.get(event)?.forEach(h => h(...args))\n }\n}\n"],"mappings":";AAmBA,
|
|
1
|
+
{"version":3,"file":"events.esm.js","names":["SessionEvents","event","handler","args","h"],"sources":["../../src/authSession/events.ts"],"sourcesContent":["/**\n * Legacy event compatibility layer.\n *\n * Pure EventEmitter-style shim — no side effects, no uvdsl dependencies.\n * Wired into the auth session by authSession.ts.\n */\n\nexport type LegacyEventName = 'identityReplaced' | 'login' | 'logout' | 'sessionChange' | 'sessionRestore'\ntype LegacyEventHandler = (...args: unknown[]) => void\n\n/**\n * Minimal EventEmitter-style shim so that existing consumers using\n * `authSession.events.on('login' | 'logout' | 'sessionRestore', handler)`\n * continue working without modification.\n *\n * Events are emitted by SolidAuthnLogic.checkUser() (login/sessionRestore)\n * and by the identity state in identityState.ts ('logout', 'sessionChange',\n * 'identityReplaced' — the event the reload helper subscribes to).\n */\nexport class SessionEvents {\n private readonly listeners: Map<string, Set<LegacyEventHandler>> = new Map()\n\n on (event: LegacyEventName, handler: LegacyEventHandler): void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set())\n this.listeners.get(event)!.add(handler)\n }\n\n off (event: LegacyEventName, handler: LegacyEventHandler): void {\n this.listeners.get(event)?.delete(handler)\n }\n\n emit (event: LegacyEventName, ...args: unknown[]): void {\n this.listeners.get(event)?.forEach(h => h(...args))\n }\n}\n"],"mappings":";AAmBA,IAAaA,IAAb,MAA2B;;EAC0C,KAAA,4BAAA,IAAI,IAAI;;CAE3E,GAAI,GAAwB,GAAmC;EAE7D,AADK,KAAK,UAAU,IAAIC,CAAK,KAAG,KAAK,UAAU,IAAIA,mBAAO,IAAI,IAAI,CAAC,GACnE,KAAK,UAAU,IAAIA,CAAK,CAAC,CAAE,IAAIC,CAAO;CACxC;CAEA,IAAK,GAAwB,GAAmC;EAC9D,KAAK,UAAU,IAAID,CAAK,CAAC,EAAE,OAAOC,CAAO;CAC3C;CAEA,KAAM,GAAwB,GAAGC,GAAuB;EACtD,KAAK,UAAU,IAAIF,CAAK,CAAC,EAAE,SAAQ,MAAKG,EAAE,GAAGD,CAAI,CAAC;CACpD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flagAuthorizationOnTransitions.cjs.js","names":[],"sources":["../../src/authSession/flagAuthorizationOnTransitions.ts"],"sourcesContent":["/**\n * Session transitions invalidate the store's cached HTTP authorization\n * metadata.\n *\n * `UpdateManager.editable()` is a synchronous read of the responses recorded\n * under `fetcher.appNode`. Those responses are not keyed by identity, so a\n * document fetched anonymously (before a restore completed) or under a\n * previous WebID keeps answering for the old identity: a writable document can\n * look read-only after login, and a read-only one can look writable after\n * logout.\n *\n * `UpdateManager.flagAuthorizationMetadata()` marks every recorded response\n * out-of-date, so `editable()` answers \"unknown\" instead of the previous\n * identity's access. It is wired HERE, once, subscribed to the session's\n * identity state — so it covers every transition whichever path noticed it (a\n * session event, a token update, a refocus resync, a cookie probe), rather\n * than whichever events a call site remembered to listen for.\n *\n * The repair is then a plain `load()`: rdflib refetches a document whose\n * recorded answers are ALL flagged (linkeddata/rdflib.js#871, in 2.4.1), which\n * is exactly the state a transition leaves behind — and `checkEditable()`\n * heals the same way. Nothing forces a fetch by hand any more.\n *\n * REQUIRES rdflib >= 2.4.1: on 2.4.0 `load()` answered such a document from\n * the cache, so the decision points below could not re-establish an answer.\n */\n\nimport * as debug from '../util/debug'\nimport { subscribeIdentity, type SessionLike } from './identityState'\n\nexport type TransitionStore = {\n updater?: { flagAuthorizationMetadata?: () => void }\n}\n\nexport type AuthorizationStore = {\n fetcher?: { load?: (doc: unknown) => unknown }\n updater?: {\n editable?: (uri: unknown) => string | boolean | undefined\n flagAuthorizationMetadata?: () => void\n }\n}\n\n/**\n * Marks every recorded response out-of-date.\n *\n * @returns whether the store could be invalidated. A store without the API, or\n * one that throws, cannot be trusted afterwards — its answers stay definitive\n * for the previous identity until a later transition flags successfully.\n */\nfunction invalidate (store: AuthorizationStore): boolean {\n try {\n const flag = store.updater?.flagAuthorizationMetadata\n if (typeof flag !== 'function') {\n throw new Error('flagAuthorizationMetadata is unavailable')\n }\n flag.call(store.updater)\n return true\n } catch (error) {\n debug.warn(`Could not flag authorization metadata: ${String(error)}`)\n return false\n }\n}\n\n/**\n * Flags the store's authorization metadata on every identity transition.\n *\n * @returns the unsubscribe function — the caller owns the lifetime (it ends\n * with the session's subscription).\n */\nexport function flagAuthorizationOnSessionTransitions (\n store: TransitionStore,\n session: SessionLike\n): () => void {\n const onTransition = (): void => {\n const state = storeState(store)\n // Whose credentials a request would carry changed: every answer recorded\n // under the previous identity is suspect from here on.\n state.generation += 1\n state.invalidationFailed = !invalidate(store)\n }\n const subscription = subscribeIdentity(session, { onTransition })\n return () => subscription.unsubscribe()\n}\n\ntype StoreAuthorizationState = {\n /** Identity transitions observed for this store. */\n generation: number\n /**\n * The store could not invalidate its metadata on the last transition, so a\n * definitive answer is not evidence that it is current.\n */\n invalidationFailed: boolean\n}\n\n// Scoped per store: two `createSolidLogic` instances with different sessions\n// must not overtake each other's repairs, and a failed invalidation in one\n// store says nothing about another.\nconst storeStates = new WeakMap<object, StoreAuthorizationState>()\nconst sharedState: StoreAuthorizationState = { generation: 0, invalidationFailed: false }\n\nfunction storeState (store: unknown): StoreAuthorizationState {\n if (store === null || typeof store !== 'object') return sharedState\n let state = storeStates.get(store)\n if (!state) {\n state = { generation: 0, invalidationFailed: false }\n storeStates.set(store, state)\n }\n return state\n}\n\n/** How many times a repair is repeated when the identity keeps changing. */\nconst REPAIR_ATTEMPTS = 3\n\n/**\n * Re-establishes `doc`'s answer under the current identity: mark every\n * recorded response out-of-date — including one recorded AFTER the transition,\n * which the transition's own flag could not have marked — and load, which\n * refetches a fully flagged document.\n *\n * Each attempt is stamped with the store's transition generation and repeated\n * when the identity changed under it: the response then belongs to the\n * previous identity and must not answer for the current one, or a caller could\n * write under the new identity on the old identity's authorization. If the\n * identity keeps changing, the answer stays \"unknown\" — never stale.\n *\n * Returns `undefined` whenever the answer cannot be established: no load\n * capability, an invalidation that failed (the recorded answers are still\n * definitive for the previous identity), a failed load, or an identity that\n * changed throughout every attempt.\n */\nasync function repairDocument (\n store: AuthorizationStore,\n doc: unknown\n): Promise<string | boolean | undefined> {\n const state = storeState(store)\n const load = store.fetcher?.load\n if (typeof load !== 'function') return undefined\n for (let attempt = 0; attempt < REPAIR_ATTEMPTS; attempt++) {\n const generation = state.generation\n if (!invalidate(store)) return undefined\n state.invalidationFailed = false\n try {\n await load.call(store.fetcher, doc)\n } catch (error) {\n debug.warn(`Could not reload ${String(doc)}: ${String(error)}`)\n return undefined\n }\n // The read below is synchronous, so a generation that still matches means\n // no transition slipped in between the response and the answer.\n if (generation === state.generation) {\n return store.updater?.editable?.(doc)\n }\n }\n return undefined\n}\n\n/**\n * Make the store able to answer for `doc` under the current identity before\n * its cached triples are read or its editability gates a write. A flagged\n * store answers `undefined` and is repaired here; a store whose invalidation\n * FAILED still answers definitively for the previous identity, so it is\n * repaired too (and keeps being repaired until a later transition flags\n * successfully, since the failure says nothing about which other documents are\n * stale).\n *\n * Returns whether the answer was established. `false` means a repair was\n * needed and could not complete: the caller must not consume cached triples\n * from that document and must not offer a write on it.\n */\nexport async function ensureDocumentAuthorization (\n store: AuthorizationStore,\n doc: unknown\n): Promise<boolean> {\n const state = storeState(store)\n if (!state.invalidationFailed && store.updater?.editable?.(doc) !== undefined) {\n return true\n }\n return (await repairDocument(store, doc)) !== undefined\n}\n\n/**\n * Load a document and make sure its cached triples can be read under the\n * current identity. The load itself is generation-checked: a response begun\n * under the previous identity can be recorded AFTER\n * `flagAuthorizationMetadata()` ran (the flag only marks response nodes that\n * already existed), which leaves a definitive-looking answer from the old\n * identity behind — so an overtaken load is repaired instead of being trusted.\n *\n * Returns whether the document can be consumed (see\n * ensureDocumentAuthorization). Load errors propagate, as a plain `load()`\n * would.\n */\nexport async function loadAuthorizedDocument (\n store: AuthorizationStore,\n doc: unknown\n): Promise<boolean> {\n const state = storeState(store)\n const generation = state.generation\n const load = store.fetcher?.load\n if (typeof load !== 'function') {\n // A plain load() would fail on a store with no fetcher: this call loads\n // `doc`, so it must not answer \"consumed\" after skipping the load.\n throw new Error('fetcher.load is unavailable')\n }\n await load.call(store.fetcher, doc)\n if (generation !== state.generation) {\n return (await repairDocument(store, doc)) !== undefined\n }\n return ensureDocumentAuthorization(store, doc)\n}\n"],"mappings":"4EAiDA,SAAS,EAAY,EAAoC,CACvD,GAAI,CACF,IAAM,EAAO,EAAM,SAAS,0BAC5B,GAAI,OAAO,GAAS,WAClB,MAAU,MAAM,0CAA0C,EAG5D,OADA,EAAK,KAAK,EAAM,OAAO,EAChB,EACT,OAAS,EAAO,CAEd,OADA,EAAA,KAAW,0CAA0C,OAAO,CAAK,GAAG,EAC7D,EACT,CACF,CAQA,SAAgB,EACd,EACA,EACY,CAQZ,IAAM,EAAe,EAAA,kBAAkB,EAAS,CAAE,iBAPjB,CAC/B,IAAM,EAAQ,EAAW,CAAK,EAG9B,EAAM,YAAc,EACpB,EAAM,mBAAqB,CAAC,EAAW,CAAK,CAC9C,CAC+D,CAAC,EAChE,UAAa,EAAa,YAAY,CACxC,CAeA,IAAM,EAAc,IAAI,QAClB,EAAuC,CAAE,WAAY,EAAG,mBAAoB,EAAM,EAExF,SAAS,EAAY,EAAyC,CAC5D,GAAsB,OAAO,GAAU,WAAnC,EAA6C,OAAO,EACxD,IAAI,EAAQ,EAAY,IAAI,CAAK,EAKjC,OAJK,IACH,EAAQ,CAAE,WAAY,EAAG,mBAAoB,EAAM,EACnD,EAAY,IAAI,EAAO,CAAK,GAEvB,CACT,CAGA,IAAM,EAAkB,EAmBxB,eAAe,EACb,EACA,EACuC,CACvC,IAAM,EAAQ,EAAW,CAAK,EACxB,EAAO,EAAM,SAAS,KAC5B,GAAI,OAAO,GAAS,WACpB,IAAK,IAAI,EAAU,EAAG,EAAU,EAAiB,IAAW,CAC1D,IAAM,EAAa,EAAM,WACzB,GAAI,CAAC,EAAW,CAAK,EAAG,OACxB,EAAM,mBAAqB,GAC3B,GAAI,CACF,MAAM,EAAK,KAAK,EAAM,QAAS,CAAG,CACpC,OAAS,EAAO,CACd,EAAA,KAAW,oBAAoB,OAAO,CAAG,EAAE,IAAI,OAAO,CAAK,GAAG,EAC9D,MACF,CAGA,GAAI,IAAe,EAAM,WACvB,OAAO,EAAM,SAAS,WAAW,CAAG,CAExC,CAEF,CAeA,eAAsB,EACpB,EACA,EACkB,CAKlB,MAHI,CADU,EAAW,CACpB,CAAA,CAAM,oBAAsB,EAAM,SAAS,WAAW,CAAG,IAAM,IAAA,IAG5D,MAAM,EAAe,EAAO,CAAG,IAAO,IAAA,EAChD,CAcA,eAAsB,EACpB,EACA,EACkB,CAClB,IAAM,EAAQ,EAAW,CAAK,EACxB,EAAa,EAAM,WACnB,EAAO,EAAM,SAAS,KAC5B,GAAI,OAAO,GAAS,WAGlB,MAAU,MAAM,6BAA6B,EAM/C,OAJA,MAAM,EAAK,KAAK,EAAM,QAAS,CAAG,EAC9B,IAAe,EAAM,WAGlB,EAA4B,EAAO,CAAG,EAFnC,MAAM,EAAe,EAAO,CAAG,IAAO,IAAA,EAGlD"}
|
|
1
|
+
{"version":3,"file":"flagAuthorizationOnTransitions.cjs.js","names":["subscribeIdentity","require_debug","require_identityState","invalidate","flag","store","error","flagAuthorizationOnSessionTransitions","subscription","session","state","storeState","storeStates","sharedState","REPAIR_ATTEMPTS","repairDocument","load","attempt","generation","doc","ensureDocumentAuthorization","loadAuthorizedDocument"],"sources":["../../src/authSession/flagAuthorizationOnTransitions.ts"],"sourcesContent":["/**\n * Session transitions invalidate the store's cached HTTP authorization\n * metadata.\n *\n * `UpdateManager.editable()` is a synchronous read of the responses recorded\n * under `fetcher.appNode`. Those responses are not keyed by identity, so a\n * document fetched anonymously (before a restore completed) or under a\n * previous WebID keeps answering for the old identity: a writable document can\n * look read-only after login, and a read-only one can look writable after\n * logout.\n *\n * `UpdateManager.flagAuthorizationMetadata()` marks every recorded response\n * out-of-date, so `editable()` answers \"unknown\" instead of the previous\n * identity's access. It is wired HERE, once, subscribed to the session's\n * identity state — so it covers every transition whichever path noticed it (a\n * session event, a token update, a refocus resync, a cookie probe), rather\n * than whichever events a call site remembered to listen for.\n *\n * The repair is then a plain `load()`: rdflib refetches a document whose\n * recorded answers are ALL flagged (linkeddata/rdflib.js#871, in 2.4.1), which\n * is exactly the state a transition leaves behind — and `checkEditable()`\n * heals the same way. Nothing forces a fetch by hand any more.\n *\n * REQUIRES rdflib >= 2.4.1: on 2.4.0 `load()` answered such a document from\n * the cache, so the decision points below could not re-establish an answer.\n */\n\nimport * as debug from '../util/debug'\nimport { subscribeIdentity, type SessionLike } from './identityState'\n\nexport type TransitionStore = {\n updater?: { flagAuthorizationMetadata?: () => void }\n}\n\nexport type AuthorizationStore = {\n fetcher?: { load?: (doc: unknown) => unknown }\n updater?: {\n editable?: (uri: unknown) => string | boolean | undefined\n flagAuthorizationMetadata?: () => void\n }\n}\n\n/**\n * Marks every recorded response out-of-date.\n *\n * @returns whether the store could be invalidated. A store without the API, or\n * one that throws, cannot be trusted afterwards — its answers stay definitive\n * for the previous identity until a later transition flags successfully.\n */\nfunction invalidate (store: AuthorizationStore): boolean {\n try {\n const flag = store.updater?.flagAuthorizationMetadata\n if (typeof flag !== 'function') {\n throw new Error('flagAuthorizationMetadata is unavailable')\n }\n flag.call(store.updater)\n return true\n } catch (error) {\n debug.warn(`Could not flag authorization metadata: ${String(error)}`)\n return false\n }\n}\n\n/**\n * Flags the store's authorization metadata on every identity transition.\n *\n * @returns the unsubscribe function — the caller owns the lifetime (it ends\n * with the session's subscription).\n */\nexport function flagAuthorizationOnSessionTransitions (\n store: TransitionStore,\n session: SessionLike\n): () => void {\n const onTransition = (): void => {\n const state = storeState(store)\n // Whose credentials a request would carry changed: every answer recorded\n // under the previous identity is suspect from here on.\n state.generation += 1\n state.invalidationFailed = !invalidate(store)\n }\n const subscription = subscribeIdentity(session, { onTransition })\n return () => subscription.unsubscribe()\n}\n\ntype StoreAuthorizationState = {\n /** Identity transitions observed for this store. */\n generation: number\n /**\n * The store could not invalidate its metadata on the last transition, so a\n * definitive answer is not evidence that it is current.\n */\n invalidationFailed: boolean\n}\n\n// Scoped per store: two `createSolidLogic` instances with different sessions\n// must not overtake each other's repairs, and a failed invalidation in one\n// store says nothing about another.\nconst storeStates = new WeakMap<object, StoreAuthorizationState>()\nconst sharedState: StoreAuthorizationState = { generation: 0, invalidationFailed: false }\n\nfunction storeState (store: unknown): StoreAuthorizationState {\n if (store === null || typeof store !== 'object') return sharedState\n let state = storeStates.get(store)\n if (!state) {\n state = { generation: 0, invalidationFailed: false }\n storeStates.set(store, state)\n }\n return state\n}\n\n/** How many times a repair is repeated when the identity keeps changing. */\nconst REPAIR_ATTEMPTS = 3\n\n/**\n * Re-establishes `doc`'s answer under the current identity: mark every\n * recorded response out-of-date — including one recorded AFTER the transition,\n * which the transition's own flag could not have marked — and load, which\n * refetches a fully flagged document.\n *\n * Each attempt is stamped with the store's transition generation and repeated\n * when the identity changed under it: the response then belongs to the\n * previous identity and must not answer for the current one, or a caller could\n * write under the new identity on the old identity's authorization. If the\n * identity keeps changing, the answer stays \"unknown\" — never stale.\n *\n * Returns `undefined` whenever the answer cannot be established: no load\n * capability, an invalidation that failed (the recorded answers are still\n * definitive for the previous identity), a failed load, or an identity that\n * changed throughout every attempt.\n */\nasync function repairDocument (\n store: AuthorizationStore,\n doc: unknown\n): Promise<string | boolean | undefined> {\n const state = storeState(store)\n const load = store.fetcher?.load\n if (typeof load !== 'function') return undefined\n for (let attempt = 0; attempt < REPAIR_ATTEMPTS; attempt++) {\n const generation = state.generation\n if (!invalidate(store)) return undefined\n state.invalidationFailed = false\n try {\n await load.call(store.fetcher, doc)\n } catch (error) {\n debug.warn(`Could not reload ${String(doc)}: ${String(error)}`)\n return undefined\n }\n // The read below is synchronous, so a generation that still matches means\n // no transition slipped in between the response and the answer.\n if (generation === state.generation) {\n return store.updater?.editable?.(doc)\n }\n }\n return undefined\n}\n\n/**\n * Make the store able to answer for `doc` under the current identity before\n * its cached triples are read or its editability gates a write. A flagged\n * store answers `undefined` and is repaired here; a store whose invalidation\n * FAILED still answers definitively for the previous identity, so it is\n * repaired too (and keeps being repaired until a later transition flags\n * successfully, since the failure says nothing about which other documents are\n * stale).\n *\n * Returns whether the answer was established. `false` means a repair was\n * needed and could not complete: the caller must not consume cached triples\n * from that document and must not offer a write on it.\n */\nexport async function ensureDocumentAuthorization (\n store: AuthorizationStore,\n doc: unknown\n): Promise<boolean> {\n const state = storeState(store)\n if (!state.invalidationFailed && store.updater?.editable?.(doc) !== undefined) {\n return true\n }\n return (await repairDocument(store, doc)) !== undefined\n}\n\n/**\n * Load a document and make sure its cached triples can be read under the\n * current identity. The load itself is generation-checked: a response begun\n * under the previous identity can be recorded AFTER\n * `flagAuthorizationMetadata()` ran (the flag only marks response nodes that\n * already existed), which leaves a definitive-looking answer from the old\n * identity behind — so an overtaken load is repaired instead of being trusted.\n *\n * Returns whether the document can be consumed (see\n * ensureDocumentAuthorization). Load errors propagate, as a plain `load()`\n * would.\n */\nexport async function loadAuthorizedDocument (\n store: AuthorizationStore,\n doc: unknown\n): Promise<boolean> {\n const state = storeState(store)\n const generation = state.generation\n const load = store.fetcher?.load\n if (typeof load !== 'function') {\n // A plain load() would fail on a store with no fetcher: this call loads\n // `doc`, so it must not answer \"consumed\" after skipping the load.\n throw new Error('fetcher.load is unavailable')\n }\n await load.call(store.fetcher, doc)\n if (generation !== state.generation) {\n return (await repairDocument(store, doc)) !== undefined\n }\n return ensureDocumentAuthorization(store, doc)\n}\n"],"mappings":"4EAiDA,SAASG,EAAY,EAAoC,CACvD,GAAI,CACF,IAAMC,EAAOC,EAAM,SAAS,0BAC5B,GAAI,OAAOD,GAAS,WAClB,MAAU,MAAM,0CAA0C,EAG5D,OADAA,EAAK,KAAKC,EAAM,OAAO,EAChB,EACT,OAASC,EAAO,CAEd,OADAL,EAAA,KAAW,0CAA0C,OAAOK,CAAK,GAAG,EAC7D,EACT,CACF,CAQA,SAAgBC,EACd,EACA,EACY,CAQZ,IAAMC,EAAeR,EAAAA,kBAAkBS,EAAS,CAAE,iBAPjB,CAC/B,IAAMC,EAAQC,EAAWN,CAAK,EAG9B,EAAM,YAAc,EACpB,EAAM,mBAAqB,CAACF,EAAWE,CAAK,CAC9C,CAC+D,CAAC,EAChE,UAAaG,EAAa,YAAY,CACxC,CAeA,IAAMI,EAAc,IAAI,QAClBC,EAAuC,CAAE,WAAY,EAAG,mBAAoB,EAAM,EAExF,SAASF,EAAY,EAAyC,CAC5D,GAAsB,OAAON,GAAU,WAAnCA,EAA6C,OAAOQ,EACxD,IAAIH,EAAQE,EAAY,IAAIP,CAAK,EAKjC,OAJKK,IACH,EAAQ,CAAE,WAAY,EAAG,mBAAoB,EAAM,EACnDE,EAAY,IAAIP,EAAOK,CAAK,GAEvBA,CACT,CAGA,IAAMI,EAAkB,EAmBxB,eAAeC,EACb,EACA,EACuC,CACvC,IAAML,EAAQC,EAAWN,CAAK,EACxBW,EAAOX,EAAM,SAAS,KAC5B,GAAI,OAAOW,GAAS,WACpB,IAAK,IAAIC,EAAU,EAAGA,EAAUH,EAAiB,IAAW,CAC1D,IAAMI,EAAaR,EAAM,WACzB,GAAI,CAACP,EAAWE,CAAK,EAAG,OACxB,EAAM,mBAAqB,GAC3B,GAAI,CACF,MAAMW,EAAK,KAAKX,EAAM,QAASc,CAAG,CACpC,OAASb,EAAO,CACdL,EAAA,KAAW,oBAAoB,OAAOkB,CAAG,EAAE,IAAI,OAAOb,CAAK,GAAG,EAC9D,MACF,CAGA,GAAIY,IAAeR,EAAM,WACvB,OAAOL,EAAM,SAAS,WAAWc,CAAG,CAExC,CAEF,CAeA,eAAsBC,EACpB,EACA,EACkB,CAKlB,MAHI,CADUT,EAAWN,CACpB,CAAA,CAAM,oBAAsBA,EAAM,SAAS,WAAWc,CAAG,IAAM,IAAA,IAG5D,MAAMJ,EAAeV,EAAOc,CAAG,IAAO,IAAA,EAChD,CAcA,eAAsBE,EACpB,EACA,EACkB,CAClB,IAAMX,EAAQC,EAAWN,CAAK,EACxBa,EAAaR,EAAM,WACnBM,EAAOX,EAAM,SAAS,KAC5B,GAAI,OAAOW,GAAS,WAGlB,MAAU,MAAM,6BAA6B,EAM/C,OAJA,MAAMA,EAAK,KAAKX,EAAM,QAASc,CAAG,EAC9BD,IAAeR,EAAM,WAGlBU,EAA4Bf,EAAOc,CAAG,EAFnC,MAAMJ,EAAeV,EAAOc,CAAG,IAAO,IAAA,EAGlD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flagAuthorizationOnTransitions.esm.js","names":[],"sources":["../../src/authSession/flagAuthorizationOnTransitions.ts"],"sourcesContent":["/**\n * Session transitions invalidate the store's cached HTTP authorization\n * metadata.\n *\n * `UpdateManager.editable()` is a synchronous read of the responses recorded\n * under `fetcher.appNode`. Those responses are not keyed by identity, so a\n * document fetched anonymously (before a restore completed) or under a\n * previous WebID keeps answering for the old identity: a writable document can\n * look read-only after login, and a read-only one can look writable after\n * logout.\n *\n * `UpdateManager.flagAuthorizationMetadata()` marks every recorded response\n * out-of-date, so `editable()` answers \"unknown\" instead of the previous\n * identity's access. It is wired HERE, once, subscribed to the session's\n * identity state — so it covers every transition whichever path noticed it (a\n * session event, a token update, a refocus resync, a cookie probe), rather\n * than whichever events a call site remembered to listen for.\n *\n * The repair is then a plain `load()`: rdflib refetches a document whose\n * recorded answers are ALL flagged (linkeddata/rdflib.js#871, in 2.4.1), which\n * is exactly the state a transition leaves behind — and `checkEditable()`\n * heals the same way. Nothing forces a fetch by hand any more.\n *\n * REQUIRES rdflib >= 2.4.1: on 2.4.0 `load()` answered such a document from\n * the cache, so the decision points below could not re-establish an answer.\n */\n\nimport * as debug from '../util/debug'\nimport { subscribeIdentity, type SessionLike } from './identityState'\n\nexport type TransitionStore = {\n updater?: { flagAuthorizationMetadata?: () => void }\n}\n\nexport type AuthorizationStore = {\n fetcher?: { load?: (doc: unknown) => unknown }\n updater?: {\n editable?: (uri: unknown) => string | boolean | undefined\n flagAuthorizationMetadata?: () => void\n }\n}\n\n/**\n * Marks every recorded response out-of-date.\n *\n * @returns whether the store could be invalidated. A store without the API, or\n * one that throws, cannot be trusted afterwards — its answers stay definitive\n * for the previous identity until a later transition flags successfully.\n */\nfunction invalidate (store: AuthorizationStore): boolean {\n try {\n const flag = store.updater?.flagAuthorizationMetadata\n if (typeof flag !== 'function') {\n throw new Error('flagAuthorizationMetadata is unavailable')\n }\n flag.call(store.updater)\n return true\n } catch (error) {\n debug.warn(`Could not flag authorization metadata: ${String(error)}`)\n return false\n }\n}\n\n/**\n * Flags the store's authorization metadata on every identity transition.\n *\n * @returns the unsubscribe function — the caller owns the lifetime (it ends\n * with the session's subscription).\n */\nexport function flagAuthorizationOnSessionTransitions (\n store: TransitionStore,\n session: SessionLike\n): () => void {\n const onTransition = (): void => {\n const state = storeState(store)\n // Whose credentials a request would carry changed: every answer recorded\n // under the previous identity is suspect from here on.\n state.generation += 1\n state.invalidationFailed = !invalidate(store)\n }\n const subscription = subscribeIdentity(session, { onTransition })\n return () => subscription.unsubscribe()\n}\n\ntype StoreAuthorizationState = {\n /** Identity transitions observed for this store. */\n generation: number\n /**\n * The store could not invalidate its metadata on the last transition, so a\n * definitive answer is not evidence that it is current.\n */\n invalidationFailed: boolean\n}\n\n// Scoped per store: two `createSolidLogic` instances with different sessions\n// must not overtake each other's repairs, and a failed invalidation in one\n// store says nothing about another.\nconst storeStates = new WeakMap<object, StoreAuthorizationState>()\nconst sharedState: StoreAuthorizationState = { generation: 0, invalidationFailed: false }\n\nfunction storeState (store: unknown): StoreAuthorizationState {\n if (store === null || typeof store !== 'object') return sharedState\n let state = storeStates.get(store)\n if (!state) {\n state = { generation: 0, invalidationFailed: false }\n storeStates.set(store, state)\n }\n return state\n}\n\n/** How many times a repair is repeated when the identity keeps changing. */\nconst REPAIR_ATTEMPTS = 3\n\n/**\n * Re-establishes `doc`'s answer under the current identity: mark every\n * recorded response out-of-date — including one recorded AFTER the transition,\n * which the transition's own flag could not have marked — and load, which\n * refetches a fully flagged document.\n *\n * Each attempt is stamped with the store's transition generation and repeated\n * when the identity changed under it: the response then belongs to the\n * previous identity and must not answer for the current one, or a caller could\n * write under the new identity on the old identity's authorization. If the\n * identity keeps changing, the answer stays \"unknown\" — never stale.\n *\n * Returns `undefined` whenever the answer cannot be established: no load\n * capability, an invalidation that failed (the recorded answers are still\n * definitive for the previous identity), a failed load, or an identity that\n * changed throughout every attempt.\n */\nasync function repairDocument (\n store: AuthorizationStore,\n doc: unknown\n): Promise<string | boolean | undefined> {\n const state = storeState(store)\n const load = store.fetcher?.load\n if (typeof load !== 'function') return undefined\n for (let attempt = 0; attempt < REPAIR_ATTEMPTS; attempt++) {\n const generation = state.generation\n if (!invalidate(store)) return undefined\n state.invalidationFailed = false\n try {\n await load.call(store.fetcher, doc)\n } catch (error) {\n debug.warn(`Could not reload ${String(doc)}: ${String(error)}`)\n return undefined\n }\n // The read below is synchronous, so a generation that still matches means\n // no transition slipped in between the response and the answer.\n if (generation === state.generation) {\n return store.updater?.editable?.(doc)\n }\n }\n return undefined\n}\n\n/**\n * Make the store able to answer for `doc` under the current identity before\n * its cached triples are read or its editability gates a write. A flagged\n * store answers `undefined` and is repaired here; a store whose invalidation\n * FAILED still answers definitively for the previous identity, so it is\n * repaired too (and keeps being repaired until a later transition flags\n * successfully, since the failure says nothing about which other documents are\n * stale).\n *\n * Returns whether the answer was established. `false` means a repair was\n * needed and could not complete: the caller must not consume cached triples\n * from that document and must not offer a write on it.\n */\nexport async function ensureDocumentAuthorization (\n store: AuthorizationStore,\n doc: unknown\n): Promise<boolean> {\n const state = storeState(store)\n if (!state.invalidationFailed && store.updater?.editable?.(doc) !== undefined) {\n return true\n }\n return (await repairDocument(store, doc)) !== undefined\n}\n\n/**\n * Load a document and make sure its cached triples can be read under the\n * current identity. The load itself is generation-checked: a response begun\n * under the previous identity can be recorded AFTER\n * `flagAuthorizationMetadata()` ran (the flag only marks response nodes that\n * already existed), which leaves a definitive-looking answer from the old\n * identity behind — so an overtaken load is repaired instead of being trusted.\n *\n * Returns whether the document can be consumed (see\n * ensureDocumentAuthorization). Load errors propagate, as a plain `load()`\n * would.\n */\nexport async function loadAuthorizedDocument (\n store: AuthorizationStore,\n doc: unknown\n): Promise<boolean> {\n const state = storeState(store)\n const generation = state.generation\n const load = store.fetcher?.load\n if (typeof load !== 'function') {\n // A plain load() would fail on a store with no fetcher: this call loads\n // `doc`, so it must not answer \"consumed\" after skipping the load.\n throw new Error('fetcher.load is unavailable')\n }\n await load.call(store.fetcher, doc)\n if (generation !== state.generation) {\n return (await repairDocument(store, doc)) !== undefined\n }\n return ensureDocumentAuthorization(store, doc)\n}\n"],"mappings":";;;AAiDA,SAAS,EAAY,GAAoC;CACvD,IAAI;EACF,IAAM,IAAO,EAAM,SAAS;EAC5B,IAAI,OAAO,KAAS,YAClB,MAAU,MAAM,0CAA0C;EAG5D,OADA,EAAK,KAAK,EAAM,OAAO,GAChB;CACT,SAAS,GAAO;EAEd,OADA,EAAW,0CAA0C,OAAO,CAAK,GAAG,GAC7D;CACT;AACF;AAQA,SAAgB,EACd,GACA,GACY;CAQZ,IAAM,IAAe,EAAkB,GAAS,EAAE,oBAPjB;EAC/B,IAAM,IAAQ,EAAW,CAAK;EAI9B,AADA,EAAM,cAAc,GACpB,EAAM,qBAAqB,CAAC,EAAW,CAAK;CAC9C,EAC+D,CAAC;CAChE,aAAa,EAAa,YAAY;AACxC;AAeA,IAAM,oBAAc,IAAI,QAAyC,GAC3D,IAAuC;CAAE,YAAY;CAAG,oBAAoB;AAAM;AAExF,SAAS,EAAY,GAAyC;CAC5D,IAAsB,OAAO,KAAU,aAAnC,GAA6C,OAAO;CACxD,IAAI,IAAQ,EAAY,IAAI,CAAK;CAKjC,OAJK,MACH,IAAQ;EAAE,YAAY;EAAG,oBAAoB;CAAM,GACnD,EAAY,IAAI,GAAO,CAAK,IAEvB;AACT;AAGA,IAAM,IAAkB;AAmBxB,eAAe,EACb,GACA,GACuC;CACvC,IAAM,IAAQ,EAAW,CAAK,GACxB,IAAO,EAAM,SAAS;CAC5B,IAAI,OAAO,KAAS,YACpB,KAAK,IAAI,IAAU,GAAG,IAAU,GAAiB,KAAW;EAC1D,IAAM,IAAa,EAAM;EACzB,IAAI,CAAC,EAAW,CAAK,GAAG;EACxB,EAAM,qBAAqB;EAC3B,IAAI;GACF,MAAM,EAAK,KAAK,EAAM,SAAS,CAAG;EACpC,SAAS,GAAO;GACd,EAAW,oBAAoB,OAAO,CAAG,EAAE,IAAI,OAAO,CAAK,GAAG;GAC9D;EACF;EAGA,IAAI,MAAe,EAAM,YACvB,OAAO,EAAM,SAAS,WAAW,CAAG;CAExC;AAEF;AAeA,eAAsB,EACpB,GACA,GACkB;CAKlB,OAHI,CADU,EAAW,CACpB,CAAA,CAAM,sBAAsB,EAAM,SAAS,WAAW,CAAG,MAAM,KAAA,KAG5D,MAAM,EAAe,GAAO,CAAG,MAAO,KAAA;AAChD;AAcA,eAAsB,EACpB,GACA,GACkB;CAClB,IAAM,IAAQ,EAAW,CAAK,GACxB,IAAa,EAAM,YACnB,IAAO,EAAM,SAAS;CAC5B,IAAI,OAAO,KAAS,YAGlB,MAAU,MAAM,6BAA6B;CAM/C,OAJA,MAAM,EAAK,KAAK,EAAM,SAAS,CAAG,GAC9B,MAAe,EAAM,aAGlB,EAA4B,GAAO,CAAG,IAFnC,MAAM,EAAe,GAAO,CAAG,MAAO,KAAA;AAGlD"}
|
|
1
|
+
{"version":3,"file":"flagAuthorizationOnTransitions.esm.js","names":["invalidate","flag","store","error","warn","flagAuthorizationOnSessionTransitions","subscription","subscribeIdentity","session","state","storeState","storeStates","sharedState","REPAIR_ATTEMPTS","repairDocument","load","attempt","generation","doc","ensureDocumentAuthorization","loadAuthorizedDocument"],"sources":["../../src/authSession/flagAuthorizationOnTransitions.ts"],"sourcesContent":["/**\n * Session transitions invalidate the store's cached HTTP authorization\n * metadata.\n *\n * `UpdateManager.editable()` is a synchronous read of the responses recorded\n * under `fetcher.appNode`. Those responses are not keyed by identity, so a\n * document fetched anonymously (before a restore completed) or under a\n * previous WebID keeps answering for the old identity: a writable document can\n * look read-only after login, and a read-only one can look writable after\n * logout.\n *\n * `UpdateManager.flagAuthorizationMetadata()` marks every recorded response\n * out-of-date, so `editable()` answers \"unknown\" instead of the previous\n * identity's access. It is wired HERE, once, subscribed to the session's\n * identity state — so it covers every transition whichever path noticed it (a\n * session event, a token update, a refocus resync, a cookie probe), rather\n * than whichever events a call site remembered to listen for.\n *\n * The repair is then a plain `load()`: rdflib refetches a document whose\n * recorded answers are ALL flagged (linkeddata/rdflib.js#871, in 2.4.1), which\n * is exactly the state a transition leaves behind — and `checkEditable()`\n * heals the same way. Nothing forces a fetch by hand any more.\n *\n * REQUIRES rdflib >= 2.4.1: on 2.4.0 `load()` answered such a document from\n * the cache, so the decision points below could not re-establish an answer.\n */\n\nimport * as debug from '../util/debug'\nimport { subscribeIdentity, type SessionLike } from './identityState'\n\nexport type TransitionStore = {\n updater?: { flagAuthorizationMetadata?: () => void }\n}\n\nexport type AuthorizationStore = {\n fetcher?: { load?: (doc: unknown) => unknown }\n updater?: {\n editable?: (uri: unknown) => string | boolean | undefined\n flagAuthorizationMetadata?: () => void\n }\n}\n\n/**\n * Marks every recorded response out-of-date.\n *\n * @returns whether the store could be invalidated. A store without the API, or\n * one that throws, cannot be trusted afterwards — its answers stay definitive\n * for the previous identity until a later transition flags successfully.\n */\nfunction invalidate (store: AuthorizationStore): boolean {\n try {\n const flag = store.updater?.flagAuthorizationMetadata\n if (typeof flag !== 'function') {\n throw new Error('flagAuthorizationMetadata is unavailable')\n }\n flag.call(store.updater)\n return true\n } catch (error) {\n debug.warn(`Could not flag authorization metadata: ${String(error)}`)\n return false\n }\n}\n\n/**\n * Flags the store's authorization metadata on every identity transition.\n *\n * @returns the unsubscribe function — the caller owns the lifetime (it ends\n * with the session's subscription).\n */\nexport function flagAuthorizationOnSessionTransitions (\n store: TransitionStore,\n session: SessionLike\n): () => void {\n const onTransition = (): void => {\n const state = storeState(store)\n // Whose credentials a request would carry changed: every answer recorded\n // under the previous identity is suspect from here on.\n state.generation += 1\n state.invalidationFailed = !invalidate(store)\n }\n const subscription = subscribeIdentity(session, { onTransition })\n return () => subscription.unsubscribe()\n}\n\ntype StoreAuthorizationState = {\n /** Identity transitions observed for this store. */\n generation: number\n /**\n * The store could not invalidate its metadata on the last transition, so a\n * definitive answer is not evidence that it is current.\n */\n invalidationFailed: boolean\n}\n\n// Scoped per store: two `createSolidLogic` instances with different sessions\n// must not overtake each other's repairs, and a failed invalidation in one\n// store says nothing about another.\nconst storeStates = new WeakMap<object, StoreAuthorizationState>()\nconst sharedState: StoreAuthorizationState = { generation: 0, invalidationFailed: false }\n\nfunction storeState (store: unknown): StoreAuthorizationState {\n if (store === null || typeof store !== 'object') return sharedState\n let state = storeStates.get(store)\n if (!state) {\n state = { generation: 0, invalidationFailed: false }\n storeStates.set(store, state)\n }\n return state\n}\n\n/** How many times a repair is repeated when the identity keeps changing. */\nconst REPAIR_ATTEMPTS = 3\n\n/**\n * Re-establishes `doc`'s answer under the current identity: mark every\n * recorded response out-of-date — including one recorded AFTER the transition,\n * which the transition's own flag could not have marked — and load, which\n * refetches a fully flagged document.\n *\n * Each attempt is stamped with the store's transition generation and repeated\n * when the identity changed under it: the response then belongs to the\n * previous identity and must not answer for the current one, or a caller could\n * write under the new identity on the old identity's authorization. If the\n * identity keeps changing, the answer stays \"unknown\" — never stale.\n *\n * Returns `undefined` whenever the answer cannot be established: no load\n * capability, an invalidation that failed (the recorded answers are still\n * definitive for the previous identity), a failed load, or an identity that\n * changed throughout every attempt.\n */\nasync function repairDocument (\n store: AuthorizationStore,\n doc: unknown\n): Promise<string | boolean | undefined> {\n const state = storeState(store)\n const load = store.fetcher?.load\n if (typeof load !== 'function') return undefined\n for (let attempt = 0; attempt < REPAIR_ATTEMPTS; attempt++) {\n const generation = state.generation\n if (!invalidate(store)) return undefined\n state.invalidationFailed = false\n try {\n await load.call(store.fetcher, doc)\n } catch (error) {\n debug.warn(`Could not reload ${String(doc)}: ${String(error)}`)\n return undefined\n }\n // The read below is synchronous, so a generation that still matches means\n // no transition slipped in between the response and the answer.\n if (generation === state.generation) {\n return store.updater?.editable?.(doc)\n }\n }\n return undefined\n}\n\n/**\n * Make the store able to answer for `doc` under the current identity before\n * its cached triples are read or its editability gates a write. A flagged\n * store answers `undefined` and is repaired here; a store whose invalidation\n * FAILED still answers definitively for the previous identity, so it is\n * repaired too (and keeps being repaired until a later transition flags\n * successfully, since the failure says nothing about which other documents are\n * stale).\n *\n * Returns whether the answer was established. `false` means a repair was\n * needed and could not complete: the caller must not consume cached triples\n * from that document and must not offer a write on it.\n */\nexport async function ensureDocumentAuthorization (\n store: AuthorizationStore,\n doc: unknown\n): Promise<boolean> {\n const state = storeState(store)\n if (!state.invalidationFailed && store.updater?.editable?.(doc) !== undefined) {\n return true\n }\n return (await repairDocument(store, doc)) !== undefined\n}\n\n/**\n * Load a document and make sure its cached triples can be read under the\n * current identity. The load itself is generation-checked: a response begun\n * under the previous identity can be recorded AFTER\n * `flagAuthorizationMetadata()` ran (the flag only marks response nodes that\n * already existed), which leaves a definitive-looking answer from the old\n * identity behind — so an overtaken load is repaired instead of being trusted.\n *\n * Returns whether the document can be consumed (see\n * ensureDocumentAuthorization). Load errors propagate, as a plain `load()`\n * would.\n */\nexport async function loadAuthorizedDocument (\n store: AuthorizationStore,\n doc: unknown\n): Promise<boolean> {\n const state = storeState(store)\n const generation = state.generation\n const load = store.fetcher?.load\n if (typeof load !== 'function') {\n // A plain load() would fail on a store with no fetcher: this call loads\n // `doc`, so it must not answer \"consumed\" after skipping the load.\n throw new Error('fetcher.load is unavailable')\n }\n await load.call(store.fetcher, doc)\n if (generation !== state.generation) {\n return (await repairDocument(store, doc)) !== undefined\n }\n return ensureDocumentAuthorization(store, doc)\n}\n"],"mappings":";;;AAiDA,SAASA,EAAY,GAAoC;CACvD,IAAI;EACF,IAAMC,IAAOC,EAAM,SAAS;EAC5B,IAAI,OAAOD,KAAS,YAClB,MAAU,MAAM,0CAA0C;EAG5D,OADAA,EAAK,KAAKC,EAAM,OAAO,GAChB;CACT,SAASC,GAAO;EAEd,OADAC,EAAW,0CAA0C,OAAOD,CAAK,GAAG,GAC7D;CACT;AACF;AAQA,SAAgBE,EACd,GACA,GACY;CAQZ,IAAMC,IAAeC,EAAkBC,GAAS,EAAE,oBAPjB;EAC/B,IAAMC,IAAQC,EAAWR,CAAK;EAI9B,AADA,EAAM,cAAc,GACpB,EAAM,qBAAqB,CAACF,EAAWE,CAAK;CAC9C,EAC+D,CAAC;CAChE,aAAaI,EAAa,YAAY;AACxC;AAeA,IAAMK,oBAAc,IAAI,QAAyC,GAC3DC,IAAuC;CAAE,YAAY;CAAG,oBAAoB;AAAM;AAExF,SAASF,EAAY,GAAyC;CAC5D,IAAsB,OAAOR,KAAU,aAAnCA,GAA6C,OAAOU;CACxD,IAAIH,IAAQE,EAAY,IAAIT,CAAK;CAKjC,OAJKO,MACH,IAAQ;EAAE,YAAY;EAAG,oBAAoB;CAAM,GACnDE,EAAY,IAAIT,GAAOO,CAAK,IAEvBA;AACT;AAGA,IAAMI,IAAkB;AAmBxB,eAAeC,EACb,GACA,GACuC;CACvC,IAAML,IAAQC,EAAWR,CAAK,GACxBa,IAAOb,EAAM,SAAS;CAC5B,IAAI,OAAOa,KAAS,YACpB,KAAK,IAAIC,IAAU,GAAGA,IAAUH,GAAiB,KAAW;EAC1D,IAAMI,IAAaR,EAAM;EACzB,IAAI,CAACT,EAAWE,CAAK,GAAG;EACxB,EAAM,qBAAqB;EAC3B,IAAI;GACF,MAAMa,EAAK,KAAKb,EAAM,SAASgB,CAAG;EACpC,SAASf,GAAO;GACd,EAAW,oBAAoB,OAAOe,CAAG,EAAE,IAAI,OAAOf,CAAK,GAAG;GAC9D;EACF;EAGA,IAAIc,MAAeR,EAAM,YACvB,OAAOP,EAAM,SAAS,WAAWgB,CAAG;CAExC;AAEF;AAeA,eAAsBC,EACpB,GACA,GACkB;CAKlB,OAHI,CADUT,EAAWR,CACpB,CAAA,CAAM,sBAAsBA,EAAM,SAAS,WAAWgB,CAAG,MAAM,KAAA,KAG5D,MAAMJ,EAAeZ,GAAOgB,CAAG,MAAO,KAAA;AAChD;AAcA,eAAsBE,EACpB,GACA,GACkB;CAClB,IAAMX,IAAQC,EAAWR,CAAK,GACxBe,IAAaR,EAAM,YACnBM,IAAOb,EAAM,SAAS;CAC5B,IAAI,OAAOa,KAAS,YAGlB,MAAU,MAAM,6BAA6B;CAM/C,OAJA,MAAMA,EAAK,KAAKb,EAAM,SAASgB,CAAG,GAC9BD,MAAeR,EAAM,aAGlBU,EAA4BjB,GAAOgB,CAAG,IAFnC,MAAMJ,EAAeZ,GAAOgB,CAAG,MAAO,KAAA;AAGlD"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e,t){return e.isActive===t.isActive?t.webId===e.webId?null:`sessionChange`:t.isActive?`sessionChange`:`logout`}function t(e,t){return e.webId===void 0||!e.isActive?!1:t.webId!==e.webId||!t.isActive}var n=e=>e.isActive===!0||e.isActive===void 0&&!!e.webId,r=2e3,i=new WeakMap,a=new WeakMap,o=e=>{if(typeof e!=`object`||!e)return;let t=i.get(e);return t||(t={session:e,raw:{isActive:!1},cleared:!1,cookieWebId:null,version:0,attemptId:0,subscribers:new Set,listenerAttached:!1,watchingTokenUpdates:!1},t.raw=u(t),i.set(e,t)),t};function s(e){return typeof e!=`object`||!e?!1:i.get(e)?.cleared===!0}function c(e){return s(e)?!0:e?.isActive===!1||e?.info?.isLoggedIn===!1}function l(e){return!c(e)&&n(e)}var u=e=>e.cleared?{isActive:!1}:{isActive:n(e.session),webId:e.session.webId};function d(e){let t=o(e);return t?l(e)?e.webId===void 0?{source:`none`}:{webId:e.webId,source:`session`}:t.cookieWebId===null?{source:`none`}:{webId:t.cookieWebId,source:`cookie`}:{source:`none`}}function f(e){return s(e)?{webId:void 0,isLoggedIn:!1}:{webId:e.webId,isLoggedIn:n(e)}}function p(e){let t=e?.restore;if(typeof t!=`function`||typeof e!=`object`||!e)return;let n=e,r=a.get(n);if(r)return r;let i=Promise.resolve().then(()=>t.call(e)).finally(()=>{a.delete(n)});return a.set(n,i),i}var m=(e,t)=>{e.subscribers.forEach(e=>e.onTransition?.(t)),e.subscribers.forEach(e=>{t.forEach(t=>e.onEvent?.(t))})};function h(r){r.cleared&&n(r.session)&&(r.cleared=!1),l(r.session)&&(r.cookieWebId=null);let i=u(r),a=e(r.raw,i),o=t(r.raw,i),s=a!==null||o;if(r.raw=i,!s)return;r.version+=1;let c=[];a&&c.push(a),o&&c.push(`identityReplaced`),m(r,c)}function g(e){if(e.cleared)return;let t=e.raw.isActive,n=e.raw.webId!==void 0
|
|
1
|
+
function e(e,t){return e.isActive===t.isActive?t.webId===e.webId?null:`sessionChange`:t.isActive?`sessionChange`:`logout`}function t(e,t){return e.webId===void 0||!e.isActive?!1:t.webId!==e.webId||!t.isActive}var n=e=>e.isActive===!0||e.isActive===void 0&&!!e.webId,r=2e3,i=new WeakMap,a=new WeakMap,o=e=>{if(typeof e!=`object`||!e)return;let t=i.get(e);return t||(t={session:e,raw:{isActive:!1},cleared:!1,cookieWebId:null,version:0,attemptId:0,subscribers:new Set,listenerAttached:!1,watchingTokenUpdates:!1},t.raw=u(t),i.set(e,t)),t};function s(e){return typeof e!=`object`||!e?!1:i.get(e)?.cleared===!0}function c(e){return s(e)?!0:e?.isActive===!1||e?.info?.isLoggedIn===!1}function l(e){return!c(e)&&n(e)}var u=e=>e.cleared?{isActive:!1}:{isActive:n(e.session),webId:e.session.webId};function d(e){let t=o(e);return t?l(e)?e.webId===void 0?{source:`none`}:{webId:e.webId,source:`session`}:t.cookieWebId===null?{source:`none`}:{webId:t.cookieWebId,source:`cookie`}:{source:`none`}}function f(e){return s(e)?{webId:void 0,isLoggedIn:!1}:{webId:e.webId,isLoggedIn:n(e)}}function p(e){let t=e?.restore;if(typeof t!=`function`||typeof e!=`object`||!e)return;let n=e,r=a.get(n);if(r)return r;let i=Promise.resolve().then(()=>t.call(e)).finally(()=>{a.delete(n)});return a.set(n,i),i}var m=(e,t)=>{e.subscribers.forEach(e=>e.onTransition?.(t)),e.subscribers.forEach(e=>{t.forEach(t=>e.onEvent?.(t))})};function h(r){r.cleared&&n(r.session)&&(r.cleared=!1),l(r.session)&&(r.cookieWebId=null);let i=u(r),a=e(r.raw,i),o=t(r.raw,i),s=a!==null||o;if(r.raw=i,!s)return;r.version+=1;let c=[];a&&c.push(a),o&&c.push(`identityReplaced`),m(r,c)}function g(e){if(e.cleared)return;let t=e.raw.isActive,n=e.raw.webId!==void 0;!t||!n?(e.raw=u(e),t&&(e.version+=1,m(e,[`logout`]))):(e.cleared=!0,e.raw=u(e),e.version+=1,m(e,[`logout`,`identityReplaced`]))}function _(e,t){if(e.attempt)return e.attempt;let r={id:++e.attemptId,version:e.version,raw:{isActive:n(e.session),webId:e.session.webId},promise:Promise.resolve().then(t).finally(()=>{e.attempt===r&&(e.attempt=void 0)})};return e.attempt=r,r}async function v(e){let t=y(e);if(typeof t!=`function`){h(e);return}let i=t,a=_(e,()=>i()),o=()=>a.id!==e.attemptId||e.version!==a.version,s=()=>n(e.session)===a.raw.isActive&&e.session.webId===a.raw.webId,c,l=!1,u=a.promise.then(e=>{c=e,l=!0},()=>{l=!0}),d=()=>{o()||(c===`cleared`?s()&&g(e):h(e))},f,p=new Promise(e=>{f=setTimeout(e,r)});try{await Promise.race([u,p])}finally{f!==void 0&&clearTimeout(f)}l?d():(h(e),u.then(d))}function y(e){let t;for(let n of e.subscribers)n.resync&&(t=n.resync);return t}function b(e){let t=e.session,n=t.setTokenDetails;typeof n!=`function`||e.watchingTokenUpdates||(e.watchingTokenUpdates=!0,t.setTokenDetails=(...r)=>{let i=u(e),a=()=>{let t=u(e);return t.webId!==i.webId||t.isActive!==i.isActive},o;try{o=n.apply(t,r)}catch(t){throw a()&&h(e),t}return o&&typeof o.then==`function`?o.then(t=>(a()&&h(e),t),t=>{throw a()&&h(e),t}):(a()&&h(e),o)})}function x(e,t={}){let n=o(e);if(!n)return{reportCookieIdentity:()=>void 0,unsubscribe:()=>void 0};let r={...t};n.subscribers.add(r),n.listenerAttached||(n.listenerAttached=!0,typeof e.addEventListener==`function`&&e.addEventListener(`sessionStateChange`,()=>h(n)),b(n));let i=typeof document>`u`?void 0:document;if(n.subscribers.size===1&&i&&typeof i.addEventListener==`function`){let e=()=>{i.visibilityState===`visible`&&(v(n),n.subscribers.forEach(e=>{e.onRefocus?.()}))};i.addEventListener(`visibilitychange`,e),n.detachDocument=()=>i.removeEventListener?.(`visibilitychange`,e)}let a=!1;return{reportCookieIdentity:e=>{a||S(n,e)},unsubscribe:()=>{a||(a=!0,n.subscribers.delete(r),n.subscribers.size===0&&(n.detachDocument?.(),n.detachDocument=void 0))}}}function S(e,t){if(e.cookieWebId===t||l(e.session))return;let n=e.cookieWebId!==null;e.cookieWebId=t;let r=[`sessionChange`];n&&r.push(`identityReplaced`),m(e,r)}function C(e,t=()=>{typeof window<`u`&&window.location.reload()}){e&&typeof e.on==`function`&&e.on(`identityReplaced`,t)}exports.classifyTransition=e,exports.effectiveIdentity=d,exports.identityReplaced=t,exports.legacySessionInfo=f,exports.reloadOnIdentityReplaced=C,exports.restoreSession=p,exports.sessionExplicitlyInactive=c,exports.sessionIsActive=n,exports.sessionOwnsIdentity=l,exports.sessionWasCleared=s,exports.subscribeIdentity=x;
|
|
2
2
|
//# sourceMappingURL=identityState.cjs.js.map
|