microsoft / OpenAPI.NET

The OpenAPI.NET SDK contains a useful object model for OpenAPI documents in .NET along with common serializers to extract raw OpenAPI JSON and YAML documents from the model.
MIT License
1.39k stars 230 forks source link

If securityScheme is a reference it cannot be resolved (see issue #1166, this issue still persists in V1.6.3) #1229

Open elize-vdr opened 1 year ago

elize-vdr commented 1 year ago

Describe the bug This is applicable to V1.6.3. When the securityScheme item is a reference to another securityScheme item in another file, then it cannot be resolved. I reported this bug previously but it seems to not be fixed entirely, we have 2 securityScheme items in the list and it resolves the first one but not the second. I tried to step through the code and found this: In the class OpenApiDocument and member function ResolveReference() line 499 this:

return this.Components.SecuritySchemes[reference.Id];

then at this point if you look into the value of this.Components.SecuritySchemes it has only the OAuth2 in the array and not also the PersonalAccessTokens, it then throws a KeyNotFoundException.

It is not entirely clear if the fact that the 2 items are references has anything to do with it or maybe there is another issue.

To Reproduce In the main OpenApi contract document we have this

components:
  securitySchemes:
    OAuth2:
      $ref: 'common-definitions_v4.yaml#/components/securitySchemes/OAuth2'
    PersonalAccessTokens:
      $ref: 'common-definitions_v4.yaml#/components/securitySchemes/PersonalAccessTokens'

and in the common-definitions_v4.yaml we have defined

components:
  securitySchemes:
    OAuth2:
      type: oauth2
      description: OAuth2 based authentication using authorization code flow.
      flows:
        authorizationCode:
          authorizationUrl: 'https://login.democompany.com/oauth/authorize'
          tokenUrl: 'https://login.democompany.com/oauth/token'
          scopes:
            mx:datahub:services:write: Operations on resources that may create, delete or change resources.
            mx:datahub:services:read: Read-only access to resources. 
            openid: Required to access user info, must have scope for every api
            offline_access: Required to access user info, must have scope for every api
            mx:user:profile:v1:read: Required to access user info, must have scope for every api
    PersonalAccessTokens:
      type: apiKey
      in: header
      name: Authorization
      description: 'Personal access tokens for access to the Data Hub Catalog APIs; set in your [Democompany Developer Settings](https://user-settings.democompany.com/link/developersettings). Contents of the header should be ```MxToken <Your PAT>```.'

Expected behavior When a securityScheme item is a reference to a securityScheme item in another file it should be resolved.

elize-vdr commented 1 year ago

I made an example, here is a primary contract file with a referenced file, the error received back is

[File: common-definitions.yaml] Invalid Reference identifier 'PersonalAccessTokens'

The primary contract file

openapi: 3.0.1
info:
  title: 'Data Hub Registration API'
  description: "This is the API description for the Data Hub. With these APIs, you can register and manage your data sources and datasets in the Data Hub."
  termsOfService: '#'
  contact:
    email: PlatformCore@DemoCompany.com
  version: '4.1.0'
externalDocs:
  description: 'Specification of this API'
  url: 'https://docs.DemoCompany.com/apidocs-mxsdk/apidocs/data-hub-apis/'
servers:
  - url: 'https://hub.DemoCompany.com/rest/registration/v4'
paths:
  /applications:
    post:
      tags:
        - Applications
      summary: 'Registers an application'
      security:
        - OAuth2: ['mx:datahub:services:write', 'openid', 'offline_access', 'mx:user:profile:v1:read']
        - PersonalAccessTokens: []
      requestBody:
          $ref: '#/components/requestBodies/Applications_POST'
      responses:
        '201':
          $ref: '#/components/responses/Applications_POST_201'
        '400':
          $ref: 'common-definitions.yaml#/components/responses/400'
        '500':
          $ref: 'common-definitions.yaml#/components/responses/500'
components:
  requestBodies:
    Applications_POST:
      description: 'Description of an application that publishes or consumes services.'
      content:
        application/json:
          schema:
            $ref: 'common-definitions.yaml#/components/schemas/Application_Base'
      required: true
  responses:
    Applications_POST_201:
      description: 'Created'
      headers:
        Location:
          schema:
            type: string
            maxLength: 400
            format: uri
          description: Location at which the newly created resource can be found.
      content:
        application/json:
          schema:
            $ref: 'common-definitions.yaml#/components/schemas/Application_Response'
  schemas:
    EndpointLocationConstant:
      type: object
      required:
        - Name
        - Value
      properties:
        Name:
          type: string
          example: 'MyFirstModule.EmployeeManagement_Location'
          description: 'Canonical name (Module.ConstantName) of the constant that holds the location of a consumed endpoint'
        Value:
          type: string
          example: 'https://hr.acmecorp.test/employeeservice/v2'
          description: 'Value of the location constant; should be a valid, absolute URL.  <br> In the metadata.json file this is the value given for the attribute DefaultValue.'
  securitySchemes:
    OAuth2:
      $ref: 'common-definitions.yaml#/components/securitySchemes/OAuth2'
    PersonalAccessTokens:
      $ref: 'common-definitions.yaml#/components/securitySchemes/PersonalAccessTokens'

The referenced file common-definitions.yaml

openapi: 3.0.1
info:
  title: 'Data Hub API Common Definitions'
  description: "These are the common definitions for the Data Hub API."
  termsOfService: '#'
  contact:
    email: application@DemoCompany.com
  version: '4.1.0'
paths:
  /services:
    post:
      tags:
        - Services
      summary: 'Registers a service'
      security:
        - OAuth2: ['mx:datahub:services:write', 'openid', 'offline_access', 'mx:user:profile:v1:read']
        - PersonalAccessTokens: []
      parameters:
        - $ref: '#/components/parameters/AppUUID_Path'
        - $ref: '#/components/parameters/ServiceName_Path'
      responses:
        '400':
          $ref: '#/components/responses/400'
        '500':
          $ref: '#/components/responses/500'
components:        
  parameters:
    AppUUID_Path:
      in: "path"
      name: "AppUUID"
      schema:
        type: string
        format: uuid
      required: true
      description: 'Valid UUID of the specified application.'
    ServiceName_Path:
      in: "path"
      name: "ServiceName"
      schema:
        type: string
        maxLength: 200
      required: true
      description: 'Name of the specified service.'
  responses:
    '400':
      description: 'Incorrect Input'
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Error_Base'
              - type: object
                properties:
                  code:
                    description: 'Code describing why the request failed.'
                    example: 'INVALID_REQUEST'
                  message:
                    description: 'Message detailing the reason why the request failed. A response with HTTP status 400 indicates (some of) the provided parameters with the request are invalid.'
                    example: 'Invalid input'
    '500':
      description: 'Internal Server Error'
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Error_Base'
              - type: object
                properties:
                  code:
                    description: 'Code describing why the request failed.'
                    example: 'INTERNAL_SERVER_ERROR'
                  message:
                    description: 'Message detailing the reason why the request failed. A response with HTTP status 500 indicates that an unexpected error occurred while the server was processing the request. If the problem persists you should contact DemoCompany Support.'
                    example: 'An internal server error occurred.'
  schemas:
    Owner:
      type: object
      required: 
        - Name
        - Email
      properties:
        Name: 
          type: string
          maxLength: 400
          description: Name of the owner.
          example: 'Stan Raine'
        Email:
          type: string
          maxLength: 500
          description: Email of the owner.
          example: 'stan.raine@acme.test'
        OpenID:
          type: string
          format: uri
          maxLength: 100
          description: The OpenID of the owner. Will only be available if the owner has a Mendix Platform account.
          example: 'https://mxid2.mendixcloud.com/mxid2/id?id=964fe18e-65f6-4ed2-99c3-62acee9dcdd6'         
    Application_Base:
      required:
        - Name
      properties:
        Name:
          type: string
          maxLength: 200
          example: 'AcmeHR'
          description: 'Name of the application.'
        Description:
          type: string
          maxLength: 10000
          example: 'This application is used to manage the entire HR process of AcmeCorp'
          description: 'Description of the application'
        RepositoryLocation:
          type: string
          format: uri
          maxLength: 400
          example: 'https://teamserver.sprintr.com/e0fb450a-203f-3e0a-ae3c-9cadaea00743'
          description: 'Location of the development repository of the application.'
        Type:
          type: string
          enum: [DemoCompany, Teamcenter, MindSphere, Microsoft, SAP, Opcenter, Other]
          example: 'DemoCompany'
          description: 'Type of the application, possible values are "DemoCompany", "Teamcenter", "MindSphere", "Microsoft", "SAP", "Opcenter" and "Other" (Default)'
        BusinessOwner:
          allOf:
            - $ref: '#/components/schemas/Owner'
            - type: object
              description: 'Business owner of the application.'
        TechnicalOwner:
          allOf:
            - $ref: '#/components/schemas/Owner'
            - type: object
              description: 'Technical owner of the application.'
    Application_Response:
      allOf:
        - $ref: '#/components/schemas/Application_Base'
        - type: object
          required:
            - Type
            - UUID
            - Icon
          properties:
            UUID:
              type: string
              format: uuid
              example: 'cfc36b98-7409-4384-b71d-f003b0c2f84b'
              description: 'UUID that uniquely identifies the application in the DemoCompany Platform.'
            Icon:
              type: string
              format: uri
              description: URL of the icon location
              example: 'https://cdn.DemoCompany.com/image.png'
    Error_Base:
      type: object 
      required:
        - code
        - message
      properties:
        code:
          type: string
        message:
          type: string  
  securitySchemes:
    OAuth2:
      type: oauth2
      description: OAuth2 based authentication using authorization code flow.
      flows:
        authorizationCode:
          authorizationUrl: https://login.DemoCompany.com/oauth/authorize
          tokenUrl: https://login.DemoCompany.com/oauth/token
          scopes:
            mx:datahub:services:write: Operations on resources that may create, delete or change resources.
            mx:datahub:services:read: Read-only access to resources. 
            openid: Required to access user info, must have scope for every api
            offline_access: Required to access user info, must have scope for every api
            mx:user:profile:v1:read: Required to access user info, must have scope for every api
    PersonalAccessTokens:
      type: apiKey
      in: header
      name: Authorization
      description: 'Personal access tokens for access to the Data Hub Catalog APIs; set in your [DemoCompany Developer Settings](https://user-settings.DemoCompany.com/link/developersettings). Contents of the header should be ```MxToken <Your PAT>```.'