Company Administration

Preparation

You must own a Company Administrator account. Only Company Administrators can manage Tenants and users. Make sure you have this account's credentials before you continue with this tutorial.

Introduction

The BUZZ Administration API lets you create, modify and delete companies. While you can accomplish this manually using the BUZZ administration console, using the API is useful when automating administrative tasks.

Company Data Model

This section describes the relevant data entities of the company provisioning APIs.

The person who installed the BUZZ server has created for you a company entity as well as administration accounts for this company. This was done manually using the BUZZ administration console.

But companies are hierarchical organizations. For example, a company can be a telecommunications provider, which in turn can serve its customer companies. The topmost organization is called a "Company", and its subordinate organizations are called "Tenants". The main difference between a Company and a Tenant is the fact that the former doesn't have any "master" attached to it, but the latter does. Here's an example of a Company named "Telco Global" with two customers Tenants attached to it:

Sample company database table

 

The initial Company created at installation time, which is the company you belong to, is the master. Thus you will mainly deal with Tenants.

Here's a model of the Company entity:

Company data model

Permissions

There are a few different types of users, each with specific permissions.

  • Company Administrator: This is a user whose company is not a Tenant. This user has the permission to create and manage Tenants and Tenant Administrators. He can also create other Company Administrators. He cannot create Subscribers nor can he use the instant messaging and conferencing APIs.
  • Tenant Administrator: This is a user attached to a Tenant. This user can create and manage Subscribers, as well as other Tenant Administrators in the same Tenant. He cannot create and manage other Tenants, nor can he use the instant messaging and conferencing APIs.
  • Subscriber: This is an end user of BUZZ, under a Tenant. This user can use all of the instant messaging and conferencing APIs but cannot create or manage Tenants and Subscribers.

BUZZ user permissions

Creating a Tenant

The process for creating a new tenant is done by following these steps:

  1. Log in as a Company Administrator with the /api/UserSecurityLogin endpoint as a Company Administrator.
  2. Create a Tenant with the /api/CompanyProvisioning endpoint. This will create the Company entity for this tenant.
  3. Create at least one associated Tenant Administrator with the /api/UserProvisioning endpoint. Don't forget to provide this person with his credentials.

Let us revisit the tenant creation steps one by one.

1. Log In as a Company Administrator

Since a subscriber does not have permission to manage companies, you need to log in with an administrator account. So we will not use the normal /api/subscriberSecurityLogin endpoint. Instead, we will send a GET message to the /api/usersecuritylogin endpoint, with basic authentication. The Authorization header should contain the administrator's username and password.

If successful, this will return an object with the following format:

{
    "data": {
        "bearerToken": "<administrator authentication token>",
        "companyId": "<administrator's company>"
    }
}

Keep this token handy and use it in the Authentication header for step 2. Also keep the company identifier to use as the master tenant when creating tenants.

The following is an example of this step:

@Autowired
private RestTemplate rest;

@Autowired
private ObjectMapper mapper;

private String server;
private String token;
private int currentCompany;

public String buzzAdminLogin(String username, String password, String server) {
    String plainCreds = username + ":" + password;
    byte[] plainCredsBytes = plainCreds.getBytes();
    byte[] base64CredsBytes = Base64.getEncoder().encode(plainCredsBytes);
    String base64Creds = new String(base64CredsBytes);

    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization", "Basic " + base64Creds);

    HttpEntity request = new HttpEntity<>(headers);
    this.server = server;
    if (!this.server.endsWith("/")) {
        this.server += "/";
    }

    try {
        ResponseEntity response = rest.exchange(this.server + "api/usersecuritylogin", HttpMethod.GET, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            JsonNode loginResponse = mapper.readValue(response.getBody(), JsonNode.class);
            return adminLoginSuccess(loginResponse);
        }
        return "Error logging into BUZZ server: " + response.toString();
    } catch (HttpClientErrorException e) {
        return "Error logging into BUZZ server: " + e.getStatusCode().toString();
    } catch (Exception e2) {
        return "Problem logging into BUZZ server: " + e2.toString();
    }
}

private String adminLoginSuccess(JsonNode response) {
    this.token = response.get("bearerToken").asText();
    this.currentCompany = response.get("companyId").asInt();
    return null;
}
function admin_login(user, pass, server) {
    $.ajax({
        url: server+"/api/usersecuritylogin",
        headers: {
            "Authorization": "Basic " + btoa(user + ":" + pass)
        },
        method: "GET",
        success: function(result) {
            admin_login_success(result);
        }
    });
}

var admin_token;
var current_company;

function admin_login_success(response) {
    admin_token = response.data.bearerToken;
    current_company = response.data.companyId;
}

2. Create a New Tenant

Send a POST message to the endpoint /api/CompanyProvisioning, with the body containing the following:

{
    "companyName": "<Name of the tenant>",
    "description": "<Description of the tenant>",
    "allowSubTenant": false,
    "status": 1,
    "masterTenantId": <Master company ID>,
    "location911Id": 0,
    "languageId": 0,
    "loginMethod": 0,
    "autoProvision": false,
    "timeZoneId": "<legacy, leave blank>",
    "allowedProducts": "<legacy, leave blank>",
    "dialInPassword": "<legacy, leave blank>",
    "activeDirectoryDoamin": "<legacy, leave blank>",
    "client_id": "<legacy, leave blank>",
    "client_secret": "<legacy, leave blank>"
}

There are a number of settings that should be left blank or zero. These are legacy fields that are not useful anymore.

In the following example, we'll create a new tenant for the company with ID 100, and we'll name it "Namaka Customer".

public void createTenant(String name, String description, String phoneNumber) throws Exception {
    ObjectNode tenantNode = mapper.createObjectNode();
    tenantNode.set("companyName", new TextNode(name));
    tenantNode.set("description", new TextNode(description));
    tenantNode.set("masterTenantId", new IntNode(currentCompany));
    tenantNode.set("autoProvision", BooleanNode.getFalse());
    tenantNode.set("allowSubTenant", BooleanNode.getFalse());
    tenantNode.set("status", new IntNode(1));
    tenantNode.set("timeZoneId", new TextNode("Dateline Standard Time"));
    tenantNode.set("dialInPassword", new TextNode(""));
    tenantNode.set("languageId", new IntNode(0));
    tenantNode.set("location911Id", new IntNode(0));
    tenantNode.set("loginMethod", new IntNode(0));
    tenantNode.set("client_id", new TextNode(" "));
    tenantNode.set("client_secret", new TextNode(""));
    tenantNode.set("activeDirectoryDoamin", new TextNode(""));
    tenantNode.set("allowImServices", BooleanNode.getFalse());
    tenantNode.set("defaultANI", new TextNode(phoneNumber));
    tenantNode.set("defaultCountryCode", new TextNode("1"));

    HttpEntity request = createBuzzEntity(mapper.writeValueAsString(tenantNode));

    try {
        ResponseEntity response = rest.exchange(server + "api/CompanyProvisioning", HttpMethod.POST, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            JsonNode result = mapper.readValue(response.getBody(), JsonNode.class);
            //keep this for later if needed
            int newTenantId = result.get("id").asInt();
            return; //Success
        }
        throw new Exception("Error creating tenant: " + response.toString());
    } catch (HttpClientErrorException e) {
        throw new Exception("Error creating tenant: " + e.getStatusCode().toString());
    } catch (Exception e2) {
        throw new Exception("Problem creating tenant: " + e2.toString());
    }
}
var newTenantId;
function createTenant () {
    $.ajax({
        url: server+"/api/CompanyProvisioning",
        method: "POST",
        contentType: 'application/json',
        headers: {
            "Authorization": "Bearer " + admin_token
        },
        data: JSON.stringify({
            "companyName": "Namaka Customer",
            "description": "This is a tenant of Namaka.",
            "allowSubTenant": false,
            "masterTenantId": current_company,
            "location911Id": 0,
            "languageId": 0,
            "loginMethod": 0,
            "autoProvision": false,
            "timeZoneId": "",
            "allowedProducts": "",
            "dialInPassword": "",
            "activeDirectoryDoamin": "",
            "client_id": "",
            "client_secret": ""
        }),
        success: function(result) {
            newTenantId = result["id"]; //Keep this for next step
            //All done!
        }
    });
}

3. Create a Tenant Administrator user

At this point the tenant exists but nobody can manage it. So now we should create at least one Tenant Administrator. This is done by sending a POST message to the endpoint /api/UserProvisioning with the following body:

{
    "companyId": <tenant ID>,
    "username": "<unique username>",
    "description": "<description>",
    "password": "<new password>",
    "email": "<user's email address>",
    "phonenumber": "<user's phone number>",
    "type": 0,
    "status": 1,
    "timeZoneId": "<>",
    "loginMethod": 0
}.

In the following example, we'll create a new Tenant Administrator for the company with ID 200, and we'll name it "John Smith".

public void createUser(String username, String description, String email, String phoneNumber, String password) throws Exception {
    ObjectNode userNode = mapper.createObjectNode();
    userNode.set("companyId", new IntNode(selectedTenant));
    userNode.set("username", new TextNode(username));
    userNode.set("email", new TextNode(email));
    userNode.set("description", new TextNode(description));
    userNode.set("phonenumber", new TextNode(phoneNumber));
    userNode.set("type", new IntNode(0));
    userNode.set("loginMethod", new IntNode(0));
    userNode.set("timeZoneId", new TextNode("Dateline Standard Time"));
    userNode.set("password", new TextNode(password));
    userNode.set("status", new IntNode(0));

    HttpEntity request = createBuzzEntity(mapper.writeValueAsString(userNode));

    try {
        ResponseEntity response = rest.exchange(server + "api/UserProvisioning", HttpMethod.POST, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            JsonNode result = mapper.readValue(response.getBody(), JsonNode.class);
            //keep this for later if needed
            int newTenantId = result.get("id").asInt();
            return; //Success
        }
        throw new Exception("Error creating user: " + response.toString());
    } catch (HttpClientErrorException e) {
        throw new Exception("Error creating user: " + e.getStatusCode().toString());
    } catch (Exception e2) {
        throw new Exception("Problem creating user: " + e2.toString());
    }
}
function createUser () {
    $.ajax({
        url: server+"/api/UserProvisioning",
        method: "POST",
        contentType: 'application/json',
        headers: {
            "Authorization": "Bearer " + admin_token
        },
        data: JSON.stringify({
TODO
        }),
        success: function(result) {
            //All done!
        }
    });
}

Modifying a Tenant

To modify the tenant data, you can send a PUT message to the endpoint /api/CompanyProvisioning/{id} with the following body:

{
    "companyName": "<Name of the tenant>",
    "description": "<Description of the tenant>",
    "allowSubTenant": false,
    "masterTenantId": <Master company ID>,
    "location911Id": 0,
    "languageId": 0,
    "loginMethod": 0,
    "autoProvision": false,
    "timeZoneId": "<legacy, leave blank>",
    "allowedProducts": "<legacy, leave blank>",
    "dialInPassword": "<legacy, leave blank>",
    "activeDirectoryDoamin": "<legacy, leave blank>",
    "client_id": "<legacy, leave blank>",
    "client_secret": "<legacy, leave blank>"
}

In this data, a missing field indicates you want to preserve the existing value. In the following example, we will change the description of a tenant.

public void changeDescription(String id, String description) throws Exception {
    ObjectNode tenantNode = mapper.createObjectNode();
    tenantNode.set("id", new IntNode(id));
    tenantNode.set("description", new TextNode(description));

    //All other values are defaults.

    HttpEntity request = createBuzzEntity(mapper.writeValueAsString(tenantNode));

    try {
        ResponseEntity response = rest.exchange(server + "api/CompanyProvisioning/" + id, HttpMethod.PUT, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            return; //Success
        }
        throw new Exception("Error updating tenant: " + response.toString());
    } catch (HttpClientErrorException e) {
        throw new Exception("Error updating tenant: " + e.getStatusCode().toString());
    } catch (Exception e2) {
        throw new Exception("Problem updating tenant: " + e2.toString());
    }
}
function changeDescription(tenantID, new_description) {
    $.ajax({
        url: server+"/api/CompanyProvisioning/" + tenantID,
        method: "PUT",
        data: JSON.stringify({
            "description": new_description
        }),
        success: function(result) {
            //All done!;
        }
    });
}

Retrieving Companies

There are two ways to retrieve company information:

  • You can also retrieve a single company by doing a GET on the /api/CompanyProvisioning/{id} endpoints
  • You can retrieve a full list of companies that the current user is allowed to see by doing a GET on the endpoint /api/CompanyProvisioning

Let's retrieve a whole list of tenants using the /api/CompanyProvisioning endpoint:

public List<Tenant> getTenants() throws Exception {
    HttpEntity request = createBuzzEntity(null);

    try {
        ResponseEntity response = rest.exchange(server + "api/CompanyProvisioning", HttpMethod.GET, request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            JsonNode tenantsResponse = mapper.readValue(response.getBody(), JsonNode.class);
            return extractTenants((ObjectNode) tenantsResponse);
        }
        throw new Exception("Error getting tenants: " + response.toString());
    } catch (HttpClientErrorException e) {
        throw new Exception("Error getting tenants: " + e.getStatusCode().toString());
    } catch (Exception e2) {
        throw new Exception("Problem getting tenants: " + e2.toString());
    }
}

private List extractTenants(ObjectNode tenants) {
    ArrayNode tenantArray = (ArrayNode) tenants.get("results");
    List<Tenant> result = new ArrayList<>();
    for (Iterator i = tenantArray.iterator(); i.hasNext(); ) {
        ObjectNode userObject = (ObjectNode) i.next();
        Tenant tenant = new Tenant();
        tenant.setId("" + userObject.get("id").asInt());
        tenant.setName(userObject.get("companyName").asText());
        tenant.setDescription(userObject.get("description").asText());
        result.add(tenant);
    }
    return result;
}

function printTenant(tenantID) {
    $.ajax({
        url: server+"/api/CompanyProvisioning/" + tenantID,
        method: "GET",
        success: function(result) {
            console.log(result); //See below for the output
        }
    });
}

The response body of this function has the following format:

[{
    "companyName": "<Name of the tenant>",
    "description": "<Description of the tenant>",
    "allowSubTenant": false,
    "masterTenantId": <Master company ID>,
    "location911Id": 0,
    "timeZoneId": "",
    "allowedProducts": "<List of products, see below>",
    "dialInPassword": "<legacy, leave blank>",
    "languageId": 0,
    "loginMethod": 0,
    "activeDirectoryDoamin": "",
    "autoProvision": false???,
    "client_id": "???",
    "client_secret": "???"
}]