-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: slugs ending on slash with link header cannot create containers #1171
Conversation
44d4345
to
0f1128d
Compare
(Force pushed to rebase onto versions/3.0.0 branch) |
const isContainer = this.isNewContainer(metadata); | ||
const slug = metadata.get(SOLID_HTTP.slug)?.value; | ||
// Error if slug ends on slash, but no ContainerType Link header is present | ||
if (slug && isContainerPath(slug) && !this.hasContainerType(metadata.getAll(RDF.type))) { | ||
throw new BadRequestHttpError(`Slugs cannot end with '/' without an HTTP Link header with rel="type" targeting ` + | ||
`a valid LDP container type.`); | ||
} | ||
metadata.removeAll(SOLID_HTTP.slug); | ||
|
||
let newID: ResourceIdentifier = this.createURI(container, isContainer, slug); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with this solution is that the isNewContainer
is going to return an incorrect result since that still checks the slug. That function should be updated to only check the link type. Additionally, the createUri
function calls cleanSlug
which also throws an error if there is a slash so now there's some duplication there.
My suggestion would be to instead fix the isNewContainer
function, merge createUri
with cleanSlug
, and update the error there so it only gets thrown in case the result is not a container.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, but cleanSlug()
actually does more: it checks that the slug path does not contain any slashes. So in case of a directory (read: Link header is ok) a slug like my/test/slug
should still error with the specific Slugs should not contain slashes
error.
While myTestSlug/
without a correct Link header could error with Slugs cannot end with '/' without an HTTP Link header with rel="type" targeting a valid LDP container type.
So I would:
- Fix
isNewContainer()
like you said: only check on the link header being present and correct - keep
cleanSlug()
as is - Revert
createSafeUri()
- Modify
createURI()
: test for both cases there:- isContainer == true AND slug.endswitch('/') => throw my error
- cleanSlug() call: throws if slashes are still inside slug with existing error there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, but
cleanSlug()
actually does more: it checks that the slug path does not contain any slashes. So in case of a directory (read: Link header is ok) a slug likemy/test/slug
should still error with the specificSlugs should not contain slashes
error.
You're right, I was confused. Your new solution sounds good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this did something similar and suggests slightly different behaviour:
https://github.com/solid/community-server/blob/44d43456b168fc5e692e7ae1fcbe85d4beeb61b0/src/storage/DataAccessorBasedStore.ts#L199-L204
I chose to go for that behaviour, since it is consistent: resource slugs/path don't end in slash, directory slugs/path end in slash with the added requirement of the link header.
Since checking fot this behaviour is required in both setRepresentation()
and createURI()
, I've encapsulated it in a new method validateSlug(isContainer:boolean, suffix?: string): void
.
I am effectively reusing the BadRequestHttpError
mentioned above.
All unit tests now succeed, but now some integration tests seem to fail, I can't really figure out why though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am trying to fix most of the obvious failing ones (by adding a Link header, since there are some that just require that)
const isContainer = this.isNewContainer(representation.metadata, identifier.path); | ||
const isContainer = this.isNewContainer(representation.metadata); | ||
// Solid, Β§3.1: "Paths ending with a slash denote a container resource." | ||
// https://solid.github.io/specification/protocol#uri-slash-semantics | ||
if (isContainer !== isContainerIdentifier(identifier)) { | ||
throw new BadRequestHttpError('Containers should have a `/` at the end of their path, resources should not.'); | ||
} | ||
this.validateSlug(isContainer, identifier.path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the cause of your tests failing. When doing a PUT it is not required to have the correct link headers
When creating new resources, servers can determine an effective request URIβs type by examining the URI path ending
So adding checks here is going to cause all calls that depend on that behaviour to fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got them all checked now. Changed the name of isNewContainer()
to isContainerType
to better accommodate what it does now. (only checking the metadata)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor changes still required. Probably a consequence of the confusion with the previous setRepresentation
behaviour π
.
const potentialSlug = representation.metadata.get(SOLID_HTTP.slug)?.value; | ||
if (potentialSlug && (isContainerPath(potentialSlug) !== isContainerIdentifier(identifier))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const potentialSlug = representation.metadata.get(SOLID_HTTP.slug)?.value; | |
if (potentialSlug && (isContainerPath(potentialSlug) !== isContainerIdentifier(identifier))) { | |
if (isContainerType(representation.metadata) !== isContainerIdentifier(identifier)) { |
You're actually right that this is something that was checked here due to the isNewContainer
call, but that was just a consequence of that check having been added to that function later. That's on me for saying we needed the exact same behaviour π
. But the PUT calls don't care about the slug so this can be removed.
The type check I suggest above is not strictly necessary either as we set the types correctly afterwards, but is probably still a good idea to prevent users from making mistakes.
*/ | ||
protected validateSlug(isContainer: boolean, suffix?: string): void { | ||
if (suffix && (isContainer !== isContainerPath(suffix))) { | ||
throw new BadRequestHttpError('Containers should have a `/` at the end of their path, resources should not.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throw new BadRequestHttpError('Containers should have a `/` at the end of their path, resources should not.'); | |
throw new BadRequestHttpError('Only slugs used to create containers can end with a `/`.'); |
/** | ||
* Validates if the slug and headers are valid. | ||
* Errors if slug exists, ends on slash, but ContainerType Link header is NOT present | ||
* OR if slug exists, does not end on slash, but a ContainerType Link header IS present |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* OR if slug exists, does not end on slash, but a ContainerType Link header IS present |
This case should not fail. We are just being permissive and allowing users to end their slug with a slash if they create a container, but it is not a requirement.
test/integration/ServerFetch.test.ts
Outdated
body: '<a:b> <a:b> <a:b>.', | ||
body: `<a:b> <a:b> <a:b>.`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this is an artifact of previous changes? π
Co-authored-by: Joachim Van Herwegen <joachimvh@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good now, only thing is that I see that there are several tests where the line representation.metadata.add(RDF.type, LDP.terms.Container);
was added. But this should not be necessary right? Can you remove those so the tests stay mostly the same?
Ah yes, I will check against the base and remove where not needed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks.
π Related issues
#1082
βοΈ Description
This PR makes creating containers by POSTing a slug ending on
/
illegal if there is no Link header present that declares it as a Container type.While at first this seems the logical place to change this behaviour:
https://github.com/solid/community-server/blob/d2870e5c8bc2d86b1a43db3a8e039f8b494657da/src/storage/DataAccessorBasedStore.ts#L533-L539
Because of the special root container
/
case, it was cleaner and less code to just make the change here (in thecreateSafeUri(...)
method):https://github.com/solid/community-server/blob/44d43456b168fc5e692e7ae1fcbe85d4beeb61b0/src/storage/DataAccessorBasedStore.ts#L506-L510
I've labeled this with
semver.major
because I think it will break some peoples assumptions on how to create containers.β PR check list
Before this pull request can be merged, a core maintainer will check whether