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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import dev.dsf.common.auth.conf.IdentityProvider;
import dev.dsf.common.auth.conf.PractitionerIdentityImpl;
import dev.dsf.common.auth.conf.RoleConfig;
import dev.dsf.common.auth.conf.X509CertificateWrapper;

public class IdentityProviderImpl extends AbstractIdentityProvider<BpeServerRole>
implements IdentityProvider, InitializingBean
Expand Down Expand Up @@ -68,9 +69,9 @@ public Identity getIdentity(X509Certificate[] certificates)
if (certificates == null || certificates.length == 0)
return null;

String thumbprint = getThumbprint(certificates[0]);
X509CertificateWrapper certWrapper = new X509CertificateWrapper(certificates[0]);

Optional<Practitioner> practitioner = toPractitioner(certificates[0]);
Optional<Practitioner> practitioner = toPractitioner(certWrapper);
Optional<Organization> localOrganization = organizationAndEndpointProvider.getLocalOrganization();
Optional<Endpoint> localEndpoint = organizationAndEndpointProvider.getLocalEndpoint();
if (practitioner.isPresent() && localOrganization.isPresent() && localEndpoint.isPresent())
Expand All @@ -79,14 +80,14 @@ public Identity getIdentity(X509Certificate[] certificates)
Organization o = localOrganization.get();
Endpoint e = localEndpoint.get();

return new PractitionerIdentityImpl(o, e, getDsfRolesFor(p, thumbprint, null, null), certificates[0], p,
getPractitionerRolesFor(p, thumbprint, null, null), null);
return new PractitionerIdentityImpl(o, e, getDsfRolesFor(p, certWrapper.thumbprint(), null, null),
certWrapper, p, getPractitionerRolesFor(p, certWrapper.thumbprint(), null, null), null);
}
else
{
logger.warn(
"Certificate with thumbprint '{}' for '{}' unknown, not configured as local user or local organization unknown",
thumbprint, getDn(certificates[0]));
certWrapper.thumbprint(), certWrapper.subjectDn());
return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package dev.dsf.common.auth.conf;

import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
Expand All @@ -35,7 +34,7 @@ public abstract class AbstractIdentity implements Identity
private final Organization organization;
private final Endpoint endpoint;
private final Set<DsfRole> dsfRoles = new HashSet<>();
private final X509Certificate certificate;
private final X509CertificateWrapper certificate;

/**
* @param localIdentity
Expand All @@ -50,7 +49,7 @@ public abstract class AbstractIdentity implements Identity
* may be <code>null</code>
*/
public AbstractIdentity(boolean localIdentity, Organization organization, Endpoint endpoint,
Collection<? extends DsfRole> dsfRoles, X509Certificate certificate)
Collection<? extends DsfRole> dsfRoles, X509CertificateWrapper certificate)
{
this.localIdentity = localIdentity;
this.organization = Objects.requireNonNull(organization, "organization");
Expand Down Expand Up @@ -106,7 +105,7 @@ public boolean hasDsfRole(DsfRole dsfRole)
}

@Override
public Optional<X509Certificate> getCertificate()
public Optional<X509CertificateWrapper> getCertificate()
{
// null if login via OIDC
return Optional.ofNullable(certificate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

import java.net.URI;
import java.net.URISyntaxException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
Expand All @@ -31,9 +29,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.security.auth.x500.X500Principal;

import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.X500Name;
Expand Down Expand Up @@ -141,24 +136,6 @@ public final Identity getIdentity(DsfOpenIdCredentials credentials)

protected abstract Optional<Endpoint> getLocalEndpoint();

protected final String getThumbprint(X509Certificate certificate)
{
try
{
byte[] digest = MessageDigest.getInstance("SHA-512").digest(certificate.getEncoded());
return Hex.encodeHexString(digest);
}
catch (CertificateEncodingException | NoSuchAlgorithmException e)
{
throw new RuntimeException(e);
}
}

protected final String getDn(X509Certificate certificate)
{
return certificate.getSubjectX500Principal().getName(X500Principal.RFC1779);
}

protected final List<String> getGroupsFromTokens(Map<String, Object> parsedIdToken,
Map<String, Object> parsedAccessToken)
{
Expand Down Expand Up @@ -295,16 +272,16 @@ private String toEmail(String iss, String sub)
return sub + "." + getHost(iss) + "@oidc.invalid";
}

protected final Optional<Practitioner> toPractitioner(X509Certificate certificate)
protected final Optional<Practitioner> toPractitioner(X509CertificateWrapper certWrapper)
{
if (certificate == null)
if (certWrapper == null)
return Optional.empty();

String thumbprint = getThumbprint(certificate);
if (!thumbprints.contains(thumbprint))
if (!thumbprints.contains(certWrapper.thumbprint()))
return Optional.empty();

return toJcaX509CertificateHolder(certificate).flatMap(ch -> toPractitioner(ch, thumbprint));
return toJcaX509CertificateHolder(certWrapper.certificate())
.flatMap(ch -> toPractitioner(ch, certWrapper.thumbprint()));
}

private Optional<JcaX509CertificateHolder> toJcaX509CertificateHolder(X509Certificate certificate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package dev.dsf.common.auth.conf;

import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.Optional;
import java.util.Set;

Expand Down Expand Up @@ -44,7 +43,7 @@ public interface Identity extends Principal
/**
* @return {@link Optional#empty()} if login via OIDC
*/
Optional<X509Certificate> getCertificate();
Optional<X509CertificateWrapper> getCertificate();

String getDisplayName();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package dev.dsf.common.auth.conf;

import java.security.cert.X509Certificate;
import java.util.Collection;

import org.hl7.fhir.r4.model.Endpoint;
Expand All @@ -37,7 +36,7 @@ public class OrganizationIdentityImpl extends AbstractIdentity implements Organi
* may be <code>null</code>
*/
public OrganizationIdentityImpl(boolean localIdentity, Organization organization, Endpoint endpoint,
Collection<? extends DsfRole> dsfRoles, X509Certificate certificate)
Collection<? extends DsfRole> dsfRoles, X509CertificateWrapper certificate)
{
super(localIdentity, organization, endpoint, dsfRoles, certificate);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package dev.dsf.common.auth.conf;

import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
Expand Down Expand Up @@ -55,7 +54,7 @@ public class PractitionerIdentityImpl extends AbstractIdentity implements Practi
* may be <code>null</code>
*/
public PractitionerIdentityImpl(Organization organization, Endpoint endpoint,
Collection<? extends DsfRole> dsfRoles, X509Certificate certificate, Practitioner practitioner,
Collection<? extends DsfRole> dsfRoles, X509CertificateWrapper certificate, Practitioner practitioner,
Collection<? extends Coding> practitionerRoles, DsfOpenIdCredentials credentials)
{
super(true, organization, endpoint, dsfRoles, certificate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.dsf.fhir.authentication;
package dev.dsf.common.auth.conf;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import org.apache.commons.codec.binary.Hex;
import org.hl7.fhir.r4.model.Identifier;
import org.springframework.beans.factory.InitializingBean;
import javax.security.auth.x500.X500Principal;

import dev.dsf.fhir.help.ExceptionHandler;
import org.apache.commons.codec.binary.Hex;

public abstract class AbstractProvider implements InitializingBean
public record X509CertificateWrapper(X509Certificate certificate, String thumbprint, String subjectDn)
{
protected final ExceptionHandler exceptionHandler;

public AbstractProvider(ExceptionHandler exceptionHandler)
{
this.exceptionHandler = exceptionHandler;
}

@Override
public void afterPropertiesSet() throws Exception
public X509CertificateWrapper(X509Certificate certificate)
{
Objects.requireNonNull(exceptionHandler, "exceptionHandler");
this(certificate, getThumbprint(certificate), getSubjectDn(certificate));
}

protected final Optional<String> getIdentifierValue(List<Identifier> identifiers, String system)
{
return identifiers.stream().filter(Identifier::hasSystem).filter(Identifier::hasValue)
.filter(i -> system.equals(i.getSystem())).map(Identifier::getValue).findFirst();
}

protected final String getThumbprint(X509Certificate certificate)
private static String getThumbprint(X509Certificate certificate)
{
try
{
Expand All @@ -62,4 +43,9 @@ protected final String getThumbprint(X509Certificate certificate)
throw new RuntimeException(e);
}
}

private static String getSubjectDn(X509Certificate certificate)
{
return certificate.getSubjectX500Principal().getName(X500Principal.RFC1779);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,17 @@
*/
package dev.dsf.common.auth.logging;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.stream.Collectors;

import javax.security.auth.x500.X500Principal;

import org.apache.commons.codec.binary.Hex;
import org.slf4j.MDC;

import dev.dsf.common.auth.DsfOpenIdCredentials;
import dev.dsf.common.auth.conf.DsfRole;
import dev.dsf.common.auth.conf.Identity;
import dev.dsf.common.auth.conf.OrganizationIdentity;
import dev.dsf.common.auth.conf.PractitionerIdentity;
import dev.dsf.common.auth.conf.X509CertificateWrapper;

public class CurrentUserMdcLogger extends AbstractUserLogger
{
Expand All @@ -56,8 +50,9 @@ protected void before(OrganizationIdentity organization)
{
before((Identity) organization);

organization.getCertificate().map(this::getThumbprint).ifPresent(t -> MDC.put(DSF_ORGANIZATION_THUMBPRINT, t));
organization.getCertificate().map(X509Certificate::getSubjectX500Principal).map(X500Principal::getName)
organization.getCertificate().map(X509CertificateWrapper::thumbprint)
.ifPresent(t -> MDC.put(DSF_ORGANIZATION_THUMBPRINT, t));
organization.getCertificate().map(X509CertificateWrapper::subjectDn)
.ifPresent(d -> MDC.put(DSF_ORGANIZATION_DN, d));

organization.getOrganizationIdentifierValue().ifPresent(i -> MDC.put(DSF_ORGANIZATION_IDENTIFIER, i));
Expand All @@ -69,8 +64,9 @@ protected void before(PractitionerIdentity practitioner)
{
before((Identity) practitioner);

practitioner.getCertificate().map(this::getThumbprint).ifPresent(t -> MDC.put(DSF_PRACTITIONER_THUMBPRINT, t));
practitioner.getCertificate().map(X509Certificate::getSubjectX500Principal).map(X500Principal::getName)
practitioner.getCertificate().map(X509CertificateWrapper::thumbprint)
.ifPresent(t -> MDC.put(DSF_PRACTITIONER_THUMBPRINT, t));
practitioner.getCertificate().map(X509CertificateWrapper::subjectDn)
.ifPresent(d -> MDC.put(DSF_PRACTITIONER_DN, d));
practitioner.getCredentials().map(DsfOpenIdCredentials::getUserId)
.ifPresent(i -> MDC.put(DSF_PRACTITIONER_SUB, i));
Expand All @@ -91,19 +87,6 @@ private void before(Identity identity)
identity.getDsfRoles().stream().map(DsfRole::name).collect(Collectors.joining(", ", "[", "]")));
}

private String getThumbprint(X509Certificate certificate)
{
try
{
byte[] digest = MessageDigest.getInstance("SHA-512").digest(certificate.getEncoded());
return Hex.encodeHexString(digest);
}
catch (CertificateEncodingException | NoSuchAlgorithmException e)
{
throw new RuntimeException(e);
}
}

@Override
protected void before(Principal principal)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package dev.dsf.fhir.authentication;

import java.security.cert.X509Certificate;
import java.util.Optional;

import org.hl7.fhir.r4.model.Endpoint;
Expand All @@ -29,5 +28,5 @@ public interface EndpointProvider

Optional<String> getLocalEndpointIdentifierValue();

Optional<Endpoint> getEndpoint(Organization organization, X509Certificate x509Certificate);
Optional<Endpoint> getEndpoint(Organization organization, String thumbprint);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,37 @@
*/
package dev.dsf.fhir.authentication;

import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

import org.hl7.fhir.r4.model.Endpoint;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Reference;
import org.springframework.beans.factory.InitializingBean;

import dev.dsf.fhir.dao.EndpointDao;
import dev.dsf.fhir.help.ExceptionHandler;

public class EndpointProviderImpl extends AbstractProvider implements EndpointProvider, InitializingBean
public class EndpointProviderImpl implements EndpointProvider, InitializingBean
{
private final ExceptionHandler exceptionHandler;
private final EndpointDao dao;
private final String serverBaseUrl;

public EndpointProviderImpl(ExceptionHandler exceptionHandler, EndpointDao dao, String serverBaseUrl)
{
super(exceptionHandler);

this.exceptionHandler = exceptionHandler;
this.dao = dao;
this.serverBaseUrl = serverBaseUrl;
}

@Override
public void afterPropertiesSet() throws Exception
{
super.afterPropertiesSet();

Objects.requireNonNull(exceptionHandler, "exceptionHandler");
Objects.requireNonNull(dao, "dao");
Objects.requireNonNull(serverBaseUrl, "serverBaseUrl");
}
Expand All @@ -65,11 +64,15 @@ public Optional<String> getLocalEndpointIdentifierValue()
.flatMap(ids -> getIdentifierValue(ids, ENDPOINT_IDENTIFIER_SYSTEM));
}

@Override
public Optional<Endpoint> getEndpoint(Organization organization, X509Certificate x509Certificate)
private Optional<String> getIdentifierValue(List<Identifier> identifiers, String system)
{
String thumbprint = getThumbprint(x509Certificate);
return identifiers.stream().filter(Identifier::hasSystem).filter(Identifier::hasValue)
.filter(i -> system.equals(i.getSystem())).map(Identifier::getValue).findFirst();
}

@Override
public Optional<Endpoint> getEndpoint(Organization organization, String thumbprint)
{
Optional<Endpoint> endpoint = exceptionHandler.catchAndLogSqlExceptionAndIfReturn(
() -> dao.readActiveNotDeletedByThumbprint(thumbprint), Optional::empty);

Expand Down
Loading
Loading