import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
-import java.util.List;
+import java.util.Base64;
import java.util.Map;
-import java.util.stream.Collectors;
-import javax.annotation.Nullable;
import org.json.JSONObject;
public final class SamlAuthStatusPageGenerator {
private static final String WEB_CONTEXT = "%WEB_CONTEXT%";
- private static final String STATUS = "%STATUS%";
- private static final String ERRORS = "%ERRORS%";
- private static final String WARNINGS = "%WARNINGS%";
- private static final String AVAILABLE_ATTRIBUTES = "%AVAILABLE_ATTRIBUTES%";
- private static final String ATTRIBUTE_MAPPINGS = "%ATTRIBUTE_MAPPINGS%";
+ private static final String SAML_AUTHENTICATION_STATUS = "%SAML_AUTHENTICATION_STATUS%";
private static final String HTML_TEMPLATE_NAME = "samlAuthResult.html";
private static Map<String, String> getSubstitutionsMap(SamlAuthenticationStatus samlAuthenticationStatus) {
return Map.of(
WEB_CONTEXT, "",
- STATUS, toJsString(samlAuthenticationStatus.getStatus()),
- ERRORS, toJsArrayFromList(samlAuthenticationStatus.getErrors()),
- WARNINGS, toJsArrayFromList(samlAuthenticationStatus.getWarnings()),
- AVAILABLE_ATTRIBUTES, new JSONObject(samlAuthenticationStatus.getAvailableAttributes()).toString(),
- ATTRIBUTE_MAPPINGS, new JSONObject(samlAuthenticationStatus.getMappedAttributes()).toString());
+ SAML_AUTHENTICATION_STATUS, getBase64EncodedStatus(samlAuthenticationStatus));
}
- private static String toJsString(@Nullable String inputString) {
- return String.format("'%s'", inputString != null ? inputString.replace("'", "\\'") : "");
- }
-
- private static String toJsArrayFromList(List<String> inputArray) {
- return "[" + inputArray.stream()
- .map(SamlAuthStatusPageGenerator::toJsString)
- .collect(Collectors.joining(",")) + "]";
+ private static String getBase64EncodedStatus(SamlAuthenticationStatus samlAuthenticationStatus) {
+ byte[] bytes = new JSONObject(samlAuthenticationStatus).toString().getBytes(StandardCharsets.UTF_8);
+ return String.format("'%s'", Base64.getEncoder().encodeToString(bytes));
}
private static String getPlainTemplate() {
function createSectionTitle(title) {
const node = document.createElement("h2");
- node.innerText = title;
+ node.textContent = title;
return node;
}
arr.forEach((item) => {
const message = document.createElement("li");
message.className = className;
- message.innerText = item;
+ message.textContent = item;
list.appendChild(message);
});
const row = document.createElement("tr");
const keyNode = document.createElement("td");
- keyNode.innerText = key;
+ keyNode.textContent = key;
row.appendChild(keyNode);
const valueNode = document.createElement("td");
container.appendChild(box);
}
- const status = %STATUS%;
- const attributes = %AVAILABLE_ATTRIBUTES%;
- const mappings = %ATTRIBUTE_MAPPINGS%;
- const errors = %ERRORS%;
- const warnings = %WARNINGS%;
+ const response = %SAML_AUTHENTICATION_STATUS%;
+ const decodedStatus = JSON.parse(atob(response));
+ const status = decodedStatus.status;
+ const attributes = decodedStatus.availableAttributes;
+ const mappings = decodedStatus.mappedAttributes;
+ const errors = decodedStatus.errors;
+ const warnings = decodedStatus.warnings;
// Switch status class
const statusNode = document.querySelector("#status");
statusNode.classList.add(status);
- statusNode.innerText = status;
+ statusNode.textContent = status;
// generate content
const container = document.querySelector("#content");
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.sonar.auth.saml.SamlAuthStatusPageGenerator.getSamlAuthStatusHtml;
public class SamlAuthStatusPageGeneratorTest {
-
- private final SamlAuthenticationStatus samlAuthenticationStatus = mock(SamlAuthenticationStatus.class);
-
- private static final String HTML_TEMPLATE_NAME = "samlAuthResultComplete.html";
private static final String EMPTY_HTML_TEMPLATE_NAME = "samlAuthResultEmpty.html";
- @Test
- public void test_full_html_generation() {
-
- when(samlAuthenticationStatus.getStatus()).thenReturn("success");
- when(samlAuthenticationStatus.getErrors()).thenReturn(List.of("error1", "error2 'with message'"));
- when(samlAuthenticationStatus.getWarnings()).thenReturn(List.of("warning1", "warning2 'with message'"));
- when(samlAuthenticationStatus.getAvailableAttributes()).thenReturn(Map.of("key1", List.of("value1", "value2 with weird chars \n\t\"\\"), "key2", List.of("value3", "value4")));
- when(samlAuthenticationStatus.getMappedAttributes()).thenReturn(Map.of("key1", List.of("value1", "value2"), "key2", List.of("value3", "value4")));
-
- String completeHtmlTemplate = getSamlAuthStatusHtml(samlAuthenticationStatus);
- String expectedTemplate = loadTemplateFromResources(HTML_TEMPLATE_NAME);
-
- assertEquals(expectedTemplate, completeHtmlTemplate);
-
- }
-
@Test
public void test_full_html_generation_with_empty_values() {
+ SamlAuthenticationStatus samlAuthenticationStatus = mock(SamlAuthenticationStatus.class);
when(samlAuthenticationStatus.getStatus()).thenReturn(null);
when(samlAuthenticationStatus.getErrors()).thenReturn(new ArrayList<>());
+++ /dev/null
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" charset="UTF-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
- <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png" />
- <link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png" />
- <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png" />
- <link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png" />
- <link
- rel="apple-touch-icon"
- sizes="114x114"
- href="/apple-touch-icon-114x114.png"
- />
- <link
- rel="apple-touch-icon"
- sizes="120x120"
- href="/apple-touch-icon-120x120.png"
- />
- <link
- rel="apple-touch-icon"
- sizes="144x144"
- href="/apple-touch-icon-144x144.png"
- />
- <link
- rel="apple-touch-icon"
- sizes="152x152"
- href="/apple-touch-icon-152x152.png"
- />
- <link
- rel="apple-touch-icon"
- sizes="180x180"
- href="/apple-touch-icon-180x180.png"
- />
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
- <meta name="application-name" content="SonarQube" />
- <meta name="msapplication-TileColor" content="#FFFFFF" />
- <meta name="msapplication-TileImage" content="/mstile-512x512.png" />
- <title>SAML Authentication Test</title>
-
- <style>
- body {
- background-color: #f3f3f3;
- }
-
- h1 {
- margin: 0 8px 8px;
- }
- h2 {
- margin: 0 0 8px;
- }
-
- ul {
- list-style: none;
- margin: 0 8px;
- padding: 0;
- }
-
- li + li {
- padding-top: 12px;
- margin-top: 12px;
- border-top: 1px solid rgba(150, 150, 150, 0.5);
- }
-
- table {
- border-collapse: collapse;
- }
-
- tr:nth-child(2n) {
- background-color: #e6e6e6;
- }
-
- td {
- border: 1px solid #a3a3a3;
- padding: 4px 24px 4px 8px;
- vertical-align: top;
- font-family: "Courier New", Courier, monospace;
- white-space: pre-line;
- }
-
- #content {
- padding: 16px;
- }
-
- .box {
- padding: 8px;
- margin: 8px;
- border: 1px solid #e6e6e6;
- background-color: white;
- box-shadow: 2px 2px 3px 0px rgba(0, 0, 0, 0.5);
- }
-
- #status {
- padding: 16px 8px;
- font-size: large;
- color: white;
- }
-
- .error {
- background-color: #d02f3a;
- }
-
- .success {
- background-color: #008a25;
- }
- </style>
- </head>
-
- <body>
- <div id="content">
- <h1>SAML Authentication Test</h1>
- <div class="box">
- <div id="status"></div>
- </div>
- </div>
-
- <script>
- function createBox() {
- const box = document.createElement("div");
- box.className = "box";
- return box;
- }
-
- function createSectionTitle(title) {
- const node = document.createElement("h2");
- node.innerText = title;
- return node;
- }
-
- function createList(arr, className = "") {
- const list = document.createElement("ul");
-
- arr.forEach((item) => {
- const message = document.createElement("li");
- message.className = className;
- message.innerText = item;
- list.appendChild(message);
- });
-
- return list;
- }
-
- function createTable(obj) {
- const table = document.createElement("table");
- const tbody = document.createElement("tbody");
- table.appendChild(tbody);
-
- Object.keys(obj).forEach((key) => {
- const row = document.createElement("tr");
-
- const keyNode = document.createElement("td");
- keyNode.innerText = key;
- row.appendChild(keyNode);
-
- const valueNode = document.createElement("td");
- // wrap in array, to handle single values as well
- valueNode.textContent = [].concat(obj[key]).join("\r\n");
- row.appendChild(valueNode);
-
- tbody.appendChild(row);
- });
-
- return table;
- }
-
- function addSection(container, title, contents) {
- const box = createBox();
-
- box.appendChild(createSectionTitle(title));
- box.appendChild(contents);
-
- container.appendChild(box);
- }
-
- const status = 'success';
- const attributes = {"key1":["value1","value2 with weird chars \n\t\"\\"],"key2":["value3","value4"]};
- const mappings = {"key1":["value1","value2"],"key2":["value3","value4"]};
- const errors = ['error1','error2 \'with message\''];
- const warnings = ['warning1','warning2 \'with message\''];
-
- // Switch status class
- const statusNode = document.querySelector("#status");
- statusNode.classList.add(status);
- statusNode.innerText = status;
-
- // generate content
- const container = document.querySelector("#content");
-
- if (warnings && warnings.length > 0) {
- addSection(container, "Warnings", createList(warnings));
- }
-
- if (status === "error" && errors && errors.length > 0) {
- addSection(container, "Errors", createList(errors));
- }
-
- if (status === "success") {
- if (attributes && Object.keys(attributes).length > 0) {
- addSection(container, "Available attributes", createTable(attributes));
- }
-
- if (mappings && Object.keys(mappings).length > 0) {
- addSection(container, "Attribute mappings", createTable(mappings));
- }
- }
- </script>
- </body>
-</html>
function createSectionTitle(title) {
const node = document.createElement("h2");
- node.innerText = title;
+ node.textContent = title;
return node;
}
arr.forEach((item) => {
const message = document.createElement("li");
message.className = className;
- message.innerText = item;
+ message.textContent = item;
list.appendChild(message);
});
const row = document.createElement("tr");
const keyNode = document.createElement("td");
- keyNode.innerText = key;
+ keyNode.textContent = key;
row.appendChild(keyNode);
const valueNode = document.createElement("td");
container.appendChild(box);
}
- const status = '';
- const attributes = {};
- const mappings = {};
- const errors = [];
- const warnings = [];
+ const response = 'eyJ3YXJuaW5ncyI6W10sImF2YWlsYWJsZUF0dHJpYnV0ZXMiOnt9LCJlcnJvcnMiOltdLCJtYXBwZWRBdHRyaWJ1dGVzIjp7fX0=';
+ const decodedStatus = JSON.parse(atob(response));
+ const status = decodedStatus.status;
+ const attributes = decodedStatus.availableAttributes;
+ const mappings = decodedStatus.mappedAttributes;
+ const errors = decodedStatus.errors;
+ const warnings = decodedStatus.warnings;
// Switch status class
const statusNode = document.querySelector("#status");
statusNode.classList.add(status);
- statusNode.innerText = status;
+ statusNode.textContent = status;
// generate content
const container = document.querySelector("#content");