/** * Gets the principal representing a connection's client. * * @param con The connection to get the principal for. * @return The principal representing the authenticated client or {@link Constants#PRINCIPAL_ANONYMOUS} * if the client has not been authenticated. * @throws NullPointerException if the connection is {@code null}. */ public static HonoUser getClientPrincipal(final ProtonConnection con) { final Record attachments = Objects.requireNonNull(con).attachments(); return getClientPrincipal(attachments); }
@Override public String getConnectionId() { return Constants.getConnectionId(link); }
con.setCondition(ProtonHelper.condition(AmqpError.NOT_FOUND, "no such node")).close(); } else { final HonoUser user = Constants.getClientPrincipal(con); if (Constants.SUBJECT_ANONYMOUS.equals(user.getName())) { con.setCondition(ProtonHelper.condition(AmqpError.UNAUTHORIZED_ACCESS, "client must authenticate using SASL")).close(); } else { Constants.copyProperties(con, sender); sender.setSource(sender.getRemoteSource()); endpoint.onLinkAttach(con, sender, targetResource);
@Override public void process(final Handler<Boolean> completionHandler) { final String[] remoteMechanisms = sasl.getRemoteMechanisms(); if (remoteMechanisms.length == 0) { LOG.debug("client provided an empty list of SASL mechanisms [hostname: {}, state: {}]", sasl.getHostname(), sasl.getState().name()); completionHandler.handle(false); } else { final String chosenMechanism = remoteMechanisms[0]; LOG.debug("client wants to authenticate using SASL [mechanism: {}, host: {}, state: {}]", chosenMechanism, sasl.getHostname(), sasl.getState().name()); final Future<HonoUser> authTracker = Future.future(); authTracker.setHandler(s -> { if (s.succeeded()) { final HonoUser user = s.result(); LOG.debug("authentication of client [authorization ID: {}] succeeded", user.getName()); Constants.setClientPrincipal(protonConnection, user); succeeded = true; sasl.done(SaslOutcome.PN_SASL_OK); } else { LOG.debug("authentication failed: " + s.cause().getMessage()); sasl.done(SaslOutcome.PN_SASL_AUTH); } completionHandler.handle(Boolean.TRUE); }); final byte[] saslResponse = new byte[sasl.pending()]; sasl.recv(saslResponse, 0, saslResponse.length); verify(chosenMechanism, saslResponse, authTracker.completer()); } }
handleUnknownEndpoint(con, sender, targetResource); } else { final HonoUser user = Constants.getClientPrincipal(con); getAuthorizationService().isAuthorized(user, targetResource, Activity.READ).setHandler(authAttempt -> { if (authAttempt.succeeded() && authAttempt.result()) { Constants.copyProperties(con, sender); sender.setSource(sender.getRemoteSource()); sender.setTarget(sender.getRemoteTarget());
@Override public void process(final Handler<Boolean> completionHandler) { final String[] remoteMechanisms = sasl.getRemoteMechanisms(); if (remoteMechanisms.length == 0) { LOG.debug("client provided an empty list of SASL mechanisms [hostname: {}, state: {}]", sasl.getHostname(), sasl.getState().name()); completionHandler.handle(false); } else { final String chosenMechanism = remoteMechanisms[0]; LOG.debug("client wants to authenticate using SASL [mechanism: {}, host: {}, state: {}]", chosenMechanism, sasl.getHostname(), sasl.getState().name()); final Future<HonoUser> authTracker = Future.future(); authTracker.setHandler(s -> { if (s.succeeded()) { final HonoUser user = s.result(); LOG.debug("authentication of client [authorization ID: {}] succeeded", user.getName()); Constants.setClientPrincipal(protonConnection, user); succeeded = true; sasl.done(SaslOutcome.PN_SASL_OK); } else { LOG.debug("authentication failed: " + s.cause().getMessage()); sasl.done(SaslOutcome.PN_SASL_AUTH); } completionHandler.handle(Boolean.TRUE); }); final byte[] saslResponse = new byte[sasl.pending()]; sasl.recv(saslResponse, 0, saslResponse.length); verify(chosenMechanism, saslResponse, authTracker.completer()); } }
/** * Gets the principal representing a connection's client. * * @param con The connection to get the principal for. * @return The principal representing the authenticated client or {@link Constants#PRINCIPAL_ANONYMOUS} * if the client has not been authenticated. * @throws NullPointerException if the connection is {@code null}. */ public static HonoUser getClientPrincipal(final ProtonConnection con) { final Record attachments = Objects.requireNonNull(con).attachments(); return getClientPrincipal(attachments); }
handleUnknownEndpoint(con, sender, targetResource); } else { final HonoUser user = Constants.getClientPrincipal(con); getAuthorizationService().isAuthorized(user, targetResource, Activity.READ).setHandler(authAttempt -> { if (authAttempt.succeeded() && authAttempt.result()) { Constants.copyProperties(con, sender); sender.setSource(sender.getRemoteSource()); sender.setTarget(sender.getRemoteTarget());
/** * Copies properties from a connection's attachments to a link's attachments. * <p> * The properties copied are * <ul> * <li>{@link #KEY_CONNECTION_ID}</li> * </ul> * * @param source The connection. * @param target The link. */ public static void copyProperties(final ProtonConnection source, final ProtonLink<?> target) { Objects.requireNonNull(source); Objects.requireNonNull(target); target.attachments().set(Constants.KEY_CONNECTION_ID, String.class, getConnectionId(source)); }
/** * Gets the authenticated client principal name for an AMQP connection. * * @param con the connection to read the user from * @return the user associated with the connection or {@link Constants#SUBJECT_ANONYMOUS} if it cannot be determined. */ private static String getUserFromConnection(final ProtonConnection con) { Principal clientId = Constants.getClientPrincipal(con); if (clientId == null) { LOG.warn("connection from client [{}] is not authenticated properly using SASL, falling back to default subject [{}]", con.getRemoteContainer(), Constants.SUBJECT_ANONYMOUS); return Constants.SUBJECT_ANONYMOUS; } else { return clientId.getName(); } }
handleUnknownEndpoint(con, receiver, targetResource); } else { final HonoUser user = Constants.getClientPrincipal(con); getAuthorizationService().isAuthorized(user, targetResource, Activity.WRITE).setHandler(authAttempt -> { if (authAttempt.succeeded() && authAttempt.result()) { Constants.copyProperties(con, receiver); receiver.setSource(receiver.getRemoteSource()); receiver.setTarget(receiver.getRemoteTarget());
/** * Copies properties from a connection's attachments to a link's attachments. * <p> * The properties copied are * <ul> * <li>{@link #KEY_CONNECTION_ID}</li> * </ul> * * @param source The connection. * @param target The link. */ public static void copyProperties(final ProtonConnection source, final ProtonLink<?> target) { Objects.requireNonNull(source); Objects.requireNonNull(target); target.attachments().set(Constants.KEY_CONNECTION_ID, String.class, getConnectionId(source)); }
final HonoUser clientPrincipal = Constants.getClientPrincipal(connection); final Map<String, String[]> permissions = getPermissionsFromAuthorities(clientPrincipal.getAuthorities()); final Map<Symbol, Object> properties = new HashMap<>();
handleUnknownEndpoint(con, receiver, targetResource); } else { final HonoUser user = Constants.getClientPrincipal(con); getAuthorizationService().isAuthorized(user, targetResource, Activity.WRITE).setHandler(authAttempt -> { if (authAttempt.succeeded() && authAttempt.result()) { Constants.copyProperties(con, receiver); receiver.setSource(receiver.getRemoteSource()); receiver.setTarget(receiver.getRemoteTarget());
/** * Processes a peer's AMQP <em>open</em> frame. * <p> * This default implementation * <ol> * <li>adds a unique connection identifier to the connection's attachments * under key {@link Constants#KEY_CONNECTION_ID}</li> * <li>invokes {@link #processDesiredCapabilities(ProtonConnection, Symbol[])}</li> * <li>sets a timer that closes the connection once the client's token * has expired</li> * <li>sends the AMQP <em>open</em> frame to the peer</li> * </ol> * * @param connection The connection to open. */ protected void processRemoteOpen(final ProtonConnection connection) { final HonoUser clientPrincipal = Constants.getClientPrincipal(connection); LOG.debug("client [container: {}, user: {}] connected", connection.getRemoteContainer(), clientPrincipal.getName()); // attach an ID so that we can later inform downstream components when connection is closed connection.attachments().set(Constants.KEY_CONNECTION_ID, String.class, UUID.randomUUID().toString()); processDesiredCapabilities(connection, connection.getRemoteDesiredCapabilities()); final Duration delay = Duration.between(Instant.now(), clientPrincipal.getExpirationTime()); final WeakReference<ProtonConnection> conRef = new WeakReference<>(connection); vertx.setTimer(delay.toMillis(), timerId -> { if (conRef.get() != null) { closeExpiredConnection(conRef.get()); } }); connection.open(); }
final HonoUser clientPrincipal = Constants.getClientPrincipal(connection);
@Override public final void onLinkAttach(final ProtonConnection con, final ProtonSender sender, final ResourceIdentifier targetResource) { if (ProtonQoS.AT_LEAST_ONCE.equals(sender.getRemoteQoS())) { final HonoUser user = Constants.getClientPrincipal(con); sender.setQoS(ProtonQoS.AT_LEAST_ONCE).open(); logger.debug("transferring token to client..."); final Message tokenMsg = ProtonHelper.message(user.getToken()); MessageHelper.addProperty(tokenMsg, AuthenticationConstants.APPLICATION_PROPERTY_TYPE, AuthenticationConstants.TYPE_AMQP_JWT); sender.send(tokenMsg, disposition -> { if (disposition.remotelySettled()) { logger.debug("successfully transferred auth token to client"); } else { logger.debug("failed to transfer auth token to client"); } sender.close(); }); } else { onLinkDetach(sender, ProtonHelper.condition(AmqpError.INVALID_FIELD, "supports AT_LEAST_ONCE delivery mode only")); } }
final HonoUser clientPrincipal = Constants.getClientPrincipal(con);
final HonoUser clientPrincipal = Constants.getClientPrincipal(con);
/** * Closes an expired client connection. * <p> * A connection is considered expired if the {@link HonoUser#isExpired()} method * of the user principal attached to the connection returns {@code true}. * * @param con The client connection. */ protected final void closeExpiredConnection(final ProtonConnection con) { if (!con.isDisconnected()) { final HonoUser clientPrincipal = Constants.getClientPrincipal(con); if (clientPrincipal != null) { LOG.debug("client's [{}] access token has expired, closing connection", clientPrincipal.getName()); con.disconnectHandler(null); con.closeHandler(null); con.setCondition(ProtonHelper.condition(AmqpError.UNAUTHORIZED_ACCESS, "access token expired")); con.close(); con.disconnect(); publishConnectionClosedEvent(con); } } }