Just a second...

Example: Update the security store

The following examples use the SecurityControl feature in the Diffusion™ API to update the security store.

// Session security allows you to change the principal that a session is authenticated as. It also  allows users to
// query and update server-side security and authentication stores, which control users, roles and permissions.
// This enables you to manage the capabilities that any logged in user will have access to.

// Connect to Diffusion with control client credentials
    host   : 'diffusion.example.com',
    port   : 443,
    secure : true,
    principal : 'control',
    credentials : 'password'
}).then(function(session) {

    // 1. A session change their principal by re-authenticating
    session.security.changePrincipal('admin', 'password').then(function() {
        console.log('Authenticated as admin');

    // 2. The security configuration provides details about roles and their assigned permissions
    session.security.getSecurityConfiguration().then(function(config) {
        console.log('Roles for anonymous sessions: ', config.anonymous);
        console.log('Roles for named sessions: ', config.named);
        console.log('Available roles: ', config.roles);
    }, function(error) {
        console.log('Unable to fetch security configuration', error);

    // 3. Changes to the security configuration are done with a SecurityScriptBuilder
    var securityScriptBuilder = session.security.securityScriptBuilder();

    // Set the permissions for a particular role - global and topic-scoped
    // Each method on a script builder returns a new builder
    var setPermissionScript = securityScriptBuilder.setGlobalPermissions('SUPERUSER', ['REGISTER_HANDLER'])
                                                   .setTopicPermissions('SUPERUSER', '/foo', ['UPDATE_TOPIC'])

    // Update the server-side store with the generated script
    session.security.updateSecurityStore(setPermissionScript).then(function() {
        console.log('Security configuration updated successfully');
    }, function(error) {
        console.log('Failed to update security configuration: ', error);

    // 4. The system authentication configuration lists all users & roles
    session.security.getSystemAuthenticationConfiguration().then(function(config) {
        console.log('System principals: ', config.principals);
        console.log('Anonymous sessions: ', config.anonymous);
    }, function(error) {
        console.log('Unable to fetch system authentication configuration', error);

    // 5. Changes to the system authentication config are done with a SystemAuthenticationScriptBuilder
    var authenticationScriptBuilder = session.security.authenticationScriptBuilder();

    // Add a new user and set password & roles.
    var addUserScript = authenticationScriptBuilder.addPrincipal('Superman', 'correcthorsebatterystapler')
                                                   .assignRoles('Superman', ['SUPERUSER'])

    // Update the system authentication store
    session.security.updateAuthenticationStore(addUserScript).then(function() {
        console.log('Updated system authentication config');
    }, function(error) {
        console.log('Failed to update system authentication: ', error);
 * Copyright © 2021 Push Technology Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * See the License for the specific language governing permissions and
 * limitations under the License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using PushTechnology.ClientInterface.Client.Factories;
using PushTechnology.ClientInterface.Client.Features.Control.Clients.SecurityControl;
using PushTechnology.ClientInterface.Client.Session;
using PushTechnology.ClientInterface.Client.Types;
using static System.Console;

namespace PushTechnology.ClientInterface.Example
    /// <summary>
    /// Client implementation that demonstrates how to update the security store.
    /// </summary>
    public sealed class SecurityControl
        public SecurityControl(string serverUrl)
            // Connect as an admin session
            var session = Diffusion.Sessions.Principal("admin").Password("password")
                .CertificateValidation((cert, chain, errors) => CertificateValidationResult.ACCEPT)

            string role = "ADMINISTRATOR";

            IReadOnlyCollection<GlobalPermission> defaultPermissions = null;
            ISecurityConfiguration securityConfig = null;

                //Get the default global permissions for the Admin role.
                securityConfig = await session.SecurityControl.GetSecurityAsync();

                var adminRole = securityConfig.Roles.Where(x => x.Name == role).FirstOrDefault();
                defaultPermissions = adminRole.GlobalPermissions;

                WriteLine($"The Administrator role has the following global permissions by default:");

                foreach (var permission in defaultPermissions)
            catch (Exception ex)
                WriteLine($"Failed to get global permissions : {ex}.");

                //Add the following global permissions for the Admin role.
                var permissions = new List<GlobalPermission>(defaultPermissions);
                permissions.AddRange(new[] { GlobalPermission.REGISTER_HANDLER, 
                                             GlobalPermission.VIEW_SESSION });

                WriteLine($"Adding further permissions...");

                string script = 
                    session.SecurityControl.Script.SetGlobalPermissions(role, permissions).ToScript();

                await session.SecurityControl.UpdateStoreAsync(script);
            catch (Exception ex)
                WriteLine($"Failed to set global permissions : {ex}.");
Java and Android
package com.pushtechnology.diffusion.examples;

import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.toCollection;

import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.pushtechnology.diffusion.client.Diffusion;
import com.pushtechnology.diffusion.client.features.control.clients.SecurityControl;
import com.pushtechnology.diffusion.client.features.control.clients.SecurityControl.Role;
import com.pushtechnology.diffusion.client.features.control.clients.SecurityControl.ScriptBuilder;
import com.pushtechnology.diffusion.client.features.control.clients.SecurityControl.SecurityConfiguration;
import com.pushtechnology.diffusion.client.session.Session;
import com.pushtechnology.diffusion.client.types.PathPermission;

 * An example of using a control client to alter the security configuration.
 * <P>
 * This uses the {@link SecurityControl} feature only.
 * @author Push Technology Limited
 * @since 5.3
public class ControlClientChangingSecurity {

    private static final Logger LOG =

    private final SecurityControl securityControl;
    private final ScriptBuilder emptyScript;

     * Constructor.
    public ControlClientChangingSecurity() {

        final Session session = Diffusion.sessions()
            // Authenticate with a user that has the VIEW_SECURITY and
            // MODIFY_SECURITY permissions.
            // Use a secure channel because we're transferring sensitive
            // information.

        securityControl = session.feature(SecurityControl.class);
        emptyScript = securityControl.scriptBuilder();

     * This will update the security store to ensure that all roles start with a
     * capital letter (note that this does not address changing the use of the
     * roles in the system authentication store).
     * @return a CompletableFuture that completes when the operation succeeds or
     *         fails.
     *         <p>
     *         If the operation was successful, the CompletableFuture will
     *         complete successfully.
     *         <p>
     *         Otherwise, the CompletableFuture will complete exceptionally with
     *         an {@link ExecutionException}. See
     *         {@link SecurityControl#getSecurity()} and
     *         {@link SecurityControl#updateStore(String)} for common failure
     *         reasons.
    public CompletableFuture<Void> capitalizeRoles() {
        return securityControl.getSecurity().thenCompose(this::capitalizeRoles);

    private CompletableFuture<Void> capitalizeRoles(
        SecurityConfiguration configuration) {

        final String script = emptyScript



                // For each role ...
                // ... build a script that capitalises that role ...
                /// .. and combine the per-role scripts into one.
                .reduce(emptyScript, (sb1, sb2) -> sb1.append(sb2)))


        LOG.info("Sending the following script to the server:\n{}", script);

        return securityControl.updateStore(script)
            // Convert CompletableFuture<?> to CompletableFuture<Void>.
            .thenAccept(ignored -> { });

    private ScriptBuilder capitalizeRole(Role role) {
        final String oldName = role.getName();
        final String newName = capitalizeString(oldName);

        ScriptBuilder builder = emptyScript;

        // Only if new name is different
        if (!oldName.equals(newName)) {
            if (!role.getGlobalPermissions().isEmpty()) {
                builder = builder
                    // Remove global permissions for old role
                    .setGlobalPermissions(oldName, emptySet())
                    // Set global permissions for new role
                        newName, role.getGlobalPermissions());

            if (!role.getDefaultPathPermissions().isEmpty()) {
                builder = builder
                    // Remove default path permissions for old role
                    .setDefaultPathPermissions(oldName, emptySet())
                    // Set default path permissions for new role
                        newName, role.getDefaultPathPermissions());

            builder = builder.append(
                    entry -> {
                        final String path = entry.getKey();
                        final Set<PathPermission> permissions = entry.getValue();

                        return emptyScript
                            // Remove path permissions for old role
                            .removePathPermissions(oldName, path)
                            // Set path permissions for new role
                            .setPathPermissions(newName, path, permissions);
                    .reduce(emptyScript, (sb1, sb2) -> sb1.append(sb2)));

        final Set<String> oldIncludedRoles = role.getIncludedRoles();

        if (oldIncludedRoles.isEmpty()) {
            return builder;

        return builder
            // Remove old included roles.
            .setRoleIncludes(oldName, emptySet())

            // Set new roles even if role name did not change as the included
            // roles may be changed.
            .setRoleIncludes(newName, capitalizeSet(oldIncludedRoles));

    private static Set<String> capitalizeSet(Set<String> roles) {
        return roles.stream()

    private static String capitalizeString(String role) {
        return Character.toUpperCase(role.charAt(0)) + role.substring(1);

     * Close the session.
    public void close() {

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