Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/pip/certifi-2023.7.22
Browse files Browse the repository at this point in the history
  • Loading branch information
ewan-IW committed Sep 7, 2023
2 parents 0602039 + 3fe2b0b commit 12000e1
Show file tree
Hide file tree
Showing 36 changed files with 337 additions and 128 deletions.
37 changes: 17 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# gp-connect-user-permissions

![Build](https://github.com/NHSDigital/gp-connect-user-permissions/workflows/Build/badge.svg?branch=master)

This is a specification for the *gp-connect-user-permissions* API.

* `specification/` This [Open API Specification](https://swagger.io/docs/specification/about/) describes the endpoints, methods and messages exchanged by the API. Use it to generate interactive documentation; the contract between the API and its consumers.
* `sandbox/` This NodeJS application implements a mock implementation of the service. Use it as a back-end service to the interactive documentation to illustrate interactions and concepts. It is not intended to provide an exhaustive/faithful environment suitable for full development and testing.
* `mock_provider/` This application implements a mock implementation of the service. Use it as a back-end service to the interactive documentation to illustrate interactions and concepts. It is not intended to provide an exhaustive/faithful environment suitable for full development and testing.
* `scripts/` Utilities helpful to developers of this specification.
* `proxies/` Live (connecting to another service) and sandbox (using the sandbox container) Apigee API Proxy definitions.
* `infra/` This terraform stack creates the AWS infrastructure that is needed to run `mock_provider`
* `terraform` This terraform stack creates our `mock_provider` service

Check out `README.md` file in the `terraform` directory for more information regarding `mock_provider` and our terraform stack.

Consumers of the API will find developer documentation on the [NHS Digital Developer Hub](https://digital.nhs.uk/developer).

Expand All @@ -25,11 +27,10 @@ The contents of this repository are protected by Crown Copyright (C).
* make
* nodejs + npm/yarn
* [poetry](https://github.com/python-poetry/poetry)
* Java 8+

### Install
```
$ make install
$ make .git/hooks/pre-commit
```

#### Updating hooks
Expand All @@ -49,10 +50,9 @@ Various scripts and commands rely on environment variables being set. These are
There are `make` commands that alias some of this functionality:
* `lint` -- Lints the spec and code
* `publish` -- Outputs the specification as a **single file** into the `build/` directory
* `serve` -- Serves a preview of the specification in human-readable format

### Testing
Each API and team is unique. We encourage you to use a `test/` folder in the root of the project, and use whatever testing frameworks or apps your team feels comfortable with. It is important that the URL your test points to be configurable. We have included some stubs in the Makefile for running tests.
Each API and team is unique. We encourage you to use a `tests/` folder in the root of the project, and use whatever testing frameworks or apps your team feels comfortable with. It is important that the URL your test points to be configurable. We have included some stubs in the Makefile for running tests.

### VS Code Plugins

Expand All @@ -64,23 +64,20 @@ Each API and team is unique. We encourage you to use a `test/` folder in the roo

* [**openapi-yaml-mode**](https://github.com/esc-emacs/openapi-yaml-mode) provides syntax highlighting, completion, and path help

### Speccy
### Specification

> [Speccy](http://speccy.io/) *A handy toolkit for OpenAPI, with a linter to enforce quality rules, documentation rendering, and resolution.*
Speccy does the lifting for the following npm scripts:

* `test` -- Lints the definition
* `publish` -- Outputs the specification as a **single file** into the `build/` directory
* `serve` -- Serves a preview of the specification in human-readable format
> [openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator-cli) *OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (both 2.0 and 3.0 are supported).*
(Workflow detailed in a [post](https://developerjack.com/blog/2018/maintaining-large-design-first-api-specs/) on the *developerjack* blog.)
`openapi-generator-cli` does the lifting for the following npm scripts:

:bulb: The `publish` command is useful when uploading to Apigee which requires the spec as a single file.
* `test` -- Lints the definition
* `publish` -- Outputs the specification as a json file into the `build/` directory

### Caveats

#### Swagger UI
Swagger UI unfortunately doesn't correctly render `$ref`s in examples, so use `speccy serve` instead.
Swagger UI only works with `$ref` if the path is resolved. That means, if you want to use it, you either need to provide all the files
or create a single file with inlined content i.e. not external files and no `$ref`s.

#### Apigee Portal
The Apigee portal will not automatically pull examples from schemas, you must specify them manually.
Expand Down Expand Up @@ -113,7 +110,7 @@ Contains templates defining Azure Devops pipelines. By default the following pip

The `project.yml` file needs to be populated with your service names to make them available to the pipelines

`/azure/templates`: Here you can define reusable actions, such as running tests, and call these actions during Azure Devops pipelines.
`/azure/templates`: Here you can define reusable actions, such as running tests, and call these actions during Azure Devops pipelines.

#### `/proxies`:

Expand Down Expand Up @@ -158,7 +155,7 @@ These files are required to deploy containers alongside your Apigee proxy during

`ecs-proxies-containers.yml`: The path to a container's Dockerfile is defined here. This path needs to be defined to allow containers to be pushed to our repository during the `azure-build-pipeline`.

`ecs-proxies-deploy.yml` : Here you can define config for your container deployment.
`ecs-proxies-deploy.yml` : Here you can define config for your container deployment.

For more information about deploying ECS containers see the [API Producer Zone confluence](https://nhsd-confluence.digital.nhs.uk/display/APM/Developing+ECS+proxies#DevelopingECSproxies-Buildingandpushingdockercontainers ).

Expand All @@ -170,4 +167,4 @@ This file defines 2 dictionaries of fields that are required for the Apigee depl

This template uses poetry for python dependency management, and uses these files: poetry.lock, poetry.toml, pyproject.toml.

Node dependencies of this template project and some npm scripts are listed in: package.json, package-lock.json.
Node dependencies of this template project and some npm scripts are listed in: package.json, package-lock.json.
11 changes: 10 additions & 1 deletion azure/azure-release-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,13 @@ extends:
GP_CONNECT_CONFIG_ENCRYPTED: gp-connect-config-encrypted
post_deploy:
- template: ./templates/post-deploy.yml

- environment: prod
depends_on:
- int
- sandbox
jinja_templates:
GP_CONNECT_ENDPOINTS: gp-connect-user-permissions-endpoints
GP_CONNECT_CONFIG: gp-connect-user-permissions-config
GP_CONNECT_CONFIG_ENCRYPTED: gp-connect-config-encrypted
post_deploy:
- template: ./templates/post-deploy.yml
2 changes: 1 addition & 1 deletion azure/project.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
variables:
service_name: gp-connect-user-permissions
short_service_name: gcup
service_base_path: gp-connect-user-permissions/FHIR/R4
service_base_path: gp-connect/patient-facing/user-permissions
pr_number: ${{ split(variables['Build.SourceBranch'], '/')[2] }}
2 changes: 1 addition & 1 deletion azure/templates/post-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ steps:
source .venv/bin/activate
domain_name=https://$(make -C terraform -s output name=service_domain_name)
oauth_endpoint="https://identity.ptl.api.platform.nhs.uk/realms/gpconnect-pfs-mock-$APIGEE_ENVIRONMENT/protocol/openid-connect/token"
python scripts/apigee_kvm.py --env $APIGEE_ENVIRONMENT --access-token $(secret.AccessToken) populate-interaction-ids gp-connect-user-permissions-endpoints-pr-$pr_no --ods REPC --provider-endpoint $domain_name --oauth-endpoint $oauth_endpoint
python scripts/apigee_kvm.py --env $APIGEE_ENVIRONMENT --access-token $(secret.AccessToken) populate-interaction-ids gp-connect-user-permissions-endpoints-pr-$pr_no --ods H81109 --provider-endpoint $domain_name --oauth-endpoint $oauth_endpoint
fi
fi
displayName: Deploy mock provider
Expand Down
7 changes: 6 additions & 1 deletion manifest_template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ APIGEE_ENVIRONMENTS:
- name: int
display_name: Integration Testing
has_mock_auth: true
- name: prod
approval_type: manual
display_name: Production
quota: '7800'
ratelimit: '15000pm'
---
meta:
api:
Expand All @@ -44,7 +49,7 @@ apigee:
- name: {{ ENV.name }}
products:
- name: {{ NAME }}
approvalType: auto
approvalType: {{ ENV.approval_type | default('auto') }}
attributes:
- name: access
value: public
Expand Down
10 changes: 4 additions & 6 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 0 additions & 10 deletions proxies/live/apiproxy/policies/AssignMessage.AddAsidHeader.xml

This file was deleted.

This file was deleted.

This file was deleted.

14 changes: 0 additions & 14 deletions proxies/live/apiproxy/policies/AssignMessage.SetStatusResponse.xml

This file was deleted.

14 changes: 14 additions & 0 deletions proxies/live/apiproxy/policies/GenerateJWT.PDSClientAssertion.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GenerateJWT async="false" continueOnError="false" enabled="true" name="GenerateJWT.PDSClientAssertion">
<DisplayName>GenerateJWT.PDSClientAssertion</DisplayName>
<Algorithm>RS512</Algorithm>
<PrivateKey>
<Value ref="private.pds_jwt"/>
<Id ref="pds_kid_config"/>
</PrivateKey>
<Subject ref="pds_client_id_config"/>
<Issuer ref="pds_client_id_config"/>
<Audience ref="pds_audience_config"/>
<ExpiresIn>5m</ExpiresIn>
<OutputVariable>pds_client_assertion</OutputVariable>
</GenerateJWT>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" name="JavaScript.GetPDSAccessToken">
<DisplayName>JavaScript.GetPDSAccessToken.xml</DisplayName>
<Properties/>
<ResourceURL>jsc://GetPDSAccessToken.js</ResourceURL>
</Javascript>
6 changes: 6 additions & 0 deletions proxies/live/apiproxy/policies/JavaScript.SetPDSConfig.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" name="JavaScript.SetPDSConfig">
<DisplayName>JavaScript.SetPDSConfig</DisplayName>
<Properties/>
<ResourceURL>jsc://SetPDSConfig.js</ResourceURL>
</Javascript>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<KeyValueMapOperations async="false" continueOnError="false" enabled="true" name="KeyValueMapOperations.GetPDSConfig" mapIdentifier="{{ GP_CONNECT_CONFIG }}">
<DisplayName>KeyValueMapOperations.GetPDSConfig</DisplayName>
<Get assignTo="private.pds_config">
<Key>
<Parameter>PDS</Parameter>
</Key>
</Get>
</KeyValueMapOperations>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<KeyValueMapOperations async="false" continueOnError="false" enabled="true" name="KeyValueMapOperations.GetPDSSecureVariables" mapIdentifier="{{ GP_CONNECT_CONFIG_ENCRYPTED }}">
<Get assignTo="private.pds_jwt" index="1">
<Key>
<Parameter>PDS_private_key</Parameter>
</Key>
</Get>
</KeyValueMapOperations>
10 changes: 10 additions & 0 deletions proxies/live/apiproxy/policies/LookupCache.PDSAccessToken.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<LookupCache async="false" continueOnError="false" enabled="true" name="LookupCache.PDSAccessToken">
<DisplayName>LookupCache.PDSAccessToken</DisplayName>
<Properties/>
<Scope>Exclusive</Scope>
<CacheKey>
<KeyFragment>PDSAccessToken</KeyFragment>
</CacheKey>
<AssignTo>PDSAccessToken</AssignTo>
</LookupCache>
13 changes: 13 additions & 0 deletions proxies/live/apiproxy/policies/PopulateCache.PDSAccessToken.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PopulateCache async="false" continueOnError="false" enabled="true" name="PopulateCache.PDSAccessToken">
<DisplayName>PopulateCache.PDSAccessToken</DisplayName>
<Properties/>
<Scope>Exclusive</Scope>
<CacheKey>
<KeyFragment>PDSAccessToken</KeyFragment>
</CacheKey>
<ExpirySettings>
<TimeoutInSec>300</TimeoutInSec>
</ExpirySettings>
<Source>PDSAccessToken</Source>
</PopulateCache>
10 changes: 0 additions & 10 deletions proxies/live/apiproxy/policies/RaiseFault.400BadRequest.xml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ServiceCallout async="false" continueOnError="false" enabled="true" name="ServiceCallout.AuthenticateForPDS">
<DisplayName>ServiceCallout.AuthenticateForPDS</DisplayName>
<Properties/>
<Request clearPayload="true" variable="AuthRequest">
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<Set>
<Verb>POST</Verb>
<Headers>
<Header name="Content-Type">application/x-www-form-urlencoded</Header>
<Header name="Connection">keep-alive</Header>
<Header name="accept">*/*</Header>
<Header name="accept-encoding">gzip, deflate, br</Header>
<Header name="cache-control">no-cache</Header>
<Header name="user-agent">APIM</Header>
</Headers>
<FormParams>
<FormParam name="client_assertion">{pds_client_assertion}</FormParam>
<FormParam name="client_assertion_type">urn:ietf:params:oauth:client-assertion-type:jwt-bearer</FormParam>
<FormParam name="grant_type">client_credentials</FormParam>
</FormParams>
</Set>
</Request>
<Response>PDSAuthResponse</Response>
<HTTPTargetConnection>
<URL>https://int.api.service.nhs.uk/oauth2/token</URL>
</HTTPTargetConnection>
<Timeout>20000</Timeout>
</ServiceCallout>
22 changes: 22 additions & 0 deletions proxies/live/apiproxy/policies/ServiceCallout.GetODSCode.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ServiceCallout async="false" continueOnError="false" enabled="true" name="ServiceCallout.GetODSCode">
<DisplayName>ServiceCallout.GetODSCode</DisplayName>
<Properties/>
<Request clearPayload="true" variable="PDSRequest">
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<Set>
<Verb>GET</Verb>
<Headers>
<Header name="accept">application/fhir+json</Header>
<Header name="authorization">Bearer {PDSAccessToken}</Header>
<Header name="X-Request-ID">{x_request_id}</Header>
</Headers>
<Path>/Patient/{nhs_number}</Path>
</Set>
</Request>
<Response>PDSResponse</Response>
<HTTPTargetConnection>
<URL>https://int.api.service.nhs.uk/personal-demographics/FHIR/R4</URL>
</HTTPTargetConnection>
<Timeout>20000</Timeout>
</ServiceCallout>
18 changes: 14 additions & 4 deletions proxies/live/apiproxy/resources/jsc/CreateKVMKeyNames.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
var ods = context.getVariable("jwt.DecodeJWT.DecodeIDToken.claim.ods_code")
var interactionId = context.getVariable("request.header.Interaction-ID")
function json_tryparse(raw) {
try {
return JSON.parse(raw);
}
catch (e) {
return raw;
}
}

var endpointConfigEncryptedKey= ods + "_private_key"
var respContent=context.getVariable('PDSResponse.content');
const respObject=json_tryparse(respContent);
var ods = respObject["generalPractitioner"][0]["identifier"]["value"]

var interactionId = context.getVariable("request.header.Interaction-ID")
var endpointConfigEncryptedKey= ods + "_private_key"

context.setVariable("endpointODSKey", ods);
context.setVariable("endpointInteractionKey", interactionId)
context.setVariable("endpointODSKey", ods)
context.setVariable("endpointConfigEncryptedKey", endpointConfigEncryptedKey)

var GPCAuthInteractionId = properties.GPCAuthInteractionId
Expand Down
Loading

0 comments on commit 12000e1

Please sign in to comment.