Compare commits
5 Commits
08d68f3a5d
...
62c8f94f11
Author | SHA1 | Date |
---|---|---|
James Agnew | 62c8f94f11 | |
Thomas Papke | 3f6d1eb29b | |
Tadgh | 377e44b6ca | |
James Agnew | 703924938f | |
James Agnew | 7ad2524272 |
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 6258
|
||||||
|
title: "The AuthorizationInterceptor handling for operations has been improved
|
||||||
|
so that operation rules now directly test the contents of response Bundle
|
||||||
|
or Parameters objects returned by the operation when configure to require
|
||||||
|
explicit response authorization. This fixes a regression in 7.4.0 where
|
||||||
|
operation responses could sometimes be denied even if appropriate
|
||||||
|
permissions were granted to view resources in a response bundle. Thanks to
|
||||||
|
Gijsbert van den Brink for reporting the issue with a sample test!"
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 6290
|
||||||
|
title: "Previously, a specific migration task was using the `TRIM()` function, which does not exist in MSSQL 2012. This was causing migrations targeting MSSQL 2012 to fail.
|
||||||
|
This has been corrected and replaced with usage of a combination of LTRIM() and RTRIM(). Thanks to Primož Delopst at Better for the contribution!"
|
|
@ -70,6 +70,11 @@
|
||||||
<artifactId>jakarta.servlet-api</artifactId>
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.mail</groupId>
|
||||||
|
<artifactId>jakarta.mail-api</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -28,8 +28,8 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import jakarta.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -17,8 +17,8 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import jakarta.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
|
@ -26,8 +26,8 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import jakarta.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -20,11 +20,15 @@ import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.gclient.IOperation;
|
||||||
|
import ca.uhn.fhir.rest.gclient.IOperationUnnamed;
|
||||||
|
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInput;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
|
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule;
|
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRuleBuilder;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRuleTester;
|
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRuleTester;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum;
|
import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder;
|
import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder;
|
||||||
|
@ -68,6 +72,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.junit.jupiter.params.provider.NullSource;
|
import org.junit.jupiter.params.provider.NullSource;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
@ -897,8 +902,17 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void testDiffOperation_AllowedByType_Instance() {
|
@CsvSource({
|
||||||
|
// ResourceId , RequireExplicitResponseAuthorization , ShouldSucceed
|
||||||
|
"Patient/A , true , true",
|
||||||
|
"Patient/A/_history/2 , true , true",
|
||||||
|
"Observation/B , true , false",
|
||||||
|
"Patient/A , false , true",
|
||||||
|
"Patient/A/_history/2 , false , true",
|
||||||
|
"Observation/B , false , true"
|
||||||
|
})
|
||||||
|
public void testDiffOperation_AllowedByType_Instance(String theResourceId, boolean theRequireExplicitResponseAuthorization, boolean theShouldSucceed) {
|
||||||
createPatient(withId("A"), withActiveTrue());
|
createPatient(withId("A"), withActiveTrue());
|
||||||
createPatient(withId("A"), withActiveFalse());
|
createPatient(withId("A"), withActiveFalse());
|
||||||
createObservation(withId("B"), withStatus("final"));
|
createObservation(withId("B"), withStatus("final"));
|
||||||
|
@ -906,30 +920,42 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
||||||
myServer.getRestfulServer().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
myServer.getRestfulServer().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||||
@Override
|
@Override
|
||||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||||
return new RuleBuilder()
|
RuleBuilder ruleBuilder = new RuleBuilder();
|
||||||
.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onAnyInstance().andAllowAllResponses().andThen()
|
if (theRequireExplicitResponseAuthorization) {
|
||||||
.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onServer().andAllowAllResponses().andThen()
|
ruleBuilder.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onAnyInstance().andRequireExplicitResponseAuthorization().andThen();
|
||||||
.allow().read().resourcesOfType(Patient.class).withAnyId().andThen()
|
ruleBuilder.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onServer().andRequireExplicitResponseAuthorization().andThen();
|
||||||
.denyAll()
|
} else {
|
||||||
.build();
|
ruleBuilder.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onAnyInstance().andAllowAllResponses().andThen();
|
||||||
|
ruleBuilder.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onServer().andAllowAllResponses().andThen();
|
||||||
|
}
|
||||||
|
ruleBuilder.allow().read().resourcesOfType(Patient.class).withAnyId().andThen();
|
||||||
|
ruleBuilder.denyAll();
|
||||||
|
return ruleBuilder.build();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Parameters diff;
|
Parameters diff;
|
||||||
|
|
||||||
diff = myClient.operation().onInstance("Patient/A").named(ProviderConstants.DIFF_OPERATION_NAME).withNoParameters(Parameters.class).execute();
|
IOperation operation = myClient.operation();
|
||||||
assertThat(diff.getParameter()).hasSize(1);
|
IOperationUnnamed target;
|
||||||
|
if (theResourceId.contains("_history")) {
|
||||||
diff = myClient.operation().onInstanceVersion(new IdType("Patient/A/_history/2")).named(ProviderConstants.DIFF_OPERATION_NAME).withNoParameters(Parameters.class).execute();
|
target = operation.onInstanceVersion(new IdType(theResourceId));
|
||||||
assertThat(diff.getParameter()).hasSize(1);
|
} else {
|
||||||
|
target = operation.onInstance(theResourceId);
|
||||||
try {
|
|
||||||
myClient.operation().onInstance("Observation/B").named(ProviderConstants.DIFF_OPERATION_NAME).withNoParameters(Parameters.class).execute();
|
|
||||||
fail();
|
|
||||||
} catch (ForbiddenOperationException e) {
|
|
||||||
// good
|
|
||||||
}
|
}
|
||||||
|
IOperationUntypedWithInput<Parameters> executable = target.named(ProviderConstants.DIFF_OPERATION_NAME).withNoParameters(Parameters.class);
|
||||||
|
|
||||||
|
if (theShouldSucceed) {
|
||||||
|
diff = executable.execute();
|
||||||
|
assertThat(diff.getParameter()).hasSize(1);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
executable.execute();
|
||||||
|
fail();
|
||||||
|
} catch (ForbiddenOperationException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -943,8 +969,8 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
||||||
@Override
|
@Override
|
||||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||||
return new RuleBuilder()
|
return new RuleBuilder()
|
||||||
.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onAnyInstance().andAllowAllResponses().andThen()
|
.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onAnyInstance().andRequireExplicitResponseAuthorization().andThen()
|
||||||
.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onServer().andAllowAllResponses().andThen()
|
.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onServer().andRequireExplicitResponseAuthorization().andThen()
|
||||||
.allow().read().resourcesOfType(Patient.class).withAnyId().andThen()
|
.allow().read().resourcesOfType(Patient.class).withAnyId().andThen()
|
||||||
.denyAll()
|
.denyAll()
|
||||||
.build();
|
.build();
|
||||||
|
@ -977,6 +1003,72 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDocumentOperation_withExplicitAuthorization() {
|
||||||
|
IIdType patientId = createPatient();
|
||||||
|
IIdType compositionId = createResource("Composition", withSubject(patientId));
|
||||||
|
|
||||||
|
AuthorizationInterceptor interceptor = new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||||
|
@Override
|
||||||
|
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||||
|
return new RuleBuilder()
|
||||||
|
.allow().read().instance(patientId).andThen()
|
||||||
|
.allow().operation().named("$document").onInstance(compositionId).andRequireExplicitResponseAuthorization().andThen()
|
||||||
|
.allow().read().instance(compositionId).andThen()
|
||||||
|
.denyAll()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
myServer.getRestfulServer().registerInterceptor(interceptor);
|
||||||
|
|
||||||
|
Bundle bundle = myClient.operation().onInstanceVersion(compositionId).named("$document").withNoParameters(Parameters.class).returnResourceType(Bundle.class).execute();
|
||||||
|
assertEquals(2, bundle.getEntry().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDocumentOperation_explicitAuthorizationNotNeeded() {
|
||||||
|
IIdType patientId = createPatient();
|
||||||
|
IIdType compositionId = createResource("Composition", withSubject(patientId));
|
||||||
|
|
||||||
|
AuthorizationInterceptor interceptor = new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||||
|
@Override
|
||||||
|
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||||
|
return new RuleBuilder()
|
||||||
|
.allow().operation().named("$document").onInstance(compositionId).andAllowAllResponses().andThen()
|
||||||
|
.denyAll()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
myServer.getRestfulServer().registerInterceptor(interceptor);
|
||||||
|
|
||||||
|
Bundle bundle = myClient.operation().onInstanceVersion(compositionId).named("$document").withNoParameters(Parameters.class).returnResourceType(Bundle.class).execute();
|
||||||
|
assertEquals(2, bundle.getEntry().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDocumentOperation_withoutExplicitAuthorization() {
|
||||||
|
IIdType patientId = createPatient();
|
||||||
|
IIdType compositionId = createResource("Composition", withSubject(patientId));
|
||||||
|
|
||||||
|
AuthorizationInterceptor interceptor = new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||||
|
@Override
|
||||||
|
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||||
|
return new RuleBuilder()
|
||||||
|
.allow().operation().named("$document").onInstance(compositionId).andRequireExplicitResponseAuthorization().andThen()
|
||||||
|
.allow().read().instance(compositionId).andThen()
|
||||||
|
.denyAll()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
myServer.getRestfulServer().registerInterceptor(interceptor);
|
||||||
|
|
||||||
|
try {
|
||||||
|
myClient.operation().onInstanceVersion(compositionId).named("$document").withNoParameters(Parameters.class).returnResourceType(Bundle.class).execute();
|
||||||
|
fail();
|
||||||
|
} catch (ForbiddenOperationException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGraphQL_AllowedByType_Instance() throws IOException {
|
public void testGraphQL_AllowedByType_Instance() throws IOException {
|
||||||
|
@ -1205,8 +1297,17 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
||||||
ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp));
|
ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void testOperationEverything_SomeIncludedResourcesNotAuthorized() {
|
@CsvSource({
|
||||||
|
// RequireExplicitResponseAuthorization , AddNotExplicitlyAuthorizedResources , ShouldSucceed
|
||||||
|
"true , false , true",
|
||||||
|
"true , true , false",
|
||||||
|
"false , false , true",
|
||||||
|
"false , true , true",
|
||||||
|
})
|
||||||
|
public void testOperationEverything_SomeIncludedResourcesNotAuthorized(boolean theRequireExplicitResponseAuthorization, boolean theAddNotExplicitlyAuthorizedResources, boolean theShouldSucceed) {
|
||||||
|
int expectedCount = 2;
|
||||||
|
|
||||||
Patient pt1 = new Patient();
|
Patient pt1 = new Patient();
|
||||||
pt1.setActive(true);
|
pt1.setActive(true);
|
||||||
final IIdType pid1 = myClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless();
|
final IIdType pid1 = myClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless();
|
||||||
|
@ -1216,45 +1317,57 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
||||||
obs1.setSubject(new Reference(pid1));
|
obs1.setSubject(new Reference(pid1));
|
||||||
myClient.create().resource(obs1).execute();
|
myClient.create().resource(obs1).execute();
|
||||||
|
|
||||||
|
if (theAddNotExplicitlyAuthorizedResources) {
|
||||||
|
// Add an Encounter, which will be returned by $everything but that hasn't been
|
||||||
|
// explicitly authorized
|
||||||
|
Encounter enc = new Encounter();
|
||||||
|
enc.setSubject(new Reference(pid1));
|
||||||
|
myClient.create().resource(enc).execute();
|
||||||
|
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setName("Hello");
|
||||||
|
IIdType orgId = myClient.create().resource(org).execute().getId().toUnqualifiedVersionless();
|
||||||
|
expectedCount++;
|
||||||
|
|
||||||
|
pt1.setId(pid1);
|
||||||
|
pt1.setManagingOrganization(new Reference(orgId));
|
||||||
|
myClient.update().resource(pt1).execute();
|
||||||
|
expectedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
myServer.getRestfulServer().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
myServer.getRestfulServer().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||||
return new RuleBuilder()
|
IAuthRuleBuilder ruleBuilder = new RuleBuilder();
|
||||||
.allow().operation().named(JpaConstants.OPERATION_EVERYTHING).onInstance(pid1).andRequireExplicitResponseAuthorization().andThen()
|
if (theRequireExplicitResponseAuthorization) {
|
||||||
.allow().read().resourcesOfType(Patient.class).inCompartment("Patient", pid1).andThen()
|
ruleBuilder.allow().operation().named(JpaConstants.OPERATION_EVERYTHING).onInstance(pid1).andRequireExplicitResponseAuthorization().andThen();
|
||||||
.allow().read().resourcesOfType(Observation.class).inCompartment("Patient", pid1).andThen()
|
} else {
|
||||||
.allow().create().resourcesOfType(Encounter.class).withAnyId().andThen()
|
ruleBuilder.allow().operation().named(JpaConstants.OPERATION_EVERYTHING).onInstance(pid1).andAllowAllResponsesWithAllResourcesAccess().andThen();
|
||||||
.build();
|
}
|
||||||
|
ruleBuilder.allow().read().resourcesOfType(Patient.class).inCompartment("Patient", pid1).andThen();
|
||||||
|
ruleBuilder.allow().read().resourcesOfType(Observation.class).inCompartment("Patient", pid1).andThen();
|
||||||
|
return ruleBuilder.build();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Bundle outcome = myClient
|
IOperationUntypedWithInput<Bundle> executable = myClient
|
||||||
.operation()
|
.operation()
|
||||||
.onInstance(pid1)
|
.onInstance(pid1)
|
||||||
.named(JpaConstants.OPERATION_EVERYTHING)
|
.named(JpaConstants.OPERATION_EVERYTHING)
|
||||||
.withNoParameters(Parameters.class)
|
.withNoParameters(Parameters.class)
|
||||||
.returnResourceType(Bundle.class)
|
.returnResourceType(Bundle.class);
|
||||||
.execute();
|
|
||||||
assertThat(outcome.getEntry()).hasSize(2);
|
|
||||||
|
|
||||||
// Add an Encounter, which will be returned by $everything but that hasn't been
|
if (theShouldSucceed) {
|
||||||
// explicitly authorized
|
Bundle outcome = executable.execute();
|
||||||
|
assertThat(outcome.getEntry()).hasSize(expectedCount);
|
||||||
Encounter enc = new Encounter();
|
} else {
|
||||||
enc.setSubject(new Reference(pid1));
|
try {
|
||||||
myClient.create().resource(enc).execute();
|
executable.execute();
|
||||||
|
fail();
|
||||||
try {
|
} catch (ForbiddenOperationException e) {
|
||||||
myClient
|
assertThat(e.getMessage()).contains("Access denied by default policy");
|
||||||
.operation()
|
}
|
||||||
.onInstance(pid1)
|
|
||||||
.named(JpaConstants.OPERATION_EVERYTHING)
|
|
||||||
.withNoParameters(Parameters.class)
|
|
||||||
.returnResourceType(Bundle.class)
|
|
||||||
.execute();
|
|
||||||
fail();
|
|
||||||
} catch (ForbiddenOperationException e) {
|
|
||||||
assertThat(e.getMessage()).contains("Access denied by default policy");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2029,6 +2142,7 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
|
||||||
super(theResourceTypes);
|
super(theResourceTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||||
List<IAuthRule> rules = new ArrayList<>(super.buildRuleList(theRequestDetails));
|
List<IAuthRule> rules = new ArrayList<>(super.buildRuleList(theRequestDetails));
|
||||||
List<IAuthRule> rulesToAdd = new RuleBuilder().allow().transaction().withAnyOperation().andApplyNormalRules().build();
|
List<IAuthRule> rulesToAdd = new RuleBuilder().allow().transaction().withAnyOperation().andApplyNormalRules().build();
|
||||||
|
|
|
@ -79,25 +79,11 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.simplejavamail</groupId>
|
<groupId>org.simplejavamail</groupId>
|
||||||
<artifactId>simple-java-mail</artifactId>
|
<artifactId>simple-java-mail</artifactId>
|
||||||
<!-- Excluded in favor of jakarta.activation:jakarta.activation-api -->
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>com.sun.activation</groupId>
|
|
||||||
<artifactId>jakarta.activation</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.icegreen</groupId>
|
<groupId>com.icegreen</groupId>
|
||||||
<artifactId>greenmail</artifactId>
|
<artifactId>greenmail</artifactId>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
<!-- Excluded in favor of jakarta.activation:jakarta.activation-api -->
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>com.sun.activation</groupId>
|
|
||||||
<artifactId>jakarta.activation</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,8 @@ public class AuthorizationInterceptor implements IRuleApplier {
|
||||||
rules.size(),
|
rules.size(),
|
||||||
getPointcutNameOrEmpty(thePointcut),
|
getPointcutNameOrEmpty(thePointcut),
|
||||||
getResourceTypeOrEmpty(theInputResource),
|
getResourceTypeOrEmpty(theInputResource),
|
||||||
getResourceTypeOrEmpty(theOutputResource));
|
getResourceTypeOrEmpty(theOutputResource),
|
||||||
|
thePointcut);
|
||||||
|
|
||||||
Verdict verdict = null;
|
Verdict verdict = null;
|
||||||
for (IAuthRule nextRule : rules) {
|
for (IAuthRule nextRule : rules) {
|
||||||
|
@ -528,7 +529,7 @@ public class AuthorizationInterceptor implements IRuleApplier {
|
||||||
case EXTENDED_OPERATION_TYPE:
|
case EXTENDED_OPERATION_TYPE:
|
||||||
case EXTENDED_OPERATION_INSTANCE: {
|
case EXTENDED_OPERATION_INSTANCE: {
|
||||||
if (theResponseObject != null) {
|
if (theResponseObject != null) {
|
||||||
resources = toListOfResourcesAndExcludeContainer(theResponseObject, fhirContext);
|
resources = toListOfResourcesAndExcludeContainerUnlessStandalone(theResponseObject, fhirContext);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -575,7 +576,7 @@ public class AuthorizationInterceptor implements IRuleApplier {
|
||||||
OUT,
|
OUT,
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static List<IBaseResource> toListOfResourcesAndExcludeContainer(
|
protected static List<IBaseResource> toListOfResourcesAndExcludeContainerUnlessStandalone(
|
||||||
IBaseResource theResponseObject, FhirContext fhirContext) {
|
IBaseResource theResponseObject, FhirContext fhirContext) {
|
||||||
if (theResponseObject == null) {
|
if (theResponseObject == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
@ -588,6 +589,13 @@ public class AuthorizationInterceptor implements IRuleApplier {
|
||||||
return Collections.singletonList(theResponseObject);
|
return Collections.singletonList(theResponseObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return toListOfResourcesAndExcludeContainer(theResponseObject, fhirContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static List<IBaseResource> toListOfResourcesAndExcludeContainer(
|
||||||
|
IBaseResource theResponseObject, FhirContext fhirContext) {
|
||||||
|
List<IBaseResource> retVal;
|
||||||
retVal = fhirContext.newTerser().getAllPopulatedChildElementsOfType(theResponseObject, IBaseResource.class);
|
retVal = fhirContext.newTerser().getAllPopulatedChildElementsOfType(theResponseObject, IBaseResource.class);
|
||||||
|
|
||||||
// Exclude the container
|
// Exclude the container
|
||||||
|
|
|
@ -27,11 +27,9 @@ public interface IAuthRuleBuilderOperationNamedAndScoped {
|
||||||
IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponses();
|
IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponses();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responses for this operation will not be checked and access to all resources is allowed. This
|
* @deprecated This is a synonym for {@link #andAllowAllResponses()}, use that method instead
|
||||||
* is intended for operations which are known to fetch a graph of resources that is known to be
|
|
||||||
* safe, such as `$everything` which may access and fetch resources outside the patient's compartment
|
|
||||||
* but enforces safety in what it fetches via strict SQL queries.
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "7.6.0")
|
||||||
IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponsesWithAllResourcesAccess();
|
IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponsesWithAllResourcesAccess();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -85,6 +85,7 @@ class OperationRule extends BaseRule implements IAuthRule {
|
||||||
myAppliesToTypes = theAppliesToTypes;
|
myAppliesToTypes = theAppliesToTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||||
@Override
|
@Override
|
||||||
public Verdict applyRule(
|
public Verdict applyRule(
|
||||||
RestOperationTypeEnum theOperation,
|
RestOperationTypeEnum theOperation,
|
||||||
|
@ -97,23 +98,8 @@ class OperationRule extends BaseRule implements IAuthRule {
|
||||||
Pointcut thePointcut) {
|
Pointcut thePointcut) {
|
||||||
FhirContext ctx = theRequestDetails.getServer().getFhirContext();
|
FhirContext ctx = theRequestDetails.getServer().getFhirContext();
|
||||||
|
|
||||||
// Operation rules apply to the execution of the operation itself, not to side effects like
|
|
||||||
// loading resources (that will presumably be reflected in the response). Those loads need
|
|
||||||
// to be explicitly authorized
|
|
||||||
if (!myAllowAllResourcesAccess && isResourceAccess(thePointcut)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean applies = false;
|
boolean applies = false;
|
||||||
switch (theOperation) {
|
switch (theOperation) {
|
||||||
case ADD_TAGS:
|
|
||||||
case DELETE_TAGS:
|
|
||||||
case GET_TAGS:
|
|
||||||
case GET_PAGE:
|
|
||||||
case GRAPHQL_REQUEST:
|
|
||||||
// These things can't be tracked by the AuthorizationInterceptor
|
|
||||||
// at this time
|
|
||||||
return null;
|
|
||||||
case EXTENDED_OPERATION_SERVER:
|
case EXTENDED_OPERATION_SERVER:
|
||||||
if (myAppliesToServer || myAppliesAtAnyLevel) {
|
if (myAppliesToServer || myAppliesAtAnyLevel) {
|
||||||
applies = true;
|
applies = true;
|
||||||
|
@ -138,10 +124,7 @@ class OperationRule extends BaseRule implements IAuthRule {
|
||||||
applies = true;
|
applies = true;
|
||||||
} else {
|
} else {
|
||||||
IIdType requestResourceId = null;
|
IIdType requestResourceId = null;
|
||||||
if (theInputResourceId != null) {
|
if (theRequestDetails.getId() != null) {
|
||||||
requestResourceId = theInputResourceId;
|
|
||||||
}
|
|
||||||
if (requestResourceId == null && myAllowAllResponses) {
|
|
||||||
requestResourceId = theRequestDetails.getId();
|
requestResourceId = theRequestDetails.getId();
|
||||||
}
|
}
|
||||||
if (requestResourceId != null) {
|
if (requestResourceId != null) {
|
||||||
|
@ -168,40 +151,6 @@ class OperationRule extends BaseRule implements IAuthRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CREATE:
|
|
||||||
break;
|
|
||||||
case DELETE:
|
|
||||||
break;
|
|
||||||
case HISTORY_INSTANCE:
|
|
||||||
break;
|
|
||||||
case HISTORY_SYSTEM:
|
|
||||||
break;
|
|
||||||
case HISTORY_TYPE:
|
|
||||||
break;
|
|
||||||
case READ:
|
|
||||||
break;
|
|
||||||
case SEARCH_SYSTEM:
|
|
||||||
break;
|
|
||||||
case SEARCH_TYPE:
|
|
||||||
break;
|
|
||||||
case TRANSACTION:
|
|
||||||
break;
|
|
||||||
case UPDATE:
|
|
||||||
break;
|
|
||||||
case VALIDATE:
|
|
||||||
break;
|
|
||||||
case VREAD:
|
|
||||||
break;
|
|
||||||
case METADATA:
|
|
||||||
break;
|
|
||||||
case META_ADD:
|
|
||||||
break;
|
|
||||||
case META:
|
|
||||||
break;
|
|
||||||
case META_DELETE:
|
|
||||||
break;
|
|
||||||
case PATCH:
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -214,13 +163,33 @@ class OperationRule extends BaseRule implements IAuthRule {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return newVerdict(
|
if (theOutputResource == null) {
|
||||||
theOperation,
|
// This is the request part
|
||||||
theRequestDetails,
|
return newVerdict(
|
||||||
theInputResource,
|
theOperation,
|
||||||
theInputResourceId,
|
theRequestDetails,
|
||||||
theOutputResource,
|
theInputResource,
|
||||||
theRuleApplier);
|
theInputResourceId,
|
||||||
|
theOutputResource,
|
||||||
|
theRuleApplier);
|
||||||
|
} else {
|
||||||
|
// This is the response part, so we might want to check all of the
|
||||||
|
// resources in the response
|
||||||
|
if (myAllowAllResponses) {
|
||||||
|
return newVerdict(
|
||||||
|
theOperation,
|
||||||
|
theRequestDetails,
|
||||||
|
theInputResource,
|
||||||
|
theInputResourceId,
|
||||||
|
theOutputResource,
|
||||||
|
theRuleApplier);
|
||||||
|
} else {
|
||||||
|
List<IBaseResource> outputResources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainer(
|
||||||
|
theOutputResource, theRequestDetails.getFhirContext());
|
||||||
|
return RuleImplOp.applyRulesToResponseResources(
|
||||||
|
theRequestDetails, theRuleApplier, thePointcut, outputResources);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -828,31 +828,48 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
||||||
|
|
||||||
return verdict;
|
return verdict;
|
||||||
} else if (theOutputResource != null) {
|
} else if (theOutputResource != null) {
|
||||||
|
return applyRulesToResponseBundle(theRequestDetails, theOutputResource, theRuleApplier, thePointcut);
|
||||||
List<IBaseResource> outputResources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainer(
|
|
||||||
theOutputResource, theRequestDetails.getFhirContext());
|
|
||||||
|
|
||||||
Verdict verdict = null;
|
|
||||||
for (IBaseResource nextResource : outputResources) {
|
|
||||||
if (nextResource == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(
|
|
||||||
RestOperationTypeEnum.READ, theRequestDetails, null, null, nextResource, thePointcut);
|
|
||||||
if (newVerdict == null) {
|
|
||||||
continue;
|
|
||||||
} else if (verdict == null) {
|
|
||||||
verdict = newVerdict;
|
|
||||||
} else if (verdict.getDecision() == PolicyEnum.ALLOW && newVerdict.getDecision() == PolicyEnum.DENY) {
|
|
||||||
verdict = newVerdict;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return verdict;
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Verdict applyRulesToResponseBundle(
|
||||||
|
RequestDetails theRequestDetails,
|
||||||
|
IBaseResource theOutputResource,
|
||||||
|
IRuleApplier theRuleApplier,
|
||||||
|
Pointcut thePointcut) {
|
||||||
|
List<IBaseResource> outputResources =
|
||||||
|
AuthorizationInterceptor.toListOfResourcesAndExcludeContainerUnlessStandalone(
|
||||||
|
theOutputResource, theRequestDetails.getFhirContext());
|
||||||
|
return applyRulesToResponseResources(theRequestDetails, theRuleApplier, thePointcut, outputResources);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Verdict applyRulesToResponseResources(
|
||||||
|
RequestDetails theRequestDetails,
|
||||||
|
IRuleApplier theRuleApplier,
|
||||||
|
Pointcut thePointcut,
|
||||||
|
List<IBaseResource> outputResources) {
|
||||||
|
Verdict verdict = null;
|
||||||
|
for (IBaseResource nextResource : outputResources) {
|
||||||
|
if (nextResource == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(
|
||||||
|
RestOperationTypeEnum.READ, theRequestDetails, null, null, nextResource, thePointcut);
|
||||||
|
if (newVerdict == null) {
|
||||||
|
continue;
|
||||||
|
} else if (verdict == null) {
|
||||||
|
verdict = newVerdict;
|
||||||
|
} else if (verdict.getDecision() == PolicyEnum.ALLOW && newVerdict.getDecision() == PolicyEnum.DENY) {
|
||||||
|
verdict = newVerdict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return verdict;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isInvalidNestedBundleRequest(BundleEntryParts theEntry) {
|
private boolean isInvalidNestedBundleRequest(BundleEntryParts theEntry) {
|
||||||
IBaseResource resource = theEntry.getResource();
|
IBaseResource resource = theEntry.getResource();
|
||||||
if (!(resource instanceof IBaseBundle)) {
|
if (!(resource instanceof IBaseBundle)) {
|
||||||
|
|
|
@ -21,9 +21,9 @@ package ca.uhn.fhir.rest.server.mail;
|
||||||
|
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
import org.simplejavamail.api.email.Email;
|
import org.simplejavamail.api.email.Email;
|
||||||
import org.simplejavamail.api.mailer.AsyncResponse;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public interface IMailSvc {
|
public interface IMailSvc {
|
||||||
void sendMail(@Nonnull List<Email> theEmails);
|
void sendMail(@Nonnull List<Email> theEmails);
|
||||||
|
@ -31,7 +31,5 @@ public interface IMailSvc {
|
||||||
void sendMail(@Nonnull Email theEmail);
|
void sendMail(@Nonnull Email theEmail);
|
||||||
|
|
||||||
void sendMail(
|
void sendMail(
|
||||||
@Nonnull Email theEmail,
|
@Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull Consumer<Throwable> theErrorHandler);
|
||||||
@Nonnull Runnable theOnSuccess,
|
|
||||||
@Nonnull AsyncResponse.ExceptionConsumer theErrorHandler);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,9 @@
|
||||||
package ca.uhn.fhir.rest.server.mail;
|
package ca.uhn.fhir.rest.server.mail;
|
||||||
|
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.simplejavamail.MailException;
|
import org.simplejavamail.MailException;
|
||||||
import org.simplejavamail.api.email.Email;
|
import org.simplejavamail.api.email.Email;
|
||||||
import org.simplejavamail.api.email.Recipient;
|
import org.simplejavamail.api.email.Recipient;
|
||||||
import org.simplejavamail.api.mailer.AsyncResponse;
|
|
||||||
import org.simplejavamail.api.mailer.AsyncResponse.ExceptionConsumer;
|
|
||||||
import org.simplejavamail.api.mailer.Mailer;
|
import org.simplejavamail.api.mailer.Mailer;
|
||||||
import org.simplejavamail.api.mailer.config.TransportStrategy;
|
import org.simplejavamail.api.mailer.config.TransportStrategy;
|
||||||
import org.simplejavamail.mailer.MailerBuilder;
|
import org.simplejavamail.mailer.MailerBuilder;
|
||||||
|
@ -33,6 +30,8 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class MailSvc implements IMailSvc {
|
public class MailSvc implements IMailSvc {
|
||||||
|
@ -42,14 +41,14 @@ public class MailSvc implements IMailSvc {
|
||||||
private final Mailer myMailer;
|
private final Mailer myMailer;
|
||||||
|
|
||||||
public MailSvc(@Nonnull MailConfig theMailConfig) {
|
public MailSvc(@Nonnull MailConfig theMailConfig) {
|
||||||
Validate.notNull(theMailConfig);
|
Objects.requireNonNull(theMailConfig);
|
||||||
myMailConfig = theMailConfig;
|
myMailConfig = theMailConfig;
|
||||||
myMailer = makeMailer(myMailConfig);
|
myMailer = makeMailer(myMailConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMail(@Nonnull List<Email> theEmails) {
|
public void sendMail(@Nonnull List<Email> theEmails) {
|
||||||
Validate.notNull(theEmails);
|
Objects.requireNonNull(theEmails);
|
||||||
theEmails.forEach(theEmail -> send(theEmail, new OnSuccess(theEmail), new ErrorHandler(theEmail)));
|
theEmails.forEach(theEmail -> send(theEmail, new OnSuccess(theEmail), new ErrorHandler(theEmail)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,21 +59,23 @@ public class MailSvc implements IMailSvc {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMail(
|
public void sendMail(
|
||||||
@Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull ExceptionConsumer theErrorHandler) {
|
@Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull Consumer<Throwable> theErrorHandler) {
|
||||||
send(theEmail, theOnSuccess, theErrorHandler);
|
send(theEmail, theOnSuccess, theErrorHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void send(
|
private void send(
|
||||||
@Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull ExceptionConsumer theErrorHandler) {
|
@Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull Consumer<Throwable> theErrorHandler) {
|
||||||
Validate.notNull(theEmail);
|
Objects.requireNonNull(theEmail);
|
||||||
Validate.notNull(theOnSuccess);
|
Objects.requireNonNull(theOnSuccess);
|
||||||
Validate.notNull(theErrorHandler);
|
Objects.requireNonNull(theErrorHandler);
|
||||||
try {
|
try {
|
||||||
final AsyncResponse asyncResponse = myMailer.sendMail(theEmail, true);
|
myMailer.sendMail(theEmail, true).whenComplete((result, ex) -> {
|
||||||
if (asyncResponse != null) {
|
if (ex != null) {
|
||||||
asyncResponse.onSuccess(theOnSuccess);
|
theErrorHandler.accept(ex);
|
||||||
asyncResponse.onException(theErrorHandler);
|
} else {
|
||||||
}
|
theOnSuccess.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (MailException e) {
|
} catch (MailException e) {
|
||||||
theErrorHandler.accept(e);
|
theErrorHandler.accept(e);
|
||||||
}
|
}
|
||||||
|
@ -117,7 +118,7 @@ public class MailSvc implements IMailSvc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ErrorHandler implements ExceptionConsumer {
|
private class ErrorHandler implements Consumer<Throwable> {
|
||||||
private final Email myEmail;
|
private final Email myEmail;
|
||||||
|
|
||||||
private ErrorHandler(@Nonnull Email theEmail) {
|
private ErrorHandler(@Nonnull Email theEmail) {
|
||||||
|
@ -125,7 +126,7 @@ public class MailSvc implements IMailSvc {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(Exception t) {
|
public void accept(Throwable t) {
|
||||||
ourLog.error("Email not sent" + makeMessage(myEmail), t);
|
ourLog.error("Email not sent" + makeMessage(myEmail), t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import com.icegreen.greenmail.junit5.GreenMailExtension;
|
||||||
import com.icegreen.greenmail.util.GreenMailUtil;
|
import com.icegreen.greenmail.util.GreenMailUtil;
|
||||||
import com.icegreen.greenmail.util.ServerSetupTest;
|
import com.icegreen.greenmail.util.ServerSetupTest;
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
@ -11,7 +12,6 @@ import org.simplejavamail.MailException;
|
||||||
import org.simplejavamail.api.email.Email;
|
import org.simplejavamail.api.email.Email;
|
||||||
import org.simplejavamail.email.EmailBuilder;
|
import org.simplejavamail.email.EmailBuilder;
|
||||||
|
|
||||||
import javax.mail.internet.MimeMessage;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -86,13 +86,14 @@ public class MailSvcIT {
|
||||||
@Test
|
@Test
|
||||||
public void testSendMailWithInvalidToAddressExpectErrorHandler() {
|
public void testSendMailWithInvalidToAddressExpectErrorHandler() {
|
||||||
// setup
|
// setup
|
||||||
final Email email = withEmail("xyz");
|
String invalidEmailAdress = "xyz";
|
||||||
|
final Email email = withEmail(invalidEmailAdress);
|
||||||
// execute
|
// execute
|
||||||
fixture.sendMail(email,
|
fixture.sendMail(email,
|
||||||
() -> fail("Should not execute on Success"),
|
() -> fail("Should not execute on Success"),
|
||||||
(e) -> {
|
(e) -> {
|
||||||
assertTrue(e instanceof MailException);
|
assertTrue(e instanceof MailException);
|
||||||
assertEquals("Invalid TO address: " + email, e.getMessage());
|
assertEquals("Invalid TO address: " + invalidEmailAdress, e.getMessage());
|
||||||
});
|
});
|
||||||
// validate
|
// validate
|
||||||
assertTrue(ourGreenMail.waitForIncomingEmail(1000, 0));
|
assertTrue(ourGreenMail.waitForIncomingEmail(1000, 0));
|
||||||
|
|
|
@ -4230,7 +4230,7 @@ public class AuthorizationInterceptorR4Test extends BaseValidationTestWithInline
|
||||||
RequestDetails requestDetails = new SystemRequestDetails();
|
RequestDetails requestDetails = new SystemRequestDetails();
|
||||||
requestDetails.setResourceName("Bundle");
|
requestDetails.setResourceName("Bundle");
|
||||||
|
|
||||||
List<IBaseResource> resources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainer(searchSet, ourCtx);
|
List<IBaseResource> resources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainerUnlessStandalone(searchSet, ourCtx);
|
||||||
assertEquals(1, resources.size());
|
assertEquals(1, resources.size());
|
||||||
assertTrue(resources.contains(bundle));
|
assertTrue(resources.contains(bundle));
|
||||||
}
|
}
|
||||||
|
@ -4247,7 +4247,7 @@ public class AuthorizationInterceptorR4Test extends BaseValidationTestWithInline
|
||||||
RequestDetails requestDetails = new SystemRequestDetails();
|
RequestDetails requestDetails = new SystemRequestDetails();
|
||||||
requestDetails.setResourceName("Patient");
|
requestDetails.setResourceName("Patient");
|
||||||
|
|
||||||
List<IBaseResource> resources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainer(searchSet, ourCtx);
|
List<IBaseResource> resources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainerUnlessStandalone(searchSet, ourCtx);
|
||||||
assertEquals(2, resources.size());
|
assertEquals(2, resources.size());
|
||||||
assertTrue(resources.contains(patient1));
|
assertTrue(resources.contains(patient1));
|
||||||
assertTrue(resources.contains(patient2));
|
assertTrue(resources.contains(patient2));
|
||||||
|
|
34
pom.xml
34
pom.xml
|
@ -869,6 +869,7 @@
|
||||||
<developer>
|
<developer>
|
||||||
<id>delopst</id>
|
<id>delopst</id>
|
||||||
<name>Primož Delopst</name>
|
<name>Primož Delopst</name>
|
||||||
|
<organization>Better</organization>
|
||||||
</developer>
|
</developer>
|
||||||
<developer>
|
<developer>
|
||||||
<id>Zach Smith</id>
|
<id>Zach Smith</id>
|
||||||
|
@ -1160,27 +1161,38 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.simplejavamail</groupId>
|
<groupId>org.simplejavamail</groupId>
|
||||||
<artifactId>simple-java-mail</artifactId>
|
<artifactId>simple-java-mail</artifactId>
|
||||||
<version>6.6.1</version>
|
<version>8.11.2</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>com.sun.activation</groupId>
|
<groupId>com.github.bbottema</groupId>
|
||||||
<artifactId>jakarta.activation-api</artifactId>
|
<artifactId>jetbrains-runtime-annotations</artifactId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>com.sun.activation</groupId>
|
<groupId>jakarta.mail</groupId>
|
||||||
<artifactId>jakarta.activation</artifactId>
|
<artifactId>jakarta.mail-api</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.mail</groupId>
|
||||||
|
<artifactId>jakarta.mail-api</artifactId>
|
||||||
|
<version>2.1.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.icegreen</groupId>
|
||||||
|
<artifactId>greenmail</artifactId>
|
||||||
|
<version>2.1.0-rc-1</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>jakarta.mail</groupId>
|
||||||
|
<artifactId>jakarta.mail-api</artifactId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.icegreen</groupId>
|
|
||||||
<artifactId>greenmail</artifactId>
|
|
||||||
<version>1.6.4</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.icegreen</groupId>
|
<groupId>com.icegreen</groupId>
|
||||||
<artifactId>greenmail-junit5</artifactId>
|
<artifactId>greenmail-junit5</artifactId>
|
||||||
<version>1.6.4</version>
|
<version>2.1.0-rc-1</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- mail end -->
|
<!-- mail end -->
|
||||||
|
|
Loading…
Reference in New Issue