Page History
Some ISV deployment strategies may involve deploying on-premise installations of the software for each client. This may make it difficult to replace the Yellowfin license when it requires replacement.
There are two approaches that can be used to update a license in a remote environment. One involves pushing a license via the REST service, and the other allows for a pull mechanism that can be configured in the Yellowfin instance itself.
Updating a license via the REST API
Updating a license in a running instance of Yellowfin is possible with the POST /api/rpc/licence-management/upload-licence end-point, Upload License.
This particular endpoint uses a form based submission paradigm. A single form body is required named “newLicence” which should contain the license file’s contents.
The following code examples illustrate how to upload a license via the POST /api/rpc/licence-management/upload-licence end-point:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
package rest.code.examples;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Random;
import org.apache.hc.client5.http.entity.mime.HttpMultipartMode;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.fluent.Content;
import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* Upload License File via the Yellowfin REST API
*/
public class UploadALicenseFile {
public static void main(String[] args) throws Exception {
System.out.println("Upload License File");
String host = "http://localhost:8080/Yellowfin";
String restUsername = "admin@yellowfin.com.au";
String restPassword = "test";
String fileToImport = "/Downloads/Yellowfin-License.lic";
Path licenseFile = Paths.get(fileToImport);
byte[] fileContents = Files.readAllBytes(licenseFile);
String token = generateToken(host, restUsername, restPassword);
HttpEntity newLicenseFileMulitpartEntity = MultipartEntityBuilder
.create()
.setMode(HttpMultipartMode.LEGACY)
.setCharset(Charset.forName("UTF-8"))
.addBinaryBody("newLicence", fileContents , ContentType.DEFAULT_BINARY, licenseFile.getFileName().toString())
.build();
System.out.println("Upload License Content");
Content uploadLicenseContent = Request.post(host + "/api/rpc/licence-management/upload-licence")
.addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong() + ", token=" + token)
.addHeader("Accept", "application/vnd.yellowfin.api-v1+json")
.addHeader("Content-Type", newLicenseFileMulitpartEntity.getContentType())
.addHeader("cache-control", "no-cache")
.body(newLicenseFileMulitpartEntity)
.execute()
.returnContent();
System.out.println("License Upload Complete");
System.out.println(uploadLicenseContent.asString());
}
/*
* This function generates an access token for a user that will grant them access to
* call REST API endpoints.
*/
public static String generateToken(String host, String username, String password) throws IOException {
Content c = Request.post(host + "/api/refresh-tokens")
.addHeader("Authorization", "YELLOWFIN ts=" + System.currentTimeMillis() + " , nonce=" + new Random().nextLong())
.addHeader("Accept", "application/vnd.yellowfin.api-v1+json")
.addHeader("Content-Type", "application/json")
.bodyString("{ \"userName\": \""+ username + "\",\"password\": \""+ password + "\"}", null)
.execute().returnContent();
JsonObject jsonObject = new JsonParser().parse(c.asString()).getAsJsonObject();
JsonElement accessToken = jsonObject.getAsJsonObject("_embedded").getAsJsonObject("accessToken").get("securityToken");
if (accessToken!=null) {
System.out.println("Access Token: " + accessToken);
} else {
System.out.println("Token not retrieved successfully");
System.exit(-1);
}
return accessToken.getAsString();
}
} |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace YellowfinAPIExamples
{
public class UploadALicenseFile
{
public static async Task Main(string[] args)
{
Console.WriteLine("Upload License File");
string host = "http://localhost:8080/Yellowfin";
string restUsername = "admin@yellowfin.com.au";
string restPassword = "test";
string fileToImport = "/Downloads/Yellowfin-License.lic";
byte[] fileContents = File.ReadAllBytes(fileToImport);
string token = await GenerateToken(host, restUsername, restPassword);
MultipartFormDataContent multipartContent = new MultipartFormDataContent();
ByteArrayContent fileContent = new ByteArrayContent(fileContents);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
multipartContent.Add(fileContent, "newLicence", Path.GetFileName(fileToImport));
Console.WriteLine("Upload License Content");
using (var httpClient = new HttpClient())
{
long nonce = new Random().NextInt64();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("YELLOWFIN", $"ts={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}, nonce={nonce}, token={token}");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.yellowfin.api-v1+json"));
HttpResponseMessage response = await httpClient.PostAsync(host + "/api/rpc/licence-management/upload-licence", multipartContent);
if (response.IsSuccessStatusCode)
{
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine("License Upload Complete");
Console.WriteLine(responseBody);
}
else
{
Console.WriteLine($"Failed to upload license. Status code: {response.StatusCode}");
}
}
}
public static async Task<string> GenerateToken(string host, string username, string password)
{
using (var client = new HttpClient())
{
long nonce = new Random().NextInt64();
var requestBody = new
{
userName = username,
password = password
};
string jsonRequestBody = JsonConvert.SerializeObject(requestBody);
var request = new HttpRequestMessage(HttpMethod.Post, host + "/api/refresh-tokens");
request.Headers.Add("Authorization", $"YELLOWFIN ts={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}, nonce={nonce}");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.yellowfin.api-v1+json"));
request.Content = new StringContent(jsonRequestBody, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.SendAsync(request);
string responseContent = await response.Content.ReadAsStringAsync();
JObject jsonObject = JsonConvert.DeserializeObject<JObject>(responseContent);
string accessToken = jsonObject["_embedded"]["accessToken"]["securityToken"].ToString();
if (!string.IsNullOrEmpty(accessToken))
{
Console.WriteLine("Access Token: " + accessToken);
}
else
{
Console.WriteLine("Token not retrieved successfully");
Environment.Exit(-1);
}
return accessToken;
}
}
}
} |
Code Block | ||||
---|---|---|---|---|
| ||||
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"math/rand"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"time"
)
func main() {
fmt.Println("Upload License File")
host := "http://localhost:8080/Yellowfin"
restUsername := "admin@yellowfin.com.au"
restPassword := "test"
fileToImport := "/Downloads/Yellowfin-License.lic"
filePath := filepath.Clean(fileToImport)
token, err := generateToken(host, restUsername, restPassword)
if err != nil {
fmt.Println("Error generating token:", err)
return
}
file, err := os.Open(filePath)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("newLicence", filepath.Base(filePath))
if err != nil {
fmt.Println("Error writing to buffer:", err)
return
}
_, err = io.Copy(part, file)
if err != nil {
fmt.Println("Error copying file to buffer:", err)
return
}
err = writer.Close()
if err != nil {
fmt.Println("Error closing writer:", err)
return
}
nonce := rand.Int63()
req, err := http.NewRequest("POST", host+"/api/rpc/licence-management/upload-licence", body)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d, token=%s", time.Now().UnixMilli(), nonce, token))
req.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json")
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
fmt.Println("License Upload Complete")
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return
}
fmt.Println(string(responseBody))
}
func generateToken(host, restUsername, restPassword string) (string, error) {
nonce := rand.Int63()
reqBody := fmt.Sprintf(`{"userName": "%s", "password": "%s"}`, restUsername, restPassword)
req, err := http.NewRequest("POST", host+"/api/refresh-tokens", bytes.NewBufferString(reqBody))
if err != nil {
return "", err
}
req.Header.Set("Authorization", fmt.Sprintf("YELLOWFIN ts=%d, nonce=%d", time.Now().UnixMilli(), nonce))
req.Header.Set("Accept", "application/vnd.yellowfin.api-v1+json")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var jsonResponse map[string]interface{}
if err := json.Unmarshal(respBody, &jsonResponse); err != nil {
return "", err
}
accessToken, ok := jsonResponse["_embedded"].(map[string]interface{})["accessToken"].(map[string]interface{})["securityToken"].(string)
if !ok {
return "", fmt.Errorf("Token not retrieved successfully")
}
return accessToken, nil
} |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
const fs = require('fs');
const FormData = require('form-data');
const fetch = require('node-fetch');
async function main() {
const host = "http://localhost:8080/Yellowfin";
const restUsername = "admin@yellowfin.com.au";
const restPassword = "test";
const fileToImport = "/Downloads/Yellowfin-License.lic";
const fileContents = fs.readFileSync(fileToImport);
const fileName = fileToImport.split('/').pop(); // Extracting filename from path
const token = await generateToken(host, restUsername, restPassword);
if (!token) {
console.error("Failed to retrieve access token");
return;
}
console.log("Upload License File");
const formData = new FormData();
formData.append('newLicence', fileContents, { filename: fileName });
const headers = {
'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${generateNonce()}, token=${token}`,
'Accept': 'application/vnd.yellowfin.api-v1+json',
...formData.getHeaders()
};
try {
const response = await fetch(`${host}/api/rpc/licence-management/upload-licence`, {
method: 'POST',
headers: headers,
body: formData
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const responseBody = await response.text();
console.log("License Upload Complete");
console.log(responseBody);
} catch (error) {
console.error("Error:", error.message);
}
}
async function generateToken(host, restUsername, restPassword) {
const nonce = generateNonce();
const headers = {
'Authorization': `YELLOWFIN ts=${Date.now()}, nonce=${nonce}`,
'Accept': 'application/vnd.yellowfin.api-v1+json',
'Content-Type': 'application/json'
};
const body = JSON.stringify({
userName: restUsername,
password: restPassword
});
try {
const response = await fetch(`${host}/api/refresh-tokens`, {
method: 'POST',
headers: headers,
body: body
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const jsonResponse = await response.json();
const accessToken = jsonResponse._embedded.accessToken.securityToken;
if (accessToken) {
console.log(`Access Token: ${accessToken}`);
} else {
console.log("Token not retrieved");
}
return accessToken;
} catch (error) {
console.error("Error:", error.message);
}
return null;
}
function generateNonce() {
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
}
main(); |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<?php
function main() {
$host = "http://localhost:8080/Yellowfin";
$restUsername = "admin@yellowfin.com.au";
$restPassword = "test";
$fileToImport = "/Downloads/Yellowfin-License.lic";
try {
$token = generateToken($host, $restUsername, $restPassword);
} catch (Exception $e) {
echo "Error generating token: " . $e->getMessage();
return;
}
// Read file contents
try {
$fileContents = file_get_contents($fileToImport);
} catch (Exception $e) {
echo "Error reading file: " . $e->getMessage();
return;
}
$fileName = basename($fileToImport);
// Build multipart form data
$importOptions = json_encode(array(
"option1" => "value1",
"option2" => "value2"
));
$multipartBody = buildMultipartEntity($fileContents, $fileName, $importOptions);
echo "Upload License Content\n";
try {
$response = sendMultipartRequest($host, $token, $multipartBody, "/api/rpc/licence-management/upload-licence");
echo "License Upload Complete\n";
echo $response . "\n";
} catch (Exception $e) {
echo "Error uploading license: " . $e->getMessage();
}
}
function generateToken($host, $restUsername, $restPassword) {
// Generate nonce
$nonce = mt_rand();
// Create request body
$requestBody = json_encode(array(
"userName" => $restUsername,
"password" => $restPassword
));
// Create request headers
$headers = array(
'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . $nonce,
'Accept: application/vnd.yellowfin.api-v1+json',
'Content-Type: application/json'
);
try {
$response = httpRequest('POST', "$host/api/refresh-tokens", $headers, $requestBody);
$jsonResponse = json_decode($response, true);
// Get access token from response
if (isset($jsonResponse["_embedded"]["accessToken"]["securityToken"])) {
$accessToken = $jsonResponse["_embedded"]["accessToken"]["securityToken"];
echo "Access Token: " . $accessToken;
return $accessToken;
} else {
throw new Exception("Token not retrieved successfully");
}
} catch (Exception $e) {
throw new Exception("Error generating token: " . $e->getMessage());
}
}
function buildMultipartEntity($fileContents, $fileName, $importOptions) {
$boundary = uniqid();
$multipartBody = "--$boundary\r\n";
$multipartBody .= 'Content-Disposition: form-data; name="newLicence"; filename="' . $fileName . "\"\r\n";
$multipartBody .= "Content-Type: application/octet-stream\r\n\r\n";
$multipartBody .= $fileContents . "\r\n";
$multipartBody .= "--$boundary\r\n";
$multipartBody .= 'Content-Disposition: form-data; name="importOptions"' . "\r\n";
$multipartBody .= "Content-Type: application/json\r\n\r\n";
$multipartBody .= $importOptions . "\r\n";
$multipartBody .= "--$boundary--";
return $multipartBody;
}
function sendMultipartRequest($host, $token, $multipartBody, $endpoint) {
$boundary = substr($multipartBody, 2, strpos($multipartBody, "\r\n") - 2);
$headers = array(
'Authorization: YELLOWFIN ts=' . intval(microtime(true) * 1000) . ', nonce=' . mt_rand() . ', token=' . $token,
'Accept: application/vnd.yellowfin.api-v1+json',
'Content-Type: multipart/form-data; boundary=' . $boundary,
'cache-control: no-cache'
);
try {
$response = httpRequest('POST', "$host$endpoint", $headers, $multipartBody);
return $response;
} catch (Exception $e) {
throw new Exception("Error sending multipart request: " . $e->getMessage());
}
}
function httpRequest($method, $url, $headers, $data = null) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if ($data !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception('Error: ' . curl_error($ch));
}
curl_close($ch);
return $response;
}
main();
?> |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
import json
import random
import time
import os
import requests
def main():
host = "http://localhost:8080/Yellowfin"
rest_username = "admin@yellowfin.com.au"
rest_password = "test"
file_to_import = "/Downloads/Yellowfin-License.lic"
token = generate_token(host, rest_username, rest_password)
# Ensure the file exists
if not os.path.exists(file_to_import):
print(f"Error: File '{file_to_import}' not found.")
return
# Read file contents
with open(file_to_import, 'rb') as file:
file_contents = file.read()
# Prepare headers
headers = {
'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={random.randint(0, 2**63 - 1)}, token={token}',
'Accept': 'application/vnd.yellowfin.api-v1+json',
'cache-control': 'no-cache'
}
# Prepare multipart form data manually
files = {
'newLicence': (file_to_import, file_contents, 'application/octet-stream')
}
try:
response = requests.post(f"{host}/api/rpc/licence-management/upload-licence", headers=headers, files=files)
response.raise_for_status()
print("License Upload Complete")
print(response.text)
except requests.RequestException as e:
print(f"Error uploading license: {e}")
def generate_token(host, rest_username, rest_password):
nonce = random.randint(0, 2 ** 63 - 1)
# Create request body
request_body = json.dumps({
"userName": rest_username,
"password": rest_password
})
# Create request headers
headers = {
'Authorization': f'YELLOWFIN ts={int(time.time() * 1000)}, nonce={nonce}',
'Accept': 'application/vnd.yellowfin.api-v1+json',
'Content-Type': 'application/json'
}
try:
response = requests.post(f"{host}/api/refresh-tokens", headers=headers, data=request_body)
response.raise_for_status()
json_response = response.json()
access_token = json_response["_embedded"]["accessToken"]["securityToken"]
print("Access Token:", access_token)
return access_token
except requests.RequestException as e:
raise Exception("Token not retrieved successfully") from e
if __name__ == "__main__":
main() |
License Auto Provisioning
License Auto Provisioning is an inbuilt mechanism that allows for a Yellowfin instance to look for a new license file on a remote server. This can be configured by enabling the LicenceAutoProvision servlet in the web.xml.
This can be configured so that Yellowfin will connect to a server periodically to look for a new license. If a license file downloaded from the server differs from the currently installed license, it will be ingested and the license replaced. This allows an ISV with distributed installs to manage license updates remotely by updating the license at the defined server location.
The server can operate on HTTP, HTTPS or SFTP. HTTP and HTTPS support basic authentication. SFTP requires user authentication to connect.
Code Block |
---|
<servlet>
<servlet-name>LicenceAutoProvision</servlet-name>
<servlet-class>com.hof.servlet.LicenceAutoProvision</servlet-class>
<init-param>
<param-name>Enabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>TaskPeriodInterval</param-name>
<param-value>360</param-value>
</init-param>
<init-param>
<param-name>Protocol</param-name>
<param-value>http</param-value>
<!-- <param-value>https</param-value>-->
<!-- <param-value>sftp</param-value>-->
</init-param>
<init-param>
<param-name>Hostname</param-name>
<param-value>localhost</param-value>
</init-param>
<init-param>
<param-name>Port</param-name>
<param-value>8100</param-value>
</init-param>
<init-param>
<param-name>PathToLicenceFile</param-name>
<param-value>/static/licence.lic</param-value>
</init-param>
<init-param>
<param-name>Username</param-name>
<param-value>demo</param-value>
</init-param>
<init-param>
<param-name>Password</param-name>
<param-value>demo</param-value>
</init-param>
<load-on-startup><!-- can use 9 or greater, as long as its one of the last servlet initialized --></load-on-startup>
</servlet>
|