Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ end_of_line = lf
[Makefile]
indent_style = tab

[*.{xml,js,json,yaml}]
[*.{xml,js,json,yaml,yml}]
indent_size = 2

[*.postman_collection.json]
Expand Down
92 changes: 48 additions & 44 deletions azure/templates/run-tests-int.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ parameters:
default: false

steps:
- task: UsePythonVersion@0
inputs:
versionSpec: "3.9"

- task: s3-cache-action@1
inputs:
key: 'poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock'
location: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv'
key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock"
location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv"
debug: true
alias: 'Pytest'
alias: "Pytest"
displayName: cache pytest dependencies

- bash: |
Expand All @@ -20,44 +24,44 @@ steps:
displayName: Setup pytests
# Smoketests
- ${{ if parameters.smoke_tests }}:
- template: "azure/components/aws-assume-role.yml@common"
parameters:
role: "auto-ops"
profile: "apm_ptl"
- template: "azure/components/get-aws-secrets-and-ssm-params.yml@common"
parameters:
secret_file_ids:
- ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY
- ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY
config_ids:
- /ptl/azure-devops/summary-care-record/int/CLIENT_ID_INT
secret_ids:
- ptl/azure-devops/summary-care-record/int/CLIENT_SECRET_INT
- bash: |
wait
sleep 350
export RELEASE_RELEASEID=$(Build.BuildId)
export SOURCE_COMMIT_ID=$(Build.SourceVersion)
export APIGEE_ENVIRONMENT="$(ENVIRONMENT)"
export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)"
export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)"
export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)"
export OAUTH_PROXY="oauth2"
export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk"
export APIGEE_API_TOKEN="$(secret.AccessToken)"
export REDIRECT_URI="https://example.org/callback"
export CLIENT_ID="$(CLIENT_ID_INT)"
export CLIENT_SECRET="$(CLIENT_SECRET_INT)"
export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)"
export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)"
export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH=""
export AUTHENTICATE_URL=""
make prod-smoketest
workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)
displayName: Run smoketests
- task: PublishTestResults@2
displayName: 'Publish smoketest results'
condition: always()
inputs:
testResultsFiles: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml'
failTaskOnFailedTests: true
- template: "azure/components/aws-assume-role.yml@common"
parameters:
role: "auto-ops"
profile: "apm_ptl"
- template: "azure/components/get-aws-secrets-and-ssm-params.yml@common"
parameters:
secret_file_ids:
- ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY
- ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY
config_ids:
- /ptl/azure-devops/summary-care-record/int/CLIENT_ID_INT
secret_ids:
- ptl/azure-devops/summary-care-record/int/CLIENT_SECRET_INT
- bash: |
wait
sleep 350
export RELEASE_RELEASEID=$(Build.BuildId)
export SOURCE_COMMIT_ID=$(Build.SourceVersion)
export APIGEE_ENVIRONMENT="$(ENVIRONMENT)"
export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)"
export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)"
export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)"
export OAUTH_PROXY="oauth2"
export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk"
export APIGEE_API_TOKEN="$(secret.AccessToken)"
export REDIRECT_URI="https://example.org/callback"
export CLIENT_ID="$(CLIENT_ID_INT)"
export CLIENT_SECRET="$(CLIENT_SECRET_INT)"
export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)"
export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)"
export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH=""
export AUTHENTICATE_URL=""
make prod-smoketest
workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)
displayName: Run smoketests
- task: PublishTestResults@2
displayName: "Publish smoketest results"
condition: always()
inputs:
testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml"
failTaskOnFailedTests: true
14 changes: 9 additions & 5 deletions azure/templates/run-tests-prod.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: "3.9"

- task: s3-cache-action@1
inputs:
key: 'poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock'
location: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv'
key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock"
location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv"
debug: true
alias: 'Pytest'
alias: "Pytest"
displayName: cache pytest dependencies

- bash: |
Expand Down Expand Up @@ -41,8 +45,8 @@ steps:
workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)
displayName: Run smoketests
- task: PublishTestResults@2
displayName: 'Publish smoketest results'
displayName: "Publish smoketest results"
condition: always()
inputs:
testResultsFiles: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml'
testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml"
failTaskOnFailedTests: true
92 changes: 48 additions & 44 deletions azure/templates/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ parameters:
default: false

steps:
- task: UsePythonVersion@0
inputs:
versionSpec: "3.9"

- task: s3-cache-action@1
inputs:
key: 'poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock'
location: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv'
key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock"
location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv"
debug: true
alias: 'Pytest'
alias: "Pytest"
displayName: cache pytest dependencies

- bash: |
Expand All @@ -20,44 +24,44 @@ steps:
displayName: Setup pytests
# Smoketests.
- ${{ if parameters.smoke_tests }}:
- template: "azure/components/aws-assume-role.yml@common"
parameters:
role: "auto-ops"
profile: "apm_ptl"
- template: "azure/components/get-aws-secrets-and-ssm-params.yml@common"
parameters:
secret_file_ids:
- ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY
- ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY
- ptl/azure-devops/summary-care-record/JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY
config_ids:
- /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_ID
- /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_SECRET
- bash: |
wait
sleep 350
export RELEASE_RELEASEID=$(Build.BuildId)
export SOURCE_COMMIT_ID=$(Build.SourceVersion)
export APIGEE_ENVIRONMENT="$(ENVIRONMENT)"
export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)"
export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)"
export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)"
export OAUTH_PROXY="oauth2-mock"
export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk"
export APIGEE_API_TOKEN="$(secret.AccessToken)"
export REDIRECT_URI="https://example.org/callback"
export CLIENT_ID="$(CLIENT_ID)"
export CLIENT_SECRET="$(CLIENT_SECRET)"
export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)"
export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY)"
export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)"
export AUTHENTICATE_URL="https://nhsd-apim-testing-internal-dev.herokuapp.com/"
make smoketest
workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)
displayName: Run smoketests
- task: PublishTestResults@2
displayName: 'Publish smoketest results'
condition: always()
inputs:
testResultsFiles: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml'
failTaskOnFailedTests: true
- template: "azure/components/aws-assume-role.yml@common"
parameters:
role: "auto-ops"
profile: "apm_ptl"
- template: "azure/components/get-aws-secrets-and-ssm-params.yml@common"
parameters:
secret_file_ids:
- ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY
- ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY
- ptl/azure-devops/summary-care-record/JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY
config_ids:
- /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_ID
- /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_SECRET
- bash: |
wait
sleep 350
export RELEASE_RELEASEID=$(Build.BuildId)
export SOURCE_COMMIT_ID=$(Build.SourceVersion)
export APIGEE_ENVIRONMENT="$(ENVIRONMENT)"
export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)"
export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)"
export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)"
export OAUTH_PROXY="oauth2-mock"
export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk"
export APIGEE_API_TOKEN="$(secret.AccessToken)"
export REDIRECT_URI="https://example.org/callback"
export CLIENT_ID="$(CLIENT_ID)"
export CLIENT_SECRET="$(CLIENT_SECRET)"
export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)"
export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY)"
export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)"
export AUTHENTICATE_URL="https://nhsd-apim-testing-internal-dev.herokuapp.com/"
make smoketest
workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)
displayName: Run smoketests
- task: PublishTestResults@2
displayName: "Publish smoketest results"
condition: always()
inputs:
testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml"
failTaskOnFailedTests: true
6 changes: 6 additions & 0 deletions docker/service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,16 @@ dependencies {
testImplementation "io.rest-assured:json-path:4.4.0"
testImplementation "io.rest-assured:xml-path:4.4.0"
testImplementation "com.github.tomakehurst:wiremock-jre8-standalone:2.31.0"
testImplementation(platform('org.junit:junit-bom:5.13.0'))
testImplementation('org.junit.jupiter:junit-jupiter')
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
}

test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}

sourceSets {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,20 @@ public void testSetAcsPermissionBadRequest(TestData testData) throws Exception {
.andExpect(content().json(testData.getFhirResponse()));
}

@ParameterizedTest(name = "[{index}] - {0}")
@ArgumentsSource(SetAcsBadRequest.class)
public void testSetAcsPermissionNoRoleCodeBadRequest(TestData testData) throws Exception {
// FLAGSAPI-1046 should return Bad Request if no role code is returned from SDS
// or Identity Service
stubFailedIdentityService();
stubFailedSdsService();
stubSpineAcsEndpoint(acsErrorResponse);

performRequest(testData.getFhirRequest())
.andExpect(status().isBadRequest())
.andExpect(content().json(testData.getFhirResponse()));
}

private ResultActions performRequest(String request) throws Exception {
return mockMvc.perform(post(ACS_ENDPOINT)
.contentType(APPLICATION_FHIR_JSON)
Expand Down Expand Up @@ -154,6 +168,17 @@ private void stubSdsService(Resource response) throws IOException {
.withBody(readString(response.getFile().toPath(), UTF_8))));
}

private void stubFailedSdsService() {
wireMockServer.stubFor(
WireMock.get(WireMock.urlPathEqualTo(PRACTITIONER_ROLE_ENDPOINT))
.withQueryParam(USER_ID_QUERY_PARAM,
containing(NHSD_SESSION_URID))
.willReturn(aResponse()
.withStatus(OK.value())
.withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.withBody("")));
}

private void stubIdentityService(Resource response) throws IOException {
wireMockServer.stubFor(
WireMock.get(USER_INFO_ENDPOINT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,21 @@ private Pair<String, String> getUserRoleCodeAndId(String authorisation, String n

}

String errorMessage = String.format("Unable to determine Job Role Code for "
+ "the given RoleID via the SDS Service: %s", nhsdSessionUrid);

// if we haven't retrieved a role code from openID we try the SDS service
try {
return Pair.of(sdsService.getUserRoleCode(nhsdSessionUrid), nhsdIdentity);
var roleCode = sdsService.getUserRoleCode(nhsdSessionUrid);
if (roleCode != null && !roleCode.isEmpty()) {
return Pair.of(roleCode, nhsdIdentity);
}
} catch (BadRequestException | URISyntaxException e) {
throw new BadRequestException(String.format("Unable to determine SDS Job Role Code for "
+ "the given RoleID: %s", nhsdSessionUrid));
LOGGER.info(errorMessage);
throw new BadRequestException(errorMessage);
}
LOGGER.info(errorMessage);
throw new BadRequestException(errorMessage);
}

private String prepareAcsRequest(ParametersParameterComponent parameter, RequestData requestData, String sdsJobRoleCode,
Expand Down
4 changes: 1 addition & 3 deletions specification/summary-care-record.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,8 @@ info:
- a health or care staff providing direct care to patients
- strongly authenticated, using either an [NHS smartcard or a modern alternative](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/nhs-smartcards-for-developers) available via [NHS Care Identity Service 2 (NHS CIS2)](https://digital.nhs.uk/services/nhs-identity)

The API uses OAuth 2.0 to authorise the calling system. It supports the following security patterns:

The API uses OAuth 2.0 to authorise the calling system. It only supports CIS2 combined authentication and authorisation (see link below). Do not use separate authentication and authorisation:
- [user-restricted RESTful API - using NHS CIS2 - combined authentication and authorisation](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/user-restricted-restful-apis-nhs-cis2-combined-authentication-and-authorisation)
- [user-restricted RESTful API - using NHS CIS2 - separate authentication and authorisation](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/user-restricted-restful-apis-nhs-cis2-separate-authentication-and-authorisation)

For more details, see [user-restricted APIs](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation#user-restricted-apis).

Expand Down