Just a second...

Example: Register an authentication handler

The following examples use the Diffusion™ API to register a control authentication handler with the Diffusion server. The examples also include a simple or empty authentication handler.

The name by which the control authentication handler is registered must be configured in the Server.xml configuration file of the Diffusion server for the control authentication handler to be called to handle authentication requests.

Java and Android
package com.pushtechnology.diffusion.examples;

package com.pushtechnology.diffusion.examples;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import com.pushtechnology.diffusion.client.Diffusion;
import com.pushtechnology.diffusion.client.callbacks.Stream;
import com.pushtechnology.diffusion.client.features.control.clients.AuthenticationControl;
import com.pushtechnology.diffusion.client.features.control.clients.AuthenticationControl.ControlAuthenticator;
import com.pushtechnology.diffusion.client.session.Session;
import com.pushtechnology.diffusion.client.types.Credentials;

/**
 * This is a control client which registers an authentication handler with a
 * Diffusion server.
 */
public final class ControlAuthenticationClient {

    /**
     * Main entry point for the control client.
     */
    public static void main(final String[] args) throws Exception {

        // The control client links to the server using the principal 'admin', which is
        // authenticated by the system authentication handler (see etc/SystemAuthentication.store).
        // The principal must have REGISTER_HANDLER and AUTHENTICATE permissions.
        final Session session =
            Diffusion.sessions()
                .principal("admin")
                .password("password")
                .open("ws://diffusion.example.com:80");

        session.feature(AuthenticationControl.class).setAuthenticationHandler(
            "after-system-handler",
            new ExampleControlAuthenticationHandler()).get(10, TimeUnit.SECONDS);

        while (true) {
            Thread.sleep(60000);
        }
    }

    /**
     * An example of a control authentication handler.
     * <P>
     * This shows a simple example using a table of permitted principals with
     * their passwords. It also demonstrates how the handler can change the
     * properties of the client being authenticated.
     */
    private static class ExampleControlAuthenticationHandler
        extends Stream.Default
        implements ControlAuthenticator {

        private static final Map<String, byte[]> PASSWORDS = new HashMap<>();
        static {
            PASSWORDS.put("manager", "password".getBytes(Charset.forName("UTF-8")));
            PASSWORDS.put("guest", "asecret".getBytes(Charset.forName("UTF-8")));
            PASSWORDS.put("brian", "boru".getBytes(Charset.forName("UTF-8")));
            PASSWORDS.put("another", "apassword".getBytes(Charset.forName("UTF-8")));
        }

        @Override
        public void authenticate(
            String principal,
            Credentials credentials,
            Map<String, String> sessionProperties,
            Map<String, String> proposedProperties,
            Callback callback) {

            final byte[] passwordBytes = PASSWORDS.get(principal);

            if (passwordBytes != null &&
                credentials.getType() == Credentials.Type.PLAIN_PASSWORD &&
                Arrays.equals(credentials.toBytes(), passwordBytes)) {
                if ("manager".equals(principal)) {
                    // manager allows all proposed properties
                    callback.allow(proposedProperties);
                }
                else if ("brian".equals(principal)) {
                    // brian is allowed all proposed properties and also gets
                    // the 'super' role added
                    final Map<String, String> result =
                        new HashMap<>(proposedProperties);
                    final Set<String> roles =
                        Diffusion.stringToRoles(
                            sessionProperties.get(Session.ROLES));
                    roles.add("super");
                    result.put(Session.ROLES, Diffusion.rolesToString(roles));
                    callback.allow(result);
                }
                else {
                    // all others authenticated but ignoring proposed properties
                    callback.allow();
                }
            }
            else {
                // Any principal not in the table is denied.
                callback.deny();
            }
        }
    }
}
.NET
                                        
                                        
                                        
C
/*
 * Diffusion can be configured to delegate authentication requests to
 * an external handler. This program provides an authentication
 * handler to demonstrate this feature. A detailed description of
 * security and authentication handlers can be found in the Diffusion
 * user manual.
 *
 * Authentication handlers are registered with a name, which is typically specified in
 * Server.xml
 *
 * Two handler names are provided by default;
 * before-system-handler and after-system-handler, and additional
 * handlers may be specified for Diffusion through the Server.xml file
 * and an accompanying Java class that implements the
 * AuthenticationHandler interface.
 *
 * This control authentication handler connects to Diffusion and attempts
 * to register itself with a user-supplied name, which should match the name
 * configured in Server.xml.
 *
 * The default behavior is to install as the "before-system-handler",
 * which means that it will intercept authentication requests before
 * Diffusion has a chance to act on them.
 *
 * It will:
 * <ul>
 * <li>Deny all anonymous connections</li>
 * <li>Allow connections where the principal and credentials (i.e., username and password) match some hardcoded values</li>
 * <li>Abstain from all other decisions, thereby letting Diffusion and other authentication handlers decide what to do.</li>
 * </ul>
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "diffusion.h"
#include "args.h"
#include "conversation.h"

struct user_credentials_s {
        const char *username;
        const char *password;
};

/*
 * Username/password pairs that this handler accepts.
 */
static const struct user_credentials_s USERS[] = {
        { "fish", "chips" },
        { "ham", "eggs" },
        { NULL, NULL }
};

ARG_OPTS_T arg_opts[] = {
        ARG_OPTS_HELP,
        {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"},
        {'n', "name", "Name under which to register the authentication handler", ARG_OPTIONAL, ARG_HAS_VALUE, "before-system-handler"},
        {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL},
        {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL},
        END_OF_ARG_OPTS
};

/*
 * When the authentication service has been registered, this function will be
 * called.
 */
static int
on_registration(SESSION_T *session, void *context)
{
        printf("Registered authentication handler\n");
        return HANDLER_SUCCESS;
}

/*
 * When the authentication service has be deregistered, this function will be
 * called.
 */
static int
on_deregistration(SESSION_T *session, void *context)
{
        printf("Deregistered authentication handler\n");
        return HANDLER_SUCCESS;
}

/*
 * This is the function that is called when authentication has been delegated
 * from Diffusion.
 *
 * The response may return one of three values via the response parameter:
 * ALLOW:   The user is authenticated.
 * ALLOW_WITH_RESULT: The user is authenticated, and additional roles are
 *                    to be applied to the user.
 * DENY:    The user is NOT authenticated.
 * ABSTAIN: Allow another handler to make the decision.
 *
 * The handler should return HANDLER_SUCCESS in all cases, unless an actual
 * error occurs during the authentication process (in which case,
 * HANDLER_FAILURE is appropriate).
 */
static int
on_authentication(SESSION_T *session,
                  const SVC_AUTHENTICATION_REQUEST_T *request,
                  SVC_AUTHENTICATION_RESPONSE_T *response,
                  void *context)
{
        // No credentials, or not password type. We're not an authority for
        // this type of authentication so abstain in case some other registered
        // authentication handler can deal with the request.
        if(request->credentials == NULL) {
                printf("No credentials specified, abstaining\n");
                response->value = AUTHENTICATION_ABSTAIN;
                return HANDLER_SUCCESS;
        }
        if(request->credentials->type != PLAIN_PASSWORD) {
                printf("Credentials are not PLAIN_PASSWORD, abstaining\n");
                response->value = AUTHENTICATION_ABSTAIN;
                return HANDLER_SUCCESS;
        }

        printf("principal = %s\n", request->principal);
        printf("credentials = %*s\n",
               (int)request->credentials->data->len,
               request->credentials->data->data);

        if(request->principal == NULL || strlen(request->principal) == 0) {
                printf("Denying anonymous connection (no principal)\n");
                response->value = AUTHENTICATION_DENY; // Deny anon connections
                return HANDLER_SUCCESS;
        }

        char *password = malloc(request->credentials->data->len + 1);
        memmove(password, request->credentials->data->data, request->credentials->data->len);
        password[request->credentials->data->len] = '\0';

        int auth_decided = 0;
        int i = 0;
        while(USERS[i].username != NULL) {

                printf("Checking username %s vs %s\n", request->principal, USERS[i].username);
                printf("     and password %s vs %s\n", password, USERS[i].password);

                if(strcmp(USERS[i].username, request->principal) == 0 &&
                   strcmp(USERS[i].password, password) == 0) {

                        puts("Allowed");
                        response->value = AUTHENTICATION_ALLOW;
                        auth_decided = 1;
                        break;
                }
                i++;

        }

        free(password);

        if(auth_decided == 0) {
                puts("Abstained");
                response->value = AUTHENTICATION_ABSTAIN;
        }

        return HANDLER_SUCCESS;
}

int
main(int argc, char** argv)
{
        HASH_T *options = parse_cmdline(argc, argv, arg_opts);
        if (options == NULL || hash_get(options, "help") != NULL) {
                show_usage(argc, argv, arg_opts);
                return EXIT_FAILURE;
        }

        char *url = hash_get(options, "url");
        char *name = hash_get(options, "name");
        char *principal = hash_get(options, "principal");
        char *credentials = hash_get(options, "credentials");

        /*
         * Create a session with Diffusion.
         */
        puts("Creating session");
        DIFFUSION_ERROR_T error = { 0 };
        SESSION_T *session = session_create(url,
                                            principal,
                                            credentials != NULL ? credentials_create_password(credentials) : NULL,
                                            NULL, NULL,
                                            &error);
        if (session == NULL) {
                fprintf(stderr, "TEST: Failed to create session\n");
                fprintf(stderr, "ERR : %s\n", error.message);
                return EXIT_FAILURE;
        }

        /*
         * Provide a set (via a hash map containing keys and NULL
         * values) to indicate what information about the connecting
         * client that we'd like Diffusion to send us.
         */
        HASH_T *detail_set = hash_new(5);
        char buf[2];
        sprintf(buf, "%d", SESSION_DETAIL_SUMMARY);
        hash_add(detail_set, strdup(buf), NULL);
        sprintf(buf, "%d", SESSION_DETAIL_LOCATION);
        hash_add(detail_set, strdup(buf), NULL);
        sprintf(buf, "%d", SESSION_DETAIL_CONNECTOR_NAME);
        hash_add(detail_set, strdup(buf), NULL);

        /*
         * Register the authentication handler.
         */
        AUTHENTICATION_REGISTRATION_PARAMS_T auth_registration_params = {
                .name = name,
                .detail_set = detail_set,
                .on_registration = on_registration,
                .authentication_handlers.on_authentication = on_authentication
        };

        puts("Sending registration request");
        SVC_AUTHENTICATION_REGISTER_REQUEST_T *reg_request =
                authentication_register(session, auth_registration_params);

        /*
         *  Wait a while before moving on to deregistration.
         */
        sleep(30);

        AUTHENTICATION_DEREGISTRATION_PARAMS_T auth_deregistration_params = {
                .on_deregistration = on_deregistration,
                .original_request = reg_request
        };

        /*
         * Deregister the authentication handler.
         */
        printf("Deregistering authentication handler\n");
        authentication_deregister(session, auth_deregistration_params);

        session_close(session, NULL);
        session_free(session);

        return EXIT_SUCCESS;
}

Change the URL from that provided in the example to the URL of the Diffusion server.