Skip to content

Commit

Permalink
Add application level authorization.
Browse files Browse the repository at this point in the history
Enables better support for clients using an anonymous DTLS handshake and
authorize the request then on the application level, e.g.
username/password or tokens. In combination with proxies, this enables
the "offload" the authentication from dtls and move it into the REST
API.

Signed-off-by: Achim Kraus <[email protected]>
  • Loading branch information
boaks committed Jan 15, 2025
1 parent 6482560 commit 9158115
Show file tree
Hide file tree
Showing 18 changed files with 1,288 additions and 174 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@
import org.eclipse.californium.core.observe.ObserveStatisticLogger;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.elements.EndpointContextMatcher;
import org.eclipse.californium.elements.PrincipalAndAnonymousEndpointContextMatcher;
import org.eclipse.californium.elements.PrincipalEndpointContextMatcher;
import org.eclipse.californium.elements.config.CertificateAuthenticationMode;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.elements.config.Configuration.DefinitionsProvider;
import org.eclipse.californium.elements.config.IntegerDefinition;
Expand All @@ -62,9 +64,9 @@
import org.eclipse.californium.elements.util.ExecutorsUtil;
import org.eclipse.californium.elements.util.NamedThreadFactory;
import org.eclipse.californium.elements.util.NetworkInterfacesUtil;
import org.eclipse.californium.elements.util.ProtocolScheduledExecutorService;
import org.eclipse.californium.elements.util.NetworkInterfacesUtil.InetAddressFilter;
import org.eclipse.californium.elements.util.NetworkInterfacesUtil.SimpleInetAddressFilter;
import org.eclipse.californium.elements.util.ProtocolScheduledExecutorService;
import org.eclipse.californium.elements.util.SslContextUtil.Credentials;
import org.eclipse.californium.elements.util.StringUtil;
import org.eclipse.californium.elements.util.SystemResourceMonitors;
Expand Down Expand Up @@ -202,6 +204,7 @@ public void applyDefinitions(Configuration config) {
config.set(CoapConfig.RESPONSE_MATCHING, MatcherMode.PRINCIPAL_IDENTITY);
config.set(CoapConfig.ACK_TIMEOUT, 2500, TimeUnit.MILLISECONDS);
config.set(DtlsConfig.DTLS_ROLE, DtlsRole.SERVER_ONLY);
config.set(DtlsConfig.DTLS_CLIENT_AUTHENTICATION_MODE, CertificateAuthenticationMode.NEEDED);
config.set(DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT, 2500, TimeUnit.MILLISECONDS);
config.set(DtlsConfig.DTLS_ADDITIONAL_ECC_TIMEOUT, 8, TimeUnit.SECONDS);
config.set(DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT, null, TimeUnit.SECONDS);
Expand Down Expand Up @@ -656,7 +659,11 @@ public void addEndpoints(ServerConfig cliArguments) {
// Context matcher
EndpointContextMatcher customContextMatcher = null;
if (MatcherMode.PRINCIPAL == config.get(CoapConfig.RESPONSE_MATCHING)) {
customContextMatcher = new PrincipalEndpointContextMatcher(true);
if (CertificateAuthenticationMode.NEEDED == config.get(DtlsConfig.DTLS_CLIENT_AUTHENTICATION_MODE)) {
customContextMatcher = new PrincipalEndpointContextMatcher(true);
} else {
customContextMatcher = new PrincipalAndAnonymousEndpointContextMatcher();
}
}

// explore network interfaces
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/********************************************************************************
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v1.0 which is available at
* https://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
********************************************************************************/
package org.eclipse.californium.elements;

import java.security.Principal;

import org.eclipse.californium.elements.auth.ApplicationPrincipal;
import org.eclipse.californium.elements.util.Bytes;

/**
* Principal based endpoint context matcher.
* <p>
* Matches DTLS based on the used principal or the session ID, if the principal
* is anonymous. Requires unique and stable credentials.
*
* @since 4.0
*/
public class PrincipalAndAnonymousEndpointContextMatcher implements EndpointContextMatcher {

public PrincipalAndAnonymousEndpointContextMatcher() {
}

@Override
public String getName() {
return "principal and anonymous correlation";
}

/**
* Gets identity from endpoint context.
* <p>
* Use the {@link Principal} if available and not
* {@link ApplicationPrincipal#ANONYMOUS}. Otherwise use the DTLS session
* ID.
*
* @param context endpoint context
* @return identity, or {@code null}, if none is available.
*/
private Object getIdentity(EndpointContext context) {
Principal identity = context.getPeerIdentity();
if (identity != null && !identity.equals(ApplicationPrincipal.ANONYMOUS)) {
return identity;
}
Bytes id = context.get(DtlsEndpointContext.KEY_SESSION_ID);
if (id != null && !id.isEmpty()) {
return id;
}
return null;
}

@Override
public Object getEndpointIdentity(EndpointContext context) {
Object identity = getIdentity(context);
if (identity == null) {
throw new IllegalArgumentException(
"Principal identity and session id are missing in provided endpoint context!");
}
return identity;
}

@Override
public boolean isResponseRelatedToRequest(EndpointContext requestContext, EndpointContext responseContext) {
return internalMatch(requestContext, responseContext);
}

@Override
public boolean isToBeSent(EndpointContext messageContext, EndpointContext connectorContext) {
if (null == connectorContext) {
return true;
}
return internalMatch(messageContext, connectorContext);
}

private final boolean internalMatch(EndpointContext requestedContext, EndpointContext availableContext) {

Object identity = getIdentity(requestedContext);
if (identity != null) {
return identity.equals(getIdentity(availableContext));
}
String cipher = requestedContext.getString(DtlsEndpointContext.KEY_CIPHER);
if (cipher != null) {
if (!cipher.equals(availableContext.getString(DtlsEndpointContext.KEY_CIPHER))) {
return false;
}
}
return true;
}

@Override
public String toRelevantState(EndpointContext context) {
if (context == null) {
return "n.a.";
} else {
StringBuilder builder = new StringBuilder();
builder.append("[");
builder.append(getIdentity(context));
String cipher = context.getString(DtlsEndpointContext.KEY_CIPHER);
if (cipher != null) {
builder.append(",").append(cipher);
}
builder.append("]");
return builder.toString();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/********************************************************************************
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v1.0 which is available at
* https://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
********************************************************************************/
package org.eclipse.californium.elements.auth;

import java.security.Principal;
import java.util.concurrent.Future;

import org.eclipse.californium.elements.EndpointContext;

/**
* Application authorize.
* <p>
* Sets {@link Principal} from application layers. Used with
* {@code DtlsConfig.DTLS_APPLICATION_AUTHORIZATION} to authorize or reject
* anonymous clients.
*
* @since 4.0
*/
public interface ApplicationAuthorizer {

/**
* Authorize the associated connection with the {@link ApplicationPrincipal}
* to prevent connection from being removed after in short time.
* <p>
* The Authorization may be processed asynchronous. A future request
* therefore may still not contain the provided principal! Only if the
* connection has not already a principal assigned, the provided one will be
* assigned.
*
* @param context endpoint context
* @param principal anonymous principal
* @return future with boolean result. Completes with {@code true}, if the
* principal was assigned, {@code false}, otherwise.
*/
Future<Boolean> authorize(EndpointContext context, ApplicationPrincipal principal);

/**
* Reject authorization.
*
* @param context endpoint context to reject authorization.
* @return future completes with removing the connection.
*/
Future<Void> rejectAuthorization(EndpointContext context);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/********************************************************************************
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v1.0 which is available at
* https://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
********************************************************************************/
package org.eclipse.californium.elements.auth;

import java.util.Objects;

/**
* Application level principal.
*
* @since 4.0
*/
public class ApplicationPrincipal extends AbstractExtensiblePrincipal<ApplicationPrincipal> {

/**
* Anonymous principal.
*/
public static final ApplicationPrincipal ANONYMOUS = new ApplicationPrincipal("anonymous");

/**
* Principal's name.
*/
private final String name;

/**
* Creates an application principal.
*
* @param name name
*/
public ApplicationPrincipal(String name) {
this.name = name;
}

/**
* Creates an application principal.
*
* @param name name
* @param additionalInformation additional information
*/
private ApplicationPrincipal(String name, AdditionalInfo additionalInformation) {
super(additionalInformation);
this.name = name;
}

@Override
public ApplicationPrincipal amend(AdditionalInfo additionalInfo) {
return new ApplicationPrincipal(name, additionalInfo);
}

@Override
public String getName() {
return name;
}

@Override
public String toString() {
return "Application Prinicpal [" + name + "]";
}

@Override
public int hashCode() {
return name.hashCode();
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null) {
return false;
} else if (getClass() != obj.getClass()) {
return false;
}
ApplicationPrincipal other = (ApplicationPrincipal) obj;
return Objects.equals(name, other.name);
}

}
Loading

0 comments on commit 9158115

Please sign in to comment.