Skip to content
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

CompositeAuthenticationProvider fails to apply authentication if a parameter of the path must be URL encoded #941

Open
jorgebsa opened this issue Jan 17, 2025 · 1 comment
Labels
area:client This item is related to the client extension bug Something isn't working

Comments

@jorgebsa
Copy link

I'm working on a Quarkus application that interacts with Forgejo/Gitea via its own rest client interfaces. I've been trying to replace the custom interfaces with ones generated directly from the specification using this library. However, I am running into an issue with the CompositeAuthenticationProvider mechanism.

I will go into more details, but firstly let me provide a snippet of the spec relevant to the issue:

schemes:
  - https
  - http
swagger: '2.0'
info:
  description: This documentation describes the Forgejo API.
  title: Forgejo API
  license:
    name: This file is distributed under the MIT license for the purpose of interoperability
    url: http://opensource.org/licenses/MIT
  version: 9.0.3+gitea-1.22.0
paths:
  /api/v1/repos/{owner}/{repo}/contents/{filepath}:
    get:
      produces:
        - application/json
      tags:
        - repository
      summary: Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
      operationId: repoGetContents
      parameters:
        - type: string
          description: owner of the repo
          name: owner
          in: path
          required: true
        - type: string
          description: name of the repo
          name: repo
          in: path
          required: true
        - type: string
          description: path of the dir, file, symlink or submodule in the repo
          name: filepath
          in: path
          required: true
        - type: string
          description: The name of the commit/branch/tag. Default the repository’s default branch (usually master)
          name: ref
          in: query
      responses:
        '200':
          description: Successful response
          schema:
            type: array
            items:
              $ref: '#/definitions/ContentsResponse'
        '404':
          $ref: '#/responses/notFound'
securityDefinitions:
  AuthorizationHeaderToken:
    description: API tokens must be prepended with "token" followed by a space.
    type: apiKey
    name: Authorization
    in: header
security:
  - AuthorizationHeaderToken: []

I've omitted everything that has no impact on the issue for brevity.

So, I am injecting the RepositoryApi, which looks like this:

/**
  * Forgejo API
  * <p>This documentation describes the Forgejo API.</p>
  */
@jakarta.ws.rs.Path("/api/v1")
@org.eclipse.microprofile.rest.client.inject.RegisterRestClient(configKey="forgejo_yaml")
@io.quarkiverse.openapi.generator.annotations.GeneratedClass(value="forgejo.yaml", tag = "Repository")
@org.eclipse.microprofile.rest.client.annotation.RegisterProvider(com.example.forgejo.api.auth.CompositeAuthenticationProvider.class)
@org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders(com.example.forgejo.api.auth.AuthenticationPropagationHeadersFactory.class)
@jakarta.enterprise.context.ApplicationScoped
public interface RepositoryApi {

     /**
     * Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
     *
     * @param owner owner of the repo
     * @param repo name of the repo
     * @param filepath path of the dir, file, symlink or submodule in the repo
     * @param ref The name of the commit/branch/tag. Default the repository’s default branch (usually master)
     */
    @io.quarkiverse.openapi.generator.markers.OperationMarker(name="AuthorizationHeaderToken", openApiSpecId="forgejo_yaml", operationId="repoGetContents", method="GET", path="/api/v1/repos/{owner}/{repo}/contents/{filepath}")
    @jakarta.ws.rs.GET
    @jakarta.ws.rs.Path("/repos/{owner}/{repo}/contents/{filepath}")
    @jakarta.ws.rs.Produces({"application/json"})
    @io.quarkiverse.openapi.generator.annotations.GeneratedMethod("repoGetContents")
    public List<ContentsResponse> repoGetContents(
        @io.quarkiverse.openapi.generator.annotations.GeneratedParam("owner") @jakarta.ws.rs.PathParam("owner")String owner, 
        @io.quarkiverse.openapi.generator.annotations.GeneratedParam("repo") @jakarta.ws.rs.PathParam("repo")String repo, 
        @io.quarkiverse.openapi.generator.annotations.GeneratedParam("filepath") @jakarta.ws.rs.PathParam("filepath")String filepath, 
        @io.quarkiverse.openapi.generator.annotations.GeneratedParam("ref") @jakarta.ws.rs.QueryParam("ref") String ref
    );
}

In order to use the built-in mechanism for Authentication, I've added the following property:

quarkus.openapi-generator.forgejo_yaml.auth.AuthorizationHeaderToken.api-key=test my-token

Yet. when I use the client to retrieve contents of a filepath that contains / into it, the mechanism fails to add the Authorization header. Upon debugging, it became clear that the fault lies on OperationAuthInfo#matchPath and the underlying UrlPatternMatcher.

Lets say that I am invoking the repoGetContents with a filepath value of some/dir, this value will be URL Encoded by the client to some%2Fdir when the HTTP request is actually made. However, OperationAuthInfo#matchPath is called by CompositeAuthenticationProvider with a requestUrl value of some/dir, which is passed to the UrlPatternMatcher instance and it fails the match because the compiler URL pattern is

/api/v1/repos/([%\w-.\~!$&'\(\)\*\+,;=:\[\]@]+?)/([%\w-.\~!$&'\(\)\*\+,;=:\[\]@]+?)/contents/([%\w-.\~!$&'\(\)\*\+,;=:\[\]@]+?)(?:\?.*?)?$

Now, the obvious first thought is to URL encode the String filepath. I've added it and debugged, by doing so the pattern will match, yet the integration tests will fail, because the client will double encode the final path, so this simple solution is not viable.

I've looked all over your documentation, but couldn't find anything similar to this, or any configuration that I could add that would add something like the @Encoded annotation to this parameter.

Have you faced anything similar? Anyone know any possible solution with the existing version, or would it require changes to the library?

Best regards

@jorgebsa jorgebsa changed the title Can't use built-in auth mechanism due to OperationAuthInfo#matchPath failure given requestPath isn't URL Encoded at the time it's called CompositeAuthenticationProvider fails to apply authentication if a parameter of the path must be URL encoded Jan 17, 2025
@ricardozanini
Copy link
Member

Hi @jorgebsa, thanks for reporting this issue. I haven't seen this problem before, so if you have an idea how to improve or fix it, please feel free to send a PR.

@ricardozanini ricardozanini added bug Something isn't working area:client This item is related to the client extension labels Jan 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:client This item is related to the client extension bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants