@undefineds.co/xpod 0.2.40 → 0.2.42
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/config/xpod.base.json +22 -0
- package/dist/api/handlers/WebIdProfileHandler.js +72 -22
- package/dist/api/handlers/WebIdProfileHandler.js.map +1 -1
- package/dist/components/components.jsonld +1 -0
- package/dist/components/context.jsonld +30 -0
- package/dist/identity/ValidatingIdentityProviderHttpHandler.d.ts +45 -0
- package/dist/identity/ValidatingIdentityProviderHttpHandler.js +129 -0
- package/dist/identity/ValidatingIdentityProviderHttpHandler.js.map +1 -0
- package/dist/identity/ValidatingIdentityProviderHttpHandler.jsonld +124 -0
- package/dist/identity/drizzle/PodLookupRepository.d.ts +10 -0
- package/dist/identity/drizzle/PodLookupRepository.js +109 -4
- package/dist/identity/drizzle/PodLookupRepository.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/ast-to-sparql.d.ts +2 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/ast-to-sparql.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/ast-to-sparql.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/comunica-patch.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/comunica-patch.js +20 -6
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/comunica-patch.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/discovery/typeindex-discovery.d.ts +2 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/discovery/typeindex-discovery.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/discovery/typeindex-discovery.js +13 -3
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/discovery/typeindex-discovery.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/ldp-executor.d.ts +5 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/ldp-executor.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/ldp-executor.js +88 -48
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/ldp-executor.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/ldp-strategy.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/ldp-strategy.js +5 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/ldp-strategy.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/pod-executor.d.ts +7 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/pod-executor.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/pod-executor.js +44 -14
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/pod-executor.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/sparql-strategy.d.ts +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/sparql-strategy.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/sparql-strategy.js +3 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/sparql-strategy.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/strategy-factory.d.ts +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/strategy-factory.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/strategy-factory.js +4 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/strategy-factory.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/types.d.ts +3 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/types.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-database.d.ts +44 -22
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-database.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-database.js +238 -44
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-database.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-dialect.d.ts +7 -3
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-dialect.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-dialect.js +33 -4
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-dialect.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-session.d.ts +2 -2
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-session.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/query-builders/helpers.d.ts +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/query-builders/helpers.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/query-builders/helpers.js +6 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/query-builders/helpers.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/repository.d.ts +76 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/repository.d.ts.map +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/repository.js +132 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/repository.js.map +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/resource-reference.d.ts +13 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/resource-reference.d.ts.map +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/resource-reference.js +99 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/resource-reference.js.map +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/resource-resolver/base-resolver.d.ts +15 -4
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/resource-resolver/base-resolver.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/resource-resolver/base-resolver.js +65 -24
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/resource-resolver/base-resolver.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/runtime/pod-runtime.d.ts +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/runtime/pod-runtime.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/runtime/pod-runtime.js +7 -3
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/runtime/pod-runtime.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/select-plan.d.ts +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/select-plan.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql/builder/expression-builder.d.ts +3 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql/builder/expression-builder.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql/builder/expression-builder.js +57 -4
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql/builder/expression-builder.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql/builder/update-builder.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql/builder/update-builder.js +60 -45
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql/builder/update-builder.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql-engine.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql-engine.js +5 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql-engine.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/builder.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/builder.js +4 -2
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/builder.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/inverse.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/inverse.js +2 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/inverse.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/uri.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/uri.js +2 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/uri.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/types.d.ts +4 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/types.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/uri/resolver.d.ts +8 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/uri/resolver.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/uri/resolver.js +133 -46
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/uri/resolver.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/uri/types.d.ts +4 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/uri/types.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/zod-integration.d.ts +2 -2
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/zod-integration.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/ast-to-sparql.d.ts +2 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/ast-to-sparql.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/ast-to-sparql.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/comunica-patch.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/comunica-patch.js +20 -6
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/comunica-patch.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/discovery/typeindex-discovery.d.ts +2 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/discovery/typeindex-discovery.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/discovery/typeindex-discovery.js +13 -3
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/discovery/typeindex-discovery.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/ldp-executor.d.ts +5 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/ldp-executor.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/ldp-executor.js +88 -48
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/ldp-executor.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/ldp-strategy.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/ldp-strategy.js +5 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/ldp-strategy.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/pod-executor.d.ts +7 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/pod-executor.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/pod-executor.js +44 -14
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/pod-executor.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/sparql-strategy.d.ts +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/sparql-strategy.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/sparql-strategy.js +3 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/sparql-strategy.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/strategy-factory.d.ts +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/strategy-factory.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/strategy-factory.js +4 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/strategy-factory.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/types.d.ts +3 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/types.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-database.d.ts +44 -22
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-database.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-database.js +238 -44
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-database.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-dialect.d.ts +7 -3
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-dialect.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-dialect.js +33 -4
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-dialect.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-session.d.ts +2 -2
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-session.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/query-builders/helpers.d.ts +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/query-builders/helpers.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/query-builders/helpers.js +6 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/query-builders/helpers.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/repository.d.ts +76 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/repository.d.ts.map +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/repository.js +127 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/repository.js.map +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/resource-reference.d.ts +13 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/resource-reference.d.ts.map +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/resource-reference.js +95 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/resource-reference.js.map +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/resource-resolver/base-resolver.d.ts +15 -4
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/resource-resolver/base-resolver.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/resource-resolver/base-resolver.js +65 -24
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/resource-resolver/base-resolver.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/runtime/pod-runtime.d.ts +1 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/runtime/pod-runtime.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/runtime/pod-runtime.js +7 -3
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/runtime/pod-runtime.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/select-plan.d.ts +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/select-plan.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql/builder/expression-builder.d.ts +3 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql/builder/expression-builder.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql/builder/expression-builder.js +57 -4
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql/builder/expression-builder.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql/builder/update-builder.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql/builder/update-builder.js +60 -45
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql/builder/update-builder.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql-engine.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql-engine.js +5 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql-engine.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/builder.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/builder.js +4 -2
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/builder.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/inverse.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/inverse.js +2 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/inverse.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/uri.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/uri.js +2 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/uri.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/types.d.ts +4 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/types.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/uri/resolver.d.ts +8 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/uri/resolver.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/uri/resolver.js +133 -46
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/uri/resolver.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/uri/types.d.ts +4 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/uri/types.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/zod-integration.d.ts +2 -2
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/zod-integration.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/index.d.ts +3 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/index.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/index.js +3 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/index.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/pod.d.ts +13 -5
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/pod.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/utils/find-by-iri.d.ts +2 -2
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/utils/find-by-iri.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/index.d.ts +3 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/index.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/index.js +12 -3
- package/node_modules/@undefineds.co/drizzle-solid/dist/index.js.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/pod.d.ts +13 -5
- package/node_modules/@undefineds.co/drizzle-solid/dist/pod.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/utils/find-by-iri.d.ts +2 -2
- package/node_modules/@undefineds.co/drizzle-solid/dist/utils/find-by-iri.d.ts.map +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/package.json +1 -1
- package/package.json +2 -2
package/config/xpod.base.json
CHANGED
|
@@ -114,6 +114,28 @@
|
|
|
114
114
|
"htmlFile": "./static/app/auth.html"
|
|
115
115
|
}
|
|
116
116
|
},
|
|
117
|
+
{
|
|
118
|
+
"comment": "Validate CSS account cookies against account storage to avoid stale browser sessions after data resets.",
|
|
119
|
+
"@type": "Override",
|
|
120
|
+
"overrideInstance": {
|
|
121
|
+
"@id": "urn:solid-server:default:IdentityProviderHttpHandler"
|
|
122
|
+
},
|
|
123
|
+
"overrideParameters": {
|
|
124
|
+
"@type": "ValidatingIdentityProviderHttpHandler",
|
|
125
|
+
"providerFactory": {
|
|
126
|
+
"@id": "urn:solid-server:default:IdentityProviderFactory"
|
|
127
|
+
},
|
|
128
|
+
"cookieStore": {
|
|
129
|
+
"@id": "urn:solid-server:default:CookieStore"
|
|
130
|
+
},
|
|
131
|
+
"handler": {
|
|
132
|
+
"@id": "urn:solid-server:default:InteractionHandler"
|
|
133
|
+
},
|
|
134
|
+
"accountStorage": {
|
|
135
|
+
"@id": "urn:solid-server:default:AccountStorage"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
117
139
|
{
|
|
118
140
|
"comment": "Override main HTML template to remove CSS default header/footer wrapper.",
|
|
119
141
|
"@type": "Override",
|
|
@@ -19,10 +19,10 @@ function registerWebIdProfileRoutes(server, options) {
|
|
|
19
19
|
* 获取 WebID Profile (Turtle 格式)
|
|
20
20
|
* 这是 Solid 标准的 WebID 端点
|
|
21
21
|
*/
|
|
22
|
-
server.get('/:username/profile/card', async (
|
|
22
|
+
server.get('/:username/profile/card', async (request, response, params) => {
|
|
23
23
|
const username = decodeURIComponent(params.username);
|
|
24
24
|
try {
|
|
25
|
-
const profile = await resolveIdentityLookup(username, options);
|
|
25
|
+
const profile = await resolveIdentityLookup(username, options, request);
|
|
26
26
|
if (!profile) {
|
|
27
27
|
sendError(response, 404, 'Profile not found');
|
|
28
28
|
return;
|
|
@@ -94,10 +94,10 @@ function registerWebIdProfileRoutes(server, options) {
|
|
|
94
94
|
*
|
|
95
95
|
* 获取 WebID Profile 信息 (JSON 格式)
|
|
96
96
|
*/
|
|
97
|
-
server.get('/api/v1/identity/:username', async (
|
|
97
|
+
server.get('/api/v1/identity/:username', async (request, response, params) => {
|
|
98
98
|
const username = decodeURIComponent(params.username);
|
|
99
99
|
try {
|
|
100
|
-
const profile = await resolveIdentityLookup(username, options);
|
|
100
|
+
const profile = await resolveIdentityLookup(username, options, request);
|
|
101
101
|
if (!profile) {
|
|
102
102
|
sendError(response, 404, 'Profile not found');
|
|
103
103
|
return;
|
|
@@ -176,7 +176,7 @@ function registerWebIdProfileRoutes(server, options) {
|
|
|
176
176
|
});
|
|
177
177
|
logger.info('WebID Profile routes registered');
|
|
178
178
|
}
|
|
179
|
-
async function resolveIdentityLookup(username, options) {
|
|
179
|
+
async function resolveIdentityLookup(username, options, request) {
|
|
180
180
|
try {
|
|
181
181
|
const profile = await resolveProfileWithStorageBackfill(username, options);
|
|
182
182
|
if (profile) {
|
|
@@ -186,23 +186,33 @@ async function resolveIdentityLookup(username, options) {
|
|
|
186
186
|
catch (error) {
|
|
187
187
|
logger.warn(`Profile lookup unavailable for ${username}, falling back to Pod index: ${error}`);
|
|
188
188
|
}
|
|
189
|
-
return resolveProfileFromPods(username, options);
|
|
189
|
+
return resolveProfileFromPods(username, options, request);
|
|
190
190
|
}
|
|
191
|
-
async function resolveProfileFromPods(username, options) {
|
|
191
|
+
async function resolveProfileFromPods(username, options, request) {
|
|
192
192
|
const { podLookupRepo } = options;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
193
|
+
const identityBaseUrl = getHostedIdentityBaseUrl(request);
|
|
194
|
+
if (podLookupRepo) {
|
|
195
|
+
const webidUrl = buildHostedWebIdUrl(username, identityBaseUrl);
|
|
196
|
+
const webIdMatch = await tryFindPodByWebId(podLookupRepo, webidUrl, username);
|
|
197
|
+
if (webIdMatch) {
|
|
198
|
+
return profileFromPod(username, webidUrl, webIdMatch);
|
|
199
|
+
}
|
|
200
|
+
const match = await tryFindPodByStorageSlug(podLookupRepo, username);
|
|
201
|
+
if (match) {
|
|
202
|
+
return profileFromPod(username, buildStorageWebIdUrl(match.baseUrl), match);
|
|
203
|
+
}
|
|
200
204
|
}
|
|
201
|
-
const
|
|
202
|
-
if (
|
|
203
|
-
return
|
|
205
|
+
const hostedStorageUrl = new URL(`${encodeURIComponent(username)}/`, identityBaseUrl).toString();
|
|
206
|
+
if (await probeHostedStorageRoot(hostedStorageUrl, username)) {
|
|
207
|
+
return {
|
|
208
|
+
username,
|
|
209
|
+
webidUrl: buildHostedWebIdUrl(username, identityBaseUrl),
|
|
210
|
+
storageUrl: hostedStorageUrl,
|
|
211
|
+
storageMode: 'cloud',
|
|
212
|
+
oidcIssuer: deriveOrigin(identityBaseUrl),
|
|
213
|
+
};
|
|
204
214
|
}
|
|
205
|
-
return
|
|
215
|
+
return null;
|
|
206
216
|
}
|
|
207
217
|
async function resolveProfileWithStorageBackfill(username, options) {
|
|
208
218
|
const { profileRepo, podLookupRepo } = options;
|
|
@@ -298,14 +308,15 @@ function profileFromPod(username, webidUrl, pod) {
|
|
|
298
308
|
oidcIssuer: deriveOrigin(webidUrl),
|
|
299
309
|
};
|
|
300
310
|
}
|
|
301
|
-
function buildHostedWebIdUrl(username) {
|
|
302
|
-
return new URL(`${encodeURIComponent(username)}/profile/card#me`,
|
|
311
|
+
function buildHostedWebIdUrl(username, identityBaseUrl = getHostedIdentityBaseUrl()) {
|
|
312
|
+
return new URL(`${encodeURIComponent(username)}/profile/card#me`, identityBaseUrl).toString();
|
|
303
313
|
}
|
|
304
314
|
function buildStorageWebIdUrl(storageUrl) {
|
|
305
315
|
return new URL('profile/card#me', ensureTrailingSlash(storageUrl)).toString();
|
|
306
316
|
}
|
|
307
|
-
function getHostedIdentityBaseUrl() {
|
|
308
|
-
return ensureTrailingSlash(
|
|
317
|
+
function getHostedIdentityBaseUrl(request) {
|
|
318
|
+
return ensureTrailingSlash(requestOrigin(request) ??
|
|
319
|
+
absoluteHttpUrl(process.env.CSS_BASE_URL) ??
|
|
309
320
|
absoluteHttpUrl(process.env.BASE_URL) ??
|
|
310
321
|
'http://localhost:3000/');
|
|
311
322
|
}
|
|
@@ -321,6 +332,45 @@ function absoluteHttpUrl(value) {
|
|
|
321
332
|
return undefined;
|
|
322
333
|
}
|
|
323
334
|
}
|
|
335
|
+
function requestOrigin(request) {
|
|
336
|
+
if (!request?.headers) {
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
const host = firstHeader(request.headers['x-forwarded-host']) ?? firstHeader(request.headers.host);
|
|
340
|
+
if (!host) {
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
const proto = firstHeader(request.headers['x-forwarded-proto']) ?? 'https';
|
|
344
|
+
return absoluteHttpUrl(`${proto}://${host}`);
|
|
345
|
+
}
|
|
346
|
+
function firstHeader(value) {
|
|
347
|
+
if (Array.isArray(value)) {
|
|
348
|
+
return value[0];
|
|
349
|
+
}
|
|
350
|
+
return value;
|
|
351
|
+
}
|
|
352
|
+
async function probeHostedStorageRoot(storageUrl, username) {
|
|
353
|
+
const controller = new AbortController();
|
|
354
|
+
const timer = setTimeout(() => controller.abort(), 2_000);
|
|
355
|
+
try {
|
|
356
|
+
const response = await fetch(storageUrl, {
|
|
357
|
+
method: 'HEAD',
|
|
358
|
+
signal: controller.signal,
|
|
359
|
+
});
|
|
360
|
+
if ((response.status >= 200 && response.status < 400) || response.status === 401 || response.status === 403) {
|
|
361
|
+
logger.info(`Resolved hosted WebID profile for ${username} from existing storage root: ${storageUrl}`);
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
logger.warn(`Hosted storage root probe unavailable for ${username}: ${error}`);
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
finally {
|
|
371
|
+
clearTimeout(timer);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
324
374
|
function ensureTrailingSlash(url) {
|
|
325
375
|
return url.replace(/\/+$/, '') + '/';
|
|
326
376
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebIdProfileHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/WebIdProfileHandler.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAyBH,gEAgMC;AAtND,iEAAqD;AAKrD,MAAM,MAAM,GAAG,IAAA,oCAAY,EAAC,qBAAqB,CAAC,CAAC;AAiBnD,SAAgB,0BAA0B,CACxC,MAAiB,EACjB,OAAmC;IAEnC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAEhC;;;;;OAKG;IACH,MAAM,CAAC,GAAG,CAAC,yBAAyB,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;QACzE,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAErD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,qBAAqB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAE/D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,mBAAmB,CAAC,CAAC;gBAC9C,OAAO;YACT,CAAC;YAED,eAAe;YACf,MAAM,MAAM,GAAG,WAAW,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAE1D,QAAQ,CAAC,UAAU,GAAG,GAAG,CAAC;YAC1B,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;YAClD,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,OAAO,CAAC,QAAQ,sBAAsB,CAAC,CAAC;YACvE,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,6BAA6B,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;YAChE,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,uBAAuB,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAErB;;;;;;;;;;OAUG;IACH,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;QACpF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAErD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;YACzC,MAAM,OAAO,GAAG,IAAiE,CAAC;YAElF,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;gBACzB,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,wBAAwB,CAAC,CAAC;gBACnD,OAAO;YACT,CAAC;YAED,YAAY;YACZ,IAAI,CAAC;gBACH,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,2BAA2B,CAAC,CAAC;gBACtD,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,aAAa,CAAC,QAAQ,EAAE;gBACxD,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,WAAW,EAAE,OAAO,CAAC,WAAuD;aAC7E,CAAC,CAAC;YAEH,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,mBAAmB,CAAC,CAAC;gBAC9C,OAAO;YACT,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,uBAAuB,QAAQ,KAAK,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;YAEtE,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACtB,OAAO,EAAE,IAAI;gBACb,QAAQ;gBACR,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE;aAC3C,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,gCAAgC,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;YACnE,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,uBAAuB,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,4BAA4B,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;QAC5E,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAErD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,qBAAqB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAE/D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,mBAAmB,CAAC,CAAC;gBAC9C,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAA4B;gBACpC,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,UAAU,EAAE,OAAO,CAAC,UAAU;aAC/B,CAAC;YACF,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtB,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YACnD,CAAC;YACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtB,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YACnD,CAAC;YACD,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,6BAA6B,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;YAChE,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,uBAAuB,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAErB;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;QACnE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;YACzC,MAAM,OAAO,GAAG,IAKH,CAAC;YAEd,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;gBACvB,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,sBAAsB,CAAC,CAAC;gBACjD,OAAO;YACT,CAAC;YAED,UAAU;YACV,IAAI,CAAC,mCAAmC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChE,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,yBAAyB,CAAC,CAAC;gBACpD,OAAO;YACT,CAAC;YAED,UAAU;YACV,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACzD,IAAI,QAAQ,EAAE,CAAC;gBACb,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,wBAAwB,CAAC,CAAC;gBACnD,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC;gBACvC,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,WAAW,EAAE,OAAO,CAAC,WAAuD;gBAC5E,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,SAAS,EAAE,OAAO,CAAC,SAAS;aAC7B,CAAC,CAAC;YAEH,MAAM,CAAC,IAAI,CAAC,uBAAuB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YAEvD,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACtB,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE;aAC3C,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,6BAA6B,KAAK,EAAE,CAAC,CAAC;YACnD,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,uBAAuB,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;AACjD,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,QAAgB,EAChB,OAAmC;IAEnC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,iCAAiC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC3E,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,kCAAkC,QAAQ,gCAAgC,KAAK,EAAE,CAAC,CAAC;IACjG,CAAC;IAED,OAAO,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACnD,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,QAAgB,EAChB,OAAmC;IAEnC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IAClC,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,aAAa,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC9E,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,uBAAuB,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IACrE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,cAAc,CAAC,QAAQ,EAAE,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,KAAK,UAAU,iCAAiC,CAC9C,QAAgB,EAChB,OAAmC;IAEnC,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IAC/C,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,OAAO,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,aAAa,EAAE,CAAC;QAC/D,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,IAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,aAAa,CAAC,eAAe,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,gCAAgC,QAAQ,uCAAuC,OAAO,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC,CAAC;QAC1H,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,UAAU,GAAG,8BAA8B,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClE,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,gCAAgC,QAAQ,0CAA0C,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;QACnH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,aAAa,CAAC,QAAQ,EAAE;YACxD,UAAU;YACV,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC,CAAC,CAAC;QACH,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,0BAA0B,QAAQ,KAAK,UAAU,EAAE,CAAC,CAAC;YACjE,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,kCAAkC,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,8BAA8B,CACrC,QAAgB,EAChB,IAAuB;IAEvB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;IACnF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,mBAAmB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,aAAa,CAAC,OAAe;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,IAAI,IAAI,IAAI,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC9B,aAAkC,EAClC,QAAgB,EAChB,QAAgB;IAEhB,IAAI,CAAC;QACH,IAAI,OAAO,aAAa,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;YACpD,OAAO,MAAM,aAAa,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,sCAAsC,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,uBAAuB,CACpC,aAAkC,EAClC,QAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,WAAW,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,oCAAoC,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;QACtE,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CACrB,QAAgB,EAChB,QAAgB,EAChB,GAAoB;IAEpB,MAAM,UAAU,GAAG,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACpD,OAAO;QACL,QAAQ;QACR,QAAQ;QACR,UAAU;QACV,WAAW,EAAE,OAAO;QACpB,UAAU,EAAE,YAAY,CAAC,QAAQ,CAAC;KACnC,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAgB;IAC3C,OAAO,IAAI,GAAG,CAAC,GAAG,kBAAkB,CAAC,QAAQ,CAAC,kBAAkB,EAAE,wBAAwB,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC3G,CAAC;AAED,SAAS,oBAAoB,CAAC,UAAkB;IAC9C,OAAO,IAAI,GAAG,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AAChF,CAAC;AAED,SAAS,wBAAwB;IAC/B,OAAO,mBAAmB,CACxB,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QACzC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;QACrC,wBAAwB,CACzB,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,KAAyB;IAChD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAW;IACtC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC;AACvC,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,IAAI,CAAC;QACH,OAAO,mBAAmB,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,OAAwB;IAClD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC5B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACnC,IAAI,IAAI,KAAK,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACrB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,CAAC,SAAS,CAAC,CAAC;gBACnB,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,SAAS,CAAC,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,QAAQ,CAAC,QAAwB,EAAE,MAAc,EAAE,IAAa;IACvE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;IACvD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,SAAS,CAAC,QAAwB,EAAE,MAAc,EAAE,OAAe;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;AACjD,CAAC","sourcesContent":["/**\n * WebID Profile API Handler\n *\n * 提供 WebID Profile 托管服务的 HTTP API\n *\n * GET /{username}/profile/card - 获取 WebID Profile (Turtle 格式)\n * POST /api/v1/identity/{username}/storage - 更新 storage 指针 (需认证)\n */\n\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { WebIdProfileRepository } from '../../identity/drizzle/WebIdProfileRepository';\nimport type { PodLookupRepository, PodLookupResult } from '../../identity/drizzle/PodLookupRepository';\n\nconst logger = getLoggerFor('WebIdProfileHandler');\n\nexport interface WebIdProfileHandlerOptions {\n profileRepo: WebIdProfileRepository;\n podLookupRepo?: PodLookupRepository;\n}\n\ninterface IdentityProfileResponse {\n username: string;\n webidUrl: string;\n storageUrl?: string;\n storageMode: 'cloud' | 'local' | 'custom';\n oidcIssuer?: string;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nexport function registerWebIdProfileRoutes(\n server: ApiServer,\n options: WebIdProfileHandlerOptions,\n): void {\n const { profileRepo } = options;\n\n /**\n * GET /{username}/profile/card\n *\n * 获取 WebID Profile (Turtle 格式)\n * 这是 Solid 标准的 WebID 端点\n */\n server.get('/:username/profile/card', async (_request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const profile = await resolveIdentityLookup(username, options);\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n // 返回 Turtle 格式\n const turtle = profileRepo.generateProfileTurtle(profile);\n\n response.statusCode = 200;\n response.setHeader('Content-Type', 'text/turtle');\n response.setHeader('Link', `<${profile.webidUrl}>; rel=\"describedby\"`);\n response.end(turtle);\n } catch (error) {\n logger.error(`Failed to get profile for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n }, { public: true });\n\n /**\n * POST /api/v1/identity/{username}/storage\n *\n * 更新 storage 指针\n * 用于 Local 节点更新其 storage URL\n *\n * Request body:\n * {\n * \"storageUrl\": \"https://alice.undefineds.xyz/\"\n * }\n */\n server.post('/api/v1/identity/:username/storage', async (request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const body = await readJsonBody(request);\n const payload = body as { storageUrl?: string; storageMode?: string } | undefined;\n\n if (!payload?.storageUrl) {\n sendError(response, 400, 'storageUrl is required');\n return;\n }\n\n // 验证 URL 格式\n try {\n new URL(payload.storageUrl);\n } catch {\n sendError(response, 400, 'Invalid storageUrl format');\n return;\n }\n\n const profile = await profileRepo.updateStorage(username, {\n storageUrl: payload.storageUrl,\n storageMode: payload.storageMode as 'cloud' | 'local' | 'custom' | undefined,\n });\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n logger.info(`Updated storage for ${username}: ${payload.storageUrl}`);\n\n sendJson(response, 200, {\n success: true,\n username,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n updatedAt: profile.updatedAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to update storage for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * GET /api/v1/identity/{username}\n *\n * 获取 WebID Profile 信息 (JSON 格式)\n */\n server.get('/api/v1/identity/:username', async (_request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const profile = await resolveIdentityLookup(username, options);\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n const body: Record<string, unknown> = {\n username: profile.username,\n webidUrl: profile.webidUrl,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n oidcIssuer: profile.oidcIssuer,\n };\n if (profile.createdAt) {\n body.createdAt = profile.createdAt.toISOString();\n }\n if (profile.updatedAt) {\n body.updatedAt = profile.updatedAt.toISOString();\n }\n sendJson(response, 200, body);\n } catch (error) {\n logger.error(`Failed to get profile for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n }, { public: true });\n\n /**\n * POST /api/v1/identity\n *\n * 创建 WebID Profile\n *\n * Request body:\n * {\n * \"username\": \"alice\",\n * \"storageMode\": \"local\", // optional, default: \"cloud\"\n * \"storageUrl\": \"https://alice.undefineds.xyz/\" // optional\n * }\n */\n server.post('/api/v1/identity', async (request, response, _params) => {\n try {\n const body = await readJsonBody(request);\n const payload = body as {\n username?: string;\n storageMode?: string;\n storageUrl?: string;\n accountId?: string;\n } | undefined;\n\n if (!payload?.username) {\n sendError(response, 400, 'username is required');\n return;\n }\n\n // 验证用户名格式\n if (!/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/.test(payload.username)) {\n sendError(response, 400, 'Invalid username format');\n return;\n }\n\n // 检查是否已存在\n const existing = await profileRepo.get(payload.username);\n if (existing) {\n sendError(response, 409, 'Username already taken');\n return;\n }\n\n const profile = await profileRepo.create({\n username: payload.username,\n storageMode: payload.storageMode as 'cloud' | 'local' | 'custom' | undefined,\n storageUrl: payload.storageUrl,\n accountId: payload.accountId,\n });\n\n logger.info(`Created profile for ${payload.username}`);\n\n sendJson(response, 201, {\n success: true,\n username: profile.username,\n webidUrl: profile.webidUrl,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n createdAt: profile.createdAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to create profile: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n logger.info('WebID Profile routes registered');\n}\n\nasync function resolveIdentityLookup(\n username: string,\n options: WebIdProfileHandlerOptions,\n): Promise<IdentityProfileResponse | null> {\n try {\n const profile = await resolveProfileWithStorageBackfill(username, options);\n if (profile) {\n return profile;\n }\n } catch (error) {\n logger.warn(`Profile lookup unavailable for ${username}, falling back to Pod index: ${error}`);\n }\n\n return resolveProfileFromPods(username, options);\n}\n\nasync function resolveProfileFromPods(\n username: string,\n options: WebIdProfileHandlerOptions,\n): Promise<IdentityProfileResponse | null> {\n const { podLookupRepo } = options;\n if (!podLookupRepo) {\n return null;\n }\n\n const webidUrl = buildHostedWebIdUrl(username);\n const webIdMatch = await tryFindPodByWebId(podLookupRepo, webidUrl, username);\n if (webIdMatch) {\n return profileFromPod(username, webidUrl, webIdMatch);\n }\n\n const match = await tryFindPodByStorageSlug(podLookupRepo, username);\n if (!match) {\n return null;\n }\n\n return profileFromPod(username, buildStorageWebIdUrl(match.baseUrl), match);\n}\n\nasync function resolveProfileWithStorageBackfill(\n username: string,\n options: WebIdProfileHandlerOptions,\n) {\n const { profileRepo, podLookupRepo } = options;\n const profile = await profileRepo.get(username);\n if (!profile) {\n return null;\n }\n\n if (profile.storageUrl || !profile.accountId || !podLookupRepo) {\n return profile;\n }\n\n let pods: PodLookupResult[];\n try {\n pods = await podLookupRepo.listByAccountId(profile.accountId);\n } catch (error) {\n logger.warn(`Skipped storage backfill for ${username}: pod index unavailable for account ${profile.accountId}: ${error}`);\n return profile;\n }\n const storageUrl = selectStorageBackfillCandidate(username, pods);\n if (!storageUrl) {\n logger.warn(`Skipped storage backfill for ${username}: no unambiguous pod found for account ${profile.accountId}`);\n return profile;\n }\n\n try {\n const updated = await profileRepo.updateStorage(username, {\n storageUrl,\n storageMode: profile.storageMode,\n });\n if (updated) {\n logger.info(`Backfilled storage for ${username}: ${storageUrl}`);\n return updated;\n }\n } catch (error) {\n logger.warn(`Failed to backfill storage for ${username}: ${error}`);\n }\n\n return profile;\n}\n\nfunction selectStorageBackfillCandidate(\n username: string,\n pods: PodLookupResult[],\n): string | null {\n if (pods.length === 0) {\n return null;\n }\n\n const exactMatches = pods.filter((pod) => derivePodSlug(pod.baseUrl) === username);\n if (exactMatches.length === 1) {\n return ensureTrailingSlash(exactMatches[0].baseUrl);\n }\n\n if (exactMatches.length > 1) {\n return null;\n }\n\n if (pods.length === 1) {\n return ensureTrailingSlash(pods[0].baseUrl);\n }\n\n return null;\n}\n\nfunction derivePodSlug(baseUrl: string): string | null {\n try {\n const parsed = new URL(baseUrl);\n const [slug] = parsed.pathname.split('/').filter(Boolean);\n return slug || null;\n } catch {\n return null;\n }\n}\n\nasync function tryFindPodByWebId(\n podLookupRepo: PodLookupRepository,\n webidUrl: string,\n username: string,\n): Promise<PodLookupResult | undefined> {\n try {\n if (typeof podLookupRepo.findByWebId === 'function') {\n return await podLookupRepo.findByWebId(webidUrl);\n }\n } catch (error) {\n logger.warn(`WebID index lookup unavailable for ${username}: ${error}`);\n }\n return undefined;\n}\n\nasync function tryFindPodByStorageSlug(\n podLookupRepo: PodLookupRepository,\n username: string,\n): Promise<PodLookupResult | undefined> {\n try {\n const pods = await podLookupRepo.listAllPods();\n return pods.find((pod) => derivePodSlug(pod.baseUrl) === username);\n } catch (error) {\n logger.warn(`Pod index lookup unavailable for ${username}: ${error}`);\n return undefined;\n }\n}\n\nfunction profileFromPod(\n username: string,\n webidUrl: string,\n pod: PodLookupResult,\n): IdentityProfileResponse {\n const storageUrl = ensureTrailingSlash(pod.baseUrl);\n return {\n username,\n webidUrl,\n storageUrl,\n storageMode: 'cloud',\n oidcIssuer: deriveOrigin(webidUrl),\n };\n}\n\nfunction buildHostedWebIdUrl(username: string): string {\n return new URL(`${encodeURIComponent(username)}/profile/card#me`, getHostedIdentityBaseUrl()).toString();\n}\n\nfunction buildStorageWebIdUrl(storageUrl: string): string {\n return new URL('profile/card#me', ensureTrailingSlash(storageUrl)).toString();\n}\n\nfunction getHostedIdentityBaseUrl(): string {\n return ensureTrailingSlash(\n absoluteHttpUrl(process.env.CSS_BASE_URL) ??\n absoluteHttpUrl(process.env.BASE_URL) ??\n 'http://localhost:3000/',\n );\n}\n\nfunction absoluteHttpUrl(value: string | undefined): string | undefined {\n if (!value) {\n return undefined;\n }\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:' ? url.toString() : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction ensureTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, '') + '/';\n}\n\nfunction deriveOrigin(url: string): string | undefined {\n try {\n return ensureTrailingSlash(new URL(url).origin);\n } catch {\n return undefined;\n }\n}\n\nasync function readJsonBody(request: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n\nfunction sendError(response: ServerResponse, status: number, message: string): void {\n sendJson(response, status, { error: message });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"WebIdProfileHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/WebIdProfileHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WebID Profile API Handler\n *\n * 提供 WebID Profile 托管服务的 HTTP API\n *\n * GET /{username}/profile/card - 获取 WebID Profile (Turtle 格式)\n * POST /api/v1/identity/{username}/storage - 更新 storage 指针 (需认证)\n */\n\nimport type { ServerResponse, IncomingMessage } from 'node:http';\nimport { getLoggerFor } from 'global-logger-factory';\nimport type { ApiServer } from '../ApiServer';\nimport type { WebIdProfileRepository } from '../../identity/drizzle/WebIdProfileRepository';\nimport type { PodLookupRepository, PodLookupResult } from '../../identity/drizzle/PodLookupRepository';\n\nconst logger = getLoggerFor('WebIdProfileHandler');\n\nexport interface WebIdProfileHandlerOptions {\n profileRepo: WebIdProfileRepository;\n podLookupRepo?: PodLookupRepository;\n}\n\ninterface IdentityProfileResponse {\n username: string;\n webidUrl: string;\n storageUrl?: string;\n storageMode: 'cloud' | 'local' | 'custom';\n oidcIssuer?: string;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nexport function registerWebIdProfileRoutes(\n server: ApiServer,\n options: WebIdProfileHandlerOptions,\n): void {\n const { profileRepo } = options;\n\n /**\n * GET /{username}/profile/card\n *\n * 获取 WebID Profile (Turtle 格式)\n * 这是 Solid 标准的 WebID 端点\n */\n server.get('/:username/profile/card', async (request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const profile = await resolveIdentityLookup(username, options, request);\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n // 返回 Turtle 格式\n const turtle = profileRepo.generateProfileTurtle(profile);\n\n response.statusCode = 200;\n response.setHeader('Content-Type', 'text/turtle');\n response.setHeader('Link', `<${profile.webidUrl}>; rel=\"describedby\"`);\n response.end(turtle);\n } catch (error) {\n logger.error(`Failed to get profile for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n }, { public: true });\n\n /**\n * POST /api/v1/identity/{username}/storage\n *\n * 更新 storage 指针\n * 用于 Local 节点更新其 storage URL\n *\n * Request body:\n * {\n * \"storageUrl\": \"https://alice.undefineds.xyz/\"\n * }\n */\n server.post('/api/v1/identity/:username/storage', async (request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const body = await readJsonBody(request);\n const payload = body as { storageUrl?: string; storageMode?: string } | undefined;\n\n if (!payload?.storageUrl) {\n sendError(response, 400, 'storageUrl is required');\n return;\n }\n\n // 验证 URL 格式\n try {\n new URL(payload.storageUrl);\n } catch {\n sendError(response, 400, 'Invalid storageUrl format');\n return;\n }\n\n const profile = await profileRepo.updateStorage(username, {\n storageUrl: payload.storageUrl,\n storageMode: payload.storageMode as 'cloud' | 'local' | 'custom' | undefined,\n });\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n logger.info(`Updated storage for ${username}: ${payload.storageUrl}`);\n\n sendJson(response, 200, {\n success: true,\n username,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n updatedAt: profile.updatedAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to update storage for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n /**\n * GET /api/v1/identity/{username}\n *\n * 获取 WebID Profile 信息 (JSON 格式)\n */\n server.get('/api/v1/identity/:username', async (request, response, params) => {\n const username = decodeURIComponent(params.username);\n\n try {\n const profile = await resolveIdentityLookup(username, options, request);\n\n if (!profile) {\n sendError(response, 404, 'Profile not found');\n return;\n }\n\n const body: Record<string, unknown> = {\n username: profile.username,\n webidUrl: profile.webidUrl,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n oidcIssuer: profile.oidcIssuer,\n };\n if (profile.createdAt) {\n body.createdAt = profile.createdAt.toISOString();\n }\n if (profile.updatedAt) {\n body.updatedAt = profile.updatedAt.toISOString();\n }\n sendJson(response, 200, body);\n } catch (error) {\n logger.error(`Failed to get profile for ${username}: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n }, { public: true });\n\n /**\n * POST /api/v1/identity\n *\n * 创建 WebID Profile\n *\n * Request body:\n * {\n * \"username\": \"alice\",\n * \"storageMode\": \"local\", // optional, default: \"cloud\"\n * \"storageUrl\": \"https://alice.undefineds.xyz/\" // optional\n * }\n */\n server.post('/api/v1/identity', async (request, response, _params) => {\n try {\n const body = await readJsonBody(request);\n const payload = body as {\n username?: string;\n storageMode?: string;\n storageUrl?: string;\n accountId?: string;\n } | undefined;\n\n if (!payload?.username) {\n sendError(response, 400, 'username is required');\n return;\n }\n\n // 验证用户名格式\n if (!/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/.test(payload.username)) {\n sendError(response, 400, 'Invalid username format');\n return;\n }\n\n // 检查是否已存在\n const existing = await profileRepo.get(payload.username);\n if (existing) {\n sendError(response, 409, 'Username already taken');\n return;\n }\n\n const profile = await profileRepo.create({\n username: payload.username,\n storageMode: payload.storageMode as 'cloud' | 'local' | 'custom' | undefined,\n storageUrl: payload.storageUrl,\n accountId: payload.accountId,\n });\n\n logger.info(`Created profile for ${payload.username}`);\n\n sendJson(response, 201, {\n success: true,\n username: profile.username,\n webidUrl: profile.webidUrl,\n storageUrl: profile.storageUrl,\n storageMode: profile.storageMode,\n createdAt: profile.createdAt.toISOString(),\n });\n } catch (error) {\n logger.error(`Failed to create profile: ${error}`);\n sendError(response, 500, 'Internal server error');\n }\n });\n\n logger.info('WebID Profile routes registered');\n}\n\nasync function resolveIdentityLookup(\n username: string,\n options: WebIdProfileHandlerOptions,\n request?: IncomingMessage,\n): Promise<IdentityProfileResponse | null> {\n try {\n const profile = await resolveProfileWithStorageBackfill(username, options);\n if (profile) {\n return profile;\n }\n } catch (error) {\n logger.warn(`Profile lookup unavailable for ${username}, falling back to Pod index: ${error}`);\n }\n\n return resolveProfileFromPods(username, options, request);\n}\n\nasync function resolveProfileFromPods(\n username: string,\n options: WebIdProfileHandlerOptions,\n request?: IncomingMessage,\n): Promise<IdentityProfileResponse | null> {\n const { podLookupRepo } = options;\n const identityBaseUrl = getHostedIdentityBaseUrl(request);\n\n if (podLookupRepo) {\n const webidUrl = buildHostedWebIdUrl(username, identityBaseUrl);\n const webIdMatch = await tryFindPodByWebId(podLookupRepo, webidUrl, username);\n if (webIdMatch) {\n return profileFromPod(username, webidUrl, webIdMatch);\n }\n\n const match = await tryFindPodByStorageSlug(podLookupRepo, username);\n if (match) {\n return profileFromPod(username, buildStorageWebIdUrl(match.baseUrl), match);\n }\n }\n\n const hostedStorageUrl = new URL(`${encodeURIComponent(username)}/`, identityBaseUrl).toString();\n if (await probeHostedStorageRoot(hostedStorageUrl, username)) {\n return {\n username,\n webidUrl: buildHostedWebIdUrl(username, identityBaseUrl),\n storageUrl: hostedStorageUrl,\n storageMode: 'cloud',\n oidcIssuer: deriveOrigin(identityBaseUrl),\n };\n }\n\n return null;\n}\n\nasync function resolveProfileWithStorageBackfill(\n username: string,\n options: WebIdProfileHandlerOptions,\n) {\n const { profileRepo, podLookupRepo } = options;\n const profile = await profileRepo.get(username);\n if (!profile) {\n return null;\n }\n\n if (profile.storageUrl || !profile.accountId || !podLookupRepo) {\n return profile;\n }\n\n let pods: PodLookupResult[];\n try {\n pods = await podLookupRepo.listByAccountId(profile.accountId);\n } catch (error) {\n logger.warn(`Skipped storage backfill for ${username}: pod index unavailable for account ${profile.accountId}: ${error}`);\n return profile;\n }\n const storageUrl = selectStorageBackfillCandidate(username, pods);\n if (!storageUrl) {\n logger.warn(`Skipped storage backfill for ${username}: no unambiguous pod found for account ${profile.accountId}`);\n return profile;\n }\n\n try {\n const updated = await profileRepo.updateStorage(username, {\n storageUrl,\n storageMode: profile.storageMode,\n });\n if (updated) {\n logger.info(`Backfilled storage for ${username}: ${storageUrl}`);\n return updated;\n }\n } catch (error) {\n logger.warn(`Failed to backfill storage for ${username}: ${error}`);\n }\n\n return profile;\n}\n\nfunction selectStorageBackfillCandidate(\n username: string,\n pods: PodLookupResult[],\n): string | null {\n if (pods.length === 0) {\n return null;\n }\n\n const exactMatches = pods.filter((pod) => derivePodSlug(pod.baseUrl) === username);\n if (exactMatches.length === 1) {\n return ensureTrailingSlash(exactMatches[0].baseUrl);\n }\n\n if (exactMatches.length > 1) {\n return null;\n }\n\n if (pods.length === 1) {\n return ensureTrailingSlash(pods[0].baseUrl);\n }\n\n return null;\n}\n\nfunction derivePodSlug(baseUrl: string): string | null {\n try {\n const parsed = new URL(baseUrl);\n const [slug] = parsed.pathname.split('/').filter(Boolean);\n return slug || null;\n } catch {\n return null;\n }\n}\n\nasync function tryFindPodByWebId(\n podLookupRepo: PodLookupRepository,\n webidUrl: string,\n username: string,\n): Promise<PodLookupResult | undefined> {\n try {\n if (typeof podLookupRepo.findByWebId === 'function') {\n return await podLookupRepo.findByWebId(webidUrl);\n }\n } catch (error) {\n logger.warn(`WebID index lookup unavailable for ${username}: ${error}`);\n }\n return undefined;\n}\n\nasync function tryFindPodByStorageSlug(\n podLookupRepo: PodLookupRepository,\n username: string,\n): Promise<PodLookupResult | undefined> {\n try {\n const pods = await podLookupRepo.listAllPods();\n return pods.find((pod) => derivePodSlug(pod.baseUrl) === username);\n } catch (error) {\n logger.warn(`Pod index lookup unavailable for ${username}: ${error}`);\n return undefined;\n }\n}\n\nfunction profileFromPod(\n username: string,\n webidUrl: string,\n pod: PodLookupResult,\n): IdentityProfileResponse {\n const storageUrl = ensureTrailingSlash(pod.baseUrl);\n return {\n username,\n webidUrl,\n storageUrl,\n storageMode: 'cloud',\n oidcIssuer: deriveOrigin(webidUrl),\n };\n}\n\nfunction buildHostedWebIdUrl(username: string, identityBaseUrl = getHostedIdentityBaseUrl()): string {\n return new URL(`${encodeURIComponent(username)}/profile/card#me`, identityBaseUrl).toString();\n}\n\nfunction buildStorageWebIdUrl(storageUrl: string): string {\n return new URL('profile/card#me', ensureTrailingSlash(storageUrl)).toString();\n}\n\nfunction getHostedIdentityBaseUrl(request?: IncomingMessage): string {\n return ensureTrailingSlash(\n requestOrigin(request) ??\n absoluteHttpUrl(process.env.CSS_BASE_URL) ??\n absoluteHttpUrl(process.env.BASE_URL) ??\n 'http://localhost:3000/',\n );\n}\n\nfunction absoluteHttpUrl(value: string | undefined): string | undefined {\n if (!value) {\n return undefined;\n }\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:' ? url.toString() : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction requestOrigin(request: IncomingMessage | undefined): string | undefined {\n if (!request?.headers) {\n return undefined;\n }\n const host = firstHeader(request.headers['x-forwarded-host']) ?? firstHeader(request.headers.host);\n if (!host) {\n return undefined;\n }\n const proto = firstHeader(request.headers['x-forwarded-proto']) ?? 'https';\n return absoluteHttpUrl(`${proto}://${host}`);\n}\n\nfunction firstHeader(value: string | string[] | undefined): string | undefined {\n if (Array.isArray(value)) {\n return value[0];\n }\n return value;\n}\n\nasync function probeHostedStorageRoot(storageUrl: string, username: string): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 2_000);\n try {\n const response = await fetch(storageUrl, {\n method: 'HEAD',\n signal: controller.signal,\n });\n if ((response.status >= 200 && response.status < 400) || response.status === 401 || response.status === 403) {\n logger.info(`Resolved hosted WebID profile for ${username} from existing storage root: ${storageUrl}`);\n return true;\n }\n return false;\n } catch (error) {\n logger.warn(`Hosted storage root probe unavailable for ${username}: ${error}`);\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n\nfunction ensureTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, '') + '/';\n}\n\nfunction deriveOrigin(url: string): string | undefined {\n try {\n return ensureTrailingSlash(new URL(url).origin);\n } catch {\n return undefined;\n }\n}\n\nasync function readJsonBody(request: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n\nfunction sendError(response: ServerResponse, status: number, message: string): void {\n sendJson(response, status, { error: message });\n}\n"]}
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"undefineds:dist/pods/ReservedSuffixIdentifierGenerator.jsonld",
|
|
37
37
|
"undefineds:dist/identity/drizzle/DrizzleIndexedStorage.jsonld",
|
|
38
38
|
"undefineds:dist/identity/drizzle/WebIdProfileRepository.jsonld",
|
|
39
|
+
"undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld",
|
|
39
40
|
"undefineds:dist/storage/keyvalue/PostgresKeyValueStorage.jsonld",
|
|
40
41
|
"undefineds:dist/storage/keyvalue/RedisKeyValueStorage.jsonld",
|
|
41
42
|
"undefineds:dist/storage/keyvalue/SqliteKeyValueStorage.jsonld",
|
|
@@ -799,6 +799,36 @@
|
|
|
799
799
|
}
|
|
800
800
|
}
|
|
801
801
|
},
|
|
802
|
+
"ValidatingIdentityProviderHttpHandler": {
|
|
803
|
+
"@id": "undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld#ValidatingIdentityProviderHttpHandler",
|
|
804
|
+
"@prefix": true,
|
|
805
|
+
"@context": {
|
|
806
|
+
"args_providerFactory": {
|
|
807
|
+
"@id": "undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld#ValidatingIdentityProviderHttpHandler_args_providerFactory"
|
|
808
|
+
},
|
|
809
|
+
"args_cookieStore": {
|
|
810
|
+
"@id": "undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld#ValidatingIdentityProviderHttpHandler_args_cookieStore"
|
|
811
|
+
},
|
|
812
|
+
"args_handler": {
|
|
813
|
+
"@id": "undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld#ValidatingIdentityProviderHttpHandler_args_handler"
|
|
814
|
+
},
|
|
815
|
+
"args_accountStorage": {
|
|
816
|
+
"@id": "undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld#ValidatingIdentityProviderHttpHandler_args_accountStorage"
|
|
817
|
+
},
|
|
818
|
+
"providerFactory": {
|
|
819
|
+
"@id": "undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld#ValidatingIdentityProviderHttpHandler_args_providerFactory"
|
|
820
|
+
},
|
|
821
|
+
"cookieStore": {
|
|
822
|
+
"@id": "undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld#ValidatingIdentityProviderHttpHandler_args_cookieStore"
|
|
823
|
+
},
|
|
824
|
+
"handler": {
|
|
825
|
+
"@id": "undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld#ValidatingIdentityProviderHttpHandler_args_handler"
|
|
826
|
+
},
|
|
827
|
+
"accountStorage": {
|
|
828
|
+
"@id": "undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld#ValidatingIdentityProviderHttpHandler_args_accountStorage"
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
},
|
|
802
832
|
"PostgresKeyValueStorage": {
|
|
803
833
|
"@id": "undefineds:dist/storage/keyvalue/PostgresKeyValueStorage.jsonld#PostgresKeyValueStorage",
|
|
804
834
|
"@prefix": true,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { OperationHttpHandler, type InteractionHandler, type OperationHttpHandlerInput, type ProviderFactory, type ResponseDescription } from '@solid/community-server';
|
|
2
|
+
import type { CookieStore } from '@solid/community-server';
|
|
3
|
+
interface AccountExistenceStorage {
|
|
4
|
+
has: (type: string, id: string) => Promise<boolean>;
|
|
5
|
+
}
|
|
6
|
+
export interface ValidatingIdentityProviderHttpHandlerArgs {
|
|
7
|
+
/**
|
|
8
|
+
* Used to generate the OIDC provider.
|
|
9
|
+
*/
|
|
10
|
+
providerFactory: ProviderFactory;
|
|
11
|
+
/**
|
|
12
|
+
* Used to determine the account of the requesting agent.
|
|
13
|
+
*/
|
|
14
|
+
cookieStore: CookieStore;
|
|
15
|
+
/**
|
|
16
|
+
* Handles the requests.
|
|
17
|
+
*/
|
|
18
|
+
handler: InteractionHandler;
|
|
19
|
+
/**
|
|
20
|
+
* Storage backing CSS account state.
|
|
21
|
+
*/
|
|
22
|
+
accountStorage: AccountExistenceStorage;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* CSS-compatible IdP operation handler that drops stale account cookies.
|
|
26
|
+
*
|
|
27
|
+
* CSS trusts the account id stored in the cookie. In a clustered deployment where
|
|
28
|
+
* account storage can be reset independently from browser cookies, that can leave
|
|
29
|
+
* users stuck in a phantom logged-in state during registration or login.
|
|
30
|
+
*/
|
|
31
|
+
export declare class ValidatingIdentityProviderHttpHandler extends OperationHttpHandler {
|
|
32
|
+
protected readonly logger: import("global-logger-factory").Logger<unknown>;
|
|
33
|
+
private readonly providerFactory;
|
|
34
|
+
private readonly cookieStore;
|
|
35
|
+
private readonly handler;
|
|
36
|
+
private readonly accountStorage;
|
|
37
|
+
constructor(args: ValidatingIdentityProviderHttpHandlerArgs);
|
|
38
|
+
handle({ operation, request, response }: OperationHttpHandlerInput): Promise<ResponseDescription>;
|
|
39
|
+
private findAccountCookies;
|
|
40
|
+
private findAuthorizationAccountCookie;
|
|
41
|
+
private findBrowserAccountCookie;
|
|
42
|
+
private normalizeAccountCookie;
|
|
43
|
+
private findValidAccount;
|
|
44
|
+
}
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ValidatingIdentityProviderHttpHandler = void 0;
|
|
4
|
+
const global_logger_factory_1 = require("global-logger-factory");
|
|
5
|
+
const community_server_1 = require("@solid/community-server");
|
|
6
|
+
const ACCOUNT_TYPE = 'account';
|
|
7
|
+
const ACCOUNT_COOKIE_NAME = 'css-account';
|
|
8
|
+
const ACCOUNT_TOKEN_AUTHORIZATION_SCHEME = 'CSS-Account-Token ';
|
|
9
|
+
/**
|
|
10
|
+
* CSS-compatible IdP operation handler that drops stale account cookies.
|
|
11
|
+
*
|
|
12
|
+
* CSS trusts the account id stored in the cookie. In a clustered deployment where
|
|
13
|
+
* account storage can be reset independently from browser cookies, that can leave
|
|
14
|
+
* users stuck in a phantom logged-in state during registration or login.
|
|
15
|
+
*/
|
|
16
|
+
class ValidatingIdentityProviderHttpHandler extends community_server_1.OperationHttpHandler {
|
|
17
|
+
constructor(args) {
|
|
18
|
+
super();
|
|
19
|
+
this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
|
|
20
|
+
this.providerFactory = args.providerFactory;
|
|
21
|
+
this.cookieStore = args.cookieStore;
|
|
22
|
+
this.handler = args.handler;
|
|
23
|
+
this.accountStorage = args.accountStorage;
|
|
24
|
+
}
|
|
25
|
+
async handle({ operation, request, response }) {
|
|
26
|
+
let oidcInteraction;
|
|
27
|
+
try {
|
|
28
|
+
const provider = await this.providerFactory.getProvider();
|
|
29
|
+
oidcInteraction = await provider.interactionDetails(request, response);
|
|
30
|
+
this.logger.debug('Found an active OIDC interaction.');
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
this.logger.debug(`No active OIDC interaction found: ${(0, community_server_1.createErrorMessage)(error)}`);
|
|
34
|
+
}
|
|
35
|
+
const browserCookie = this.findBrowserAccountCookie(request);
|
|
36
|
+
const authorizationCookie = this.findAuthorizationAccountCookie(request);
|
|
37
|
+
const cookies = this.findAccountCookies(operation, authorizationCookie, browserCookie);
|
|
38
|
+
const { accountId, selectedCookie, expiredCookie } = await this.findValidAccount(cookies, browserCookie);
|
|
39
|
+
const normalizedOperation = this.normalizeAccountCookie(operation, selectedCookie);
|
|
40
|
+
const representation = await this.handler.handleSafe({ operation: normalizedOperation, oidcInteraction, accountId });
|
|
41
|
+
if (expiredCookie && !representation.metadata?.has(community_server_1.SOLID_HTTP.terms.accountCookie)) {
|
|
42
|
+
const metadata = new community_server_1.RepresentationMetadata(representation.metadata);
|
|
43
|
+
metadata.set(community_server_1.SOLID_HTTP.terms.accountCookie, expiredCookie);
|
|
44
|
+
metadata.set(community_server_1.SOLID_HTTP.terms.accountCookieExpiration, new Date(0).toISOString());
|
|
45
|
+
representation.metadata = metadata;
|
|
46
|
+
}
|
|
47
|
+
return new community_server_1.OkResponseDescription(representation.metadata, representation.data);
|
|
48
|
+
}
|
|
49
|
+
findAccountCookies(operation, authorizationCookie, browserCookie) {
|
|
50
|
+
const metadataCookies = operation.body.metadata
|
|
51
|
+
.getAll(community_server_1.SOLID_HTTP.terms.accountCookie)
|
|
52
|
+
.map((term) => term.value)
|
|
53
|
+
.filter((value) => Boolean(value));
|
|
54
|
+
return Array.from(new Set([
|
|
55
|
+
authorizationCookie,
|
|
56
|
+
...metadataCookies,
|
|
57
|
+
browserCookie,
|
|
58
|
+
].filter((value) => Boolean(value))));
|
|
59
|
+
}
|
|
60
|
+
findAuthorizationAccountCookie(request) {
|
|
61
|
+
const authorization = request.headers.authorization;
|
|
62
|
+
if (!authorization?.toLowerCase().startsWith(ACCOUNT_TOKEN_AUTHORIZATION_SCHEME.toLowerCase())) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const value = authorization.slice(ACCOUNT_TOKEN_AUTHORIZATION_SCHEME.length).trim();
|
|
66
|
+
return value || undefined;
|
|
67
|
+
}
|
|
68
|
+
findBrowserAccountCookie(request) {
|
|
69
|
+
const cookieHeader = request.headers.cookie;
|
|
70
|
+
if (!cookieHeader) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
for (const cookie of cookieHeader.split(';')) {
|
|
74
|
+
const separator = cookie.indexOf('=');
|
|
75
|
+
if (separator === -1) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const name = cookie.slice(0, separator).trim();
|
|
79
|
+
if (name !== ACCOUNT_COOKIE_NAME) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const value = cookie.slice(separator + 1).trim();
|
|
83
|
+
return value || undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
normalizeAccountCookie(operation, selectedCookie) {
|
|
87
|
+
const metadata = new community_server_1.RepresentationMetadata(operation.body.metadata);
|
|
88
|
+
metadata.removeAll(community_server_1.SOLID_HTTP.terms.accountCookie);
|
|
89
|
+
if (selectedCookie) {
|
|
90
|
+
metadata.add(community_server_1.SOLID_HTTP.terms.accountCookie, selectedCookie);
|
|
91
|
+
}
|
|
92
|
+
const body = Object.assign(Object.create(Object.getPrototypeOf(operation.body)), operation.body, { metadata });
|
|
93
|
+
return {
|
|
94
|
+
...operation,
|
|
95
|
+
body,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async findValidAccount(cookies, browserCookie) {
|
|
99
|
+
if (cookies.length === 0) {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
let expiredCookie;
|
|
103
|
+
let selectedCookie;
|
|
104
|
+
let selectedAccountId;
|
|
105
|
+
for (const cookie of cookies) {
|
|
106
|
+
const accountId = await this.cookieStore.get(cookie);
|
|
107
|
+
if (!accountId) {
|
|
108
|
+
if (cookie === browserCookie) {
|
|
109
|
+
expiredCookie ??= cookie;
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const accountExists = await this.accountStorage.has(ACCOUNT_TYPE, accountId);
|
|
114
|
+
if (accountExists) {
|
|
115
|
+
selectedCookie ??= cookie;
|
|
116
|
+
selectedAccountId ??= accountId;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
await this.cookieStore.delete(cookie);
|
|
120
|
+
if (cookie === browserCookie) {
|
|
121
|
+
expiredCookie ??= cookie;
|
|
122
|
+
}
|
|
123
|
+
this.logger.warn(`Deleted stale account cookie for missing account ${accountId}.`);
|
|
124
|
+
}
|
|
125
|
+
return { accountId: selectedAccountId, selectedCookie, expiredCookie };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.ValidatingIdentityProviderHttpHandler = ValidatingIdentityProviderHttpHandler;
|
|
129
|
+
//# sourceMappingURL=ValidatingIdentityProviderHttpHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ValidatingIdentityProviderHttpHandler.js","sourceRoot":"","sources":["../../src/identity/ValidatingIdentityProviderHttpHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import { getLoggerFor } from 'global-logger-factory';\nimport {\n OkResponseDescription,\n OperationHttpHandler,\n RepresentationMetadata,\n SOLID_HTTP,\n createErrorMessage,\n type InteractionHandler,\n type OperationHttpHandlerInput,\n type ProviderFactory,\n type ResponseDescription,\n} from '@solid/community-server';\nimport type { CookieStore } from '@solid/community-server';\n\nconst ACCOUNT_TYPE = 'account';\nconst ACCOUNT_COOKIE_NAME = 'css-account';\nconst ACCOUNT_TOKEN_AUTHORIZATION_SCHEME = 'CSS-Account-Token ';\n\ninterface AccountExistenceStorage {\n has: (type: string, id: string) => Promise<boolean>;\n}\n\nexport interface ValidatingIdentityProviderHttpHandlerArgs {\n /**\n * Used to generate the OIDC provider.\n */\n providerFactory: ProviderFactory;\n /**\n * Used to determine the account of the requesting agent.\n */\n cookieStore: CookieStore;\n /**\n * Handles the requests.\n */\n handler: InteractionHandler;\n /**\n * Storage backing CSS account state.\n */\n accountStorage: AccountExistenceStorage;\n}\n\n/**\n * CSS-compatible IdP operation handler that drops stale account cookies.\n *\n * CSS trusts the account id stored in the cookie. In a clustered deployment where\n * account storage can be reset independently from browser cookies, that can leave\n * users stuck in a phantom logged-in state during registration or login.\n */\nexport class ValidatingIdentityProviderHttpHandler extends OperationHttpHandler {\n protected readonly logger = getLoggerFor(this);\n private readonly providerFactory: ProviderFactory;\n private readonly cookieStore: CookieStore;\n private readonly handler: InteractionHandler;\n private readonly accountStorage: AccountExistenceStorage;\n\n public constructor(args: ValidatingIdentityProviderHttpHandlerArgs) {\n super();\n this.providerFactory = args.providerFactory;\n this.cookieStore = args.cookieStore;\n this.handler = args.handler;\n this.accountStorage = args.accountStorage;\n }\n\n public override async handle({ operation, request, response }: OperationHttpHandlerInput): Promise<ResponseDescription> {\n let oidcInteraction;\n try {\n const provider = await this.providerFactory.getProvider();\n oidcInteraction = await provider.interactionDetails(request, response);\n this.logger.debug('Found an active OIDC interaction.');\n } catch (error: unknown) {\n this.logger.debug(`No active OIDC interaction found: ${createErrorMessage(error)}`);\n }\n\n const browserCookie = this.findBrowserAccountCookie(request);\n const authorizationCookie = this.findAuthorizationAccountCookie(request);\n const cookies = this.findAccountCookies(operation, authorizationCookie, browserCookie);\n const { accountId, selectedCookie, expiredCookie } = await this.findValidAccount(cookies, browserCookie);\n const normalizedOperation = this.normalizeAccountCookie(operation, selectedCookie);\n const representation = await this.handler.handleSafe({ operation: normalizedOperation, oidcInteraction, accountId });\n\n if (expiredCookie && !representation.metadata?.has(SOLID_HTTP.terms.accountCookie)) {\n const metadata = new RepresentationMetadata(representation.metadata);\n metadata.set(SOLID_HTTP.terms.accountCookie, expiredCookie);\n metadata.set(SOLID_HTTP.terms.accountCookieExpiration, new Date(0).toISOString());\n representation.metadata = metadata;\n }\n\n return new OkResponseDescription(representation.metadata, representation.data);\n }\n\n private findAccountCookies(\n operation: OperationHttpHandlerInput['operation'],\n authorizationCookie: string | undefined,\n browserCookie: string | undefined,\n ): string[] {\n const metadataCookies = operation.body.metadata\n .getAll(SOLID_HTTP.terms.accountCookie)\n .map((term) => term.value)\n .filter((value): value is string => Boolean(value));\n\n return Array.from(new Set(\n [\n authorizationCookie,\n ...metadataCookies,\n browserCookie,\n ].filter((value): value is string => Boolean(value)),\n ));\n }\n\n private findAuthorizationAccountCookie(request: OperationHttpHandlerInput['request']): string | undefined {\n const authorization = request.headers.authorization;\n if (!authorization?.toLowerCase().startsWith(ACCOUNT_TOKEN_AUTHORIZATION_SCHEME.toLowerCase())) {\n return;\n }\n\n const value = authorization.slice(ACCOUNT_TOKEN_AUTHORIZATION_SCHEME.length).trim();\n return value || undefined;\n }\n\n private findBrowserAccountCookie(request: OperationHttpHandlerInput['request']): string | undefined {\n const cookieHeader = request.headers.cookie;\n if (!cookieHeader) {\n return;\n }\n\n for (const cookie of cookieHeader.split(';')) {\n const separator = cookie.indexOf('=');\n if (separator === -1) {\n continue;\n }\n\n const name = cookie.slice(0, separator).trim();\n if (name !== ACCOUNT_COOKIE_NAME) {\n continue;\n }\n\n const value = cookie.slice(separator + 1).trim();\n return value || undefined;\n }\n }\n\n private normalizeAccountCookie(\n operation: OperationHttpHandlerInput['operation'],\n selectedCookie: string | undefined,\n ): OperationHttpHandlerInput['operation'] {\n const metadata = new RepresentationMetadata(operation.body.metadata);\n metadata.removeAll(SOLID_HTTP.terms.accountCookie);\n if (selectedCookie) {\n metadata.add(SOLID_HTTP.terms.accountCookie, selectedCookie);\n }\n\n const body = Object.assign(\n Object.create(Object.getPrototypeOf(operation.body)),\n operation.body,\n { metadata },\n );\n\n return {\n ...operation,\n body,\n };\n }\n\n private async findValidAccount(cookies: string[], browserCookie: string | undefined): Promise<{\n accountId?: string;\n selectedCookie?: string;\n expiredCookie?: string;\n }> {\n if (cookies.length === 0) {\n return {};\n }\n\n let expiredCookie: string | undefined;\n let selectedCookie: string | undefined;\n let selectedAccountId: string | undefined;\n for (const cookie of cookies) {\n const accountId = await this.cookieStore.get(cookie);\n if (!accountId) {\n if (cookie === browserCookie) {\n expiredCookie ??= cookie;\n }\n continue;\n }\n\n const accountExists = await this.accountStorage.has(ACCOUNT_TYPE, accountId);\n if (accountExists) {\n selectedCookie ??= cookie;\n selectedAccountId ??= accountId;\n continue;\n }\n\n await this.cookieStore.delete(cookie);\n if (cookie === browserCookie) {\n expiredCookie ??= cookie;\n }\n this.logger.warn(`Deleted stale account cookie for missing account ${accountId}.`);\n }\n\n return { accountId: selectedAccountId, selectedCookie, expiredCookie };\n }\n}\n"]}
|