Skip to content

objects: Escape names in Gophercloud#2821

Merged
EmilienM merged 1 commit intogophercloud:masterfrom
shiftstack:urlencode
Oct 31, 2023
Merged

objects: Escape names in Gophercloud#2821
EmilienM merged 1 commit intogophercloud:masterfrom
shiftstack:urlencode

Conversation

@pierreprinetti
Copy link
Copy Markdown
Member

@pierreprinetti pierreprinetti commented Oct 23, 2023

With this change, container and object names passed to the Gophercloud functions in objectstorage/v1/containers and objectstorage/v1/objects are now URL-escaped. If you were escaping them prior to passing them to Gophercloud, you should now remove escaping and pass the intended name to Gophercloud directly.

This is a breaking change.

API CHANGES:

  • Before this change, container and object names were sent as-is to Swift. With this change, Gophercloud now escapes container and object names in all objects and containers functions. If you were previously escaping names ( with, for example: url.PathEscape or url.QueryEscape), then you should REMOVE that and pass the intended names to Gophercloud directly
  • The containers.ListOpts#Full and objects.ListOpts#Full properties are REMOVED from the Gophercloud API. The reason for that is: plaintext listing is unfixably wrong and won't handle special characters reliably (i.e. \n).
  • Empty container names, container names containing a slash (/), and empty object names are now rejected in Gophercloud before any call to Swift.
  • containers.ErrInvalidContainerName is now v1.ErrInvalidContainerName
  • New name validation errors:
    • v1.ErrEmptyContainerName
    • v1.ErrEmptyObjectName
  • In objects.Copy: the destination field (e.g. objects.CopyOpts#Destination) must be in the form /container/object: the function will reject a destination path if it doesn't start with a slash (/).

CODE CHANGES:

  • containers.List and objects.List are now always setting accept: application/json. There is no plaintext listing any more, for the reason detailed above.
  • containers and objects acceptance tests are now run with nasty Unicode code points (including European characters, a space, "\n", backslash, slash where appropriate, emojis, emojis with modifiers, Chinese traditional characters), instead of only using nice ASCII characters.
  • Container and object names are now systematically validated before being sent to Swift.

INCIDENTAL FIX:

  • it is now possible to successfuly call objects.CreateTempURL with a container name or object name containing the string /v1/.

Fixes #2816
Fixes #2547
Fixes #2827

@pierreprinetti
Copy link
Copy Markdown
Member Author

pierreprinetti commented Oct 23, 2023

TODO:

  • acceptance tests
  • unit tests

@github-actions github-actions bot added the semver:major Breaking change label Oct 23, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 24, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 24, 2023
@coveralls
Copy link
Copy Markdown

coveralls commented Oct 24, 2023

Coverage Status

coverage: 77.855% (-0.1%) from 77.962% when pulling 9d0243f on shiftstack:urlencode into cc89262 on gophercloud:master.

@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 24, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 25, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 25, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 25, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 25, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 25, 2023
@github-actions github-actions bot removed the semver:major Breaking change label Oct 25, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 25, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 25, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 26, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 26, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 26, 2023
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 26, 2023
With this change, container and object names passed to the Gophercloud
functions in `objectstorage/v1/containers` and
`objectstorage/v1/objects` are now URL-escaped. If you were escaping
them prior to passing them to Gophercloud, you should now remove
escaping and pass the intended name to Gophercloud directly.

**This is a breaking change.**

API CHANGES:
* Before this change, container and object names were sent as-is to
  Swift. With this change, **Gophercloud now escapes container and
  object names in all `objects` and `containers` functions**. If you
  were previously escaping names ( with, for example: `url.PathEscape`
  or `url.QueryEscape`), then you should REMOVE that and pass the
  intended names to Gophercloud directly.
* The **`containers.ListOpts#Full` and `objects.ListOpts#Full`
  properties are REMOVED** from the Gophercloud API. The reason for that
  is: plaintext listing is unfixably wrong and won't handle special
  characters reliably (i.e. `\n`).
* Empty container names, container names containing a slash (`/`), and
  empty object names are now rejected in Gophercloud before any call to
  Swift.
* `containers.ErrInvalidContainerName` is now `v1.ErrInvalidContainerName`
* New name validation errors:
  * `v1.ErrEmptyContainerName`
  * `v1.ErrEmptyObjectName`
* In `objects.Copy`: the `destination` field (e.g.
  `objects.CopyOpts#Destination`) must be in the form
  `/container/object`: the function will reject a destination path if it
  doesn't start with a slash (`/`).

CODE CHANGES:
* `containers.List` and `objects.List` are now always setting `accept:
  application/json`. There is no plaintext listing any more, for the
  reason detailed above.
* `containers` and `objects` acceptance tests are now run with nasty
  Unicode code points (including European characters, a space, "\n",
  backslash, slash where appropriate, emojis, emojis with modifiers,
  Chinese traditional characters), instead of only using nice ASCII
  characters.
* Container and object names are now systematically validated before
  being sent to Swift.

INCIDENTAL FIX:
* it is now possible to successfuly call `objects.CreateTempURL` with a
  container name or object name containing the string `/v1/`.
@github-actions github-actions bot added semver:major Breaking change and removed semver:major Breaking change labels Oct 26, 2023
@pierreprinetti pierreprinetti marked this pull request as ready for review October 26, 2023 09:11
@pierreprinetti pierreprinetti requested a review from a team October 26, 2023 09:12
// Copy the contents of one object to another.
copyOpts := objects.CopyOpts{
Destination: cName + "/" + oNames[1],
Destination: "/" + cName + "/" + oNames[1],
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per the docs: the Copy Destination header must be in the form of /container/object.


// List all created objects
listOpts := objects.ListOpts{
Full: true,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List option Full: false (accept: text/plain) doesn't work and should not be used

Comment on lines +33 to +36
for i := 0; i < len(oNames)-1; i++ {
oNames[i] = "test-object-" + tools.RandomFunnyString(8)
}
oNames[len(oNames)-1] = "test-object-with-/v1/-in-the-name"
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were two test cases. Now there are three: the last one tests that we correctly split the object path in CreateTempURL.

}()

listOpts := objects.ListOpts{
Full: true,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List option Full: false (accept: text/plain) doesn't work and should not be used. We always list "full" now (accept: application/json).

}

listOpts = objects.ListOpts{
Full: true,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List option Full: false (accept: text/plain) doesn't work and should not be used. We always list "full" now (accept: application/json).


const forbiddenContainerRunes = "/"

func CheckContainerName(s string) error {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to the parent directory v1


// Copy is a function that copies one object to another.
func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) (r CopyResult) {
url, err := copyURL(c, containerName, objectName)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moving this just a bit down below, just because url shadows the net/url package which we need for escaping.

if err != nil {
return "", err
}
urlToBeSigned, err := tempURL(c, containerName, objectName)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need two different URLs: one with non-escaped names (which will be signed), and one with escaped names (which will be used as the actual URL to send back to the caller). Documented here: https://docs.openstack.org/swift/latest/api/temporary_url_middleware.html#hmac-signature-for-temporary-urls

}

secretKey := []byte(tempURLKey)
splitPath := strings.Split(url, opts.Split)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incidental fix: Split splits url each time opts.Split (default: /v1/) is found. But below, we only joined the first two parts back into a full URL. Which means that a path like https://swift/v1/AUTH_123/container/my/v1/object became https://swift/v1/AUTH_123/container/my (see #2827)

// ExpectedListNames is the result expected from a call to `List` when just
// object names are requested.
var ExpectedListNames = []string{"hello", "goodbye"}
var ExpectedListNames = []string{"goodbye", "hello"}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how this ever worked. Objects are always returned lexicographically sorted.

Copy link
Copy Markdown
Contributor

@EmilienM EmilienM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

impressive work, LGTM

@EmilienM EmilienM merged commit 69b57d9 into gophercloud:master Oct 31, 2023
@EmilienM EmilienM deleted the urlencode branch October 31, 2023 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:major Breaking change

Projects

None yet

3 participants