APi test
42
build.gradle
@ -10,6 +10,7 @@ plugins {
|
||||
//id "nebula.lint" version "19.0.3"
|
||||
id("org.panteleyev.jpackageplugin") version "1.6.1"
|
||||
id "org.sonarqube" version "6.1.0.5360"
|
||||
id "jacoco"
|
||||
}
|
||||
|
||||
import com.github.jk1.license.render.*
|
||||
@ -363,7 +364,7 @@ launch4j {
|
||||
|
||||
spotless {
|
||||
java {
|
||||
target project.fileTree('src').include('**/*.java')
|
||||
target project.fileTree('src').include('**/*.java').exclude('**/test/**')
|
||||
|
||||
googleJavaFormat("1.25.2").aosp().reorderImports(false)
|
||||
|
||||
@ -588,19 +589,36 @@ task printMacVersion {
|
||||
tasks.named('generateOpenApiDocs') {
|
||||
doNotTrackState("Tracking state is not supported for this task")
|
||||
}
|
||||
tasks.register('convertersTest', Test) {
|
||||
group = 'verification'
|
||||
description = 'Run only converter integration tests under controller/api/converters'
|
||||
|
||||
// 指定来自 test 源集的字节码和 classpath
|
||||
testClassesDirs = sourceSets.test.get().output.classesDirs
|
||||
classpath = sourceSets.test.get().runtimeClasspath
|
||||
|
||||
// 只包含 controller/api/converters 目录下的测试类
|
||||
include '**/stirling/software/SPDF/controller/api/converters/ConvertMarkdownToPdfIntegrationTest.java'
|
||||
}
|
||||
|
||||
// 确保默认 test 任务还是用 JUnit Platform
|
||||
tasks.named('test') {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.register('convertersTest', Test) {
|
||||
group = 'verification'
|
||||
description = 'Run only markdown/pdf converter unit tests'
|
||||
useJUnitPlatform()
|
||||
|
||||
// 只包含 markdown/pdf 的单元测试类
|
||||
include '**/ConvertMarkdownToPdfTest.class'
|
||||
include '**/ConvertPDFToMarkdownTest.class'
|
||||
|
||||
testClassesDirs = sourceSets.test.output.classesDirs
|
||||
classpath = sourceSets.test.runtimeClasspath
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
dependsOn convertersTest // 关键:只用你定义的任务来生成覆盖率
|
||||
|
||||
reports {
|
||||
html.required = true
|
||||
xml.required = true
|
||||
csv.required = false
|
||||
}
|
||||
|
||||
executionData fileTree(buildDir).include(
|
||||
"/jacoco/convertersTest.exec"
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ package stirling.software.SPDF.utils;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
import net.bytebuddy.implementation.bytecode.Throw;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
@ -13,8 +12,6 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = {
|
||||
|
@ -1,82 +0,0 @@
|
||||
package stirling.software.SPDF.utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest;
|
||||
|
||||
public class FileToPdfTest {
|
||||
|
||||
/**
|
||||
* Test the HTML to PDF conversion. This test expects an IOException when an empty HTML input is
|
||||
* provided.
|
||||
*/
|
||||
@Test
|
||||
public void testConvertHtmlToPdf() {
|
||||
HTMLToPdfRequest request = new HTMLToPdfRequest();
|
||||
byte[] fileBytes = new byte[0]; // Sample file bytes (empty input)
|
||||
String fileName = "test.html"; // Sample file name indicating an HTML file
|
||||
boolean disableSanitize = false; // Flag to control sanitization
|
||||
|
||||
// Expect an IOException to be thrown due to empty input
|
||||
Throwable thrown =
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
() ->
|
||||
FileToPdf.convertHtmlToPdf(
|
||||
"/path/", request, fileBytes, fileName, disableSanitize));
|
||||
assertNotNull(thrown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test sanitizeZipFilename with null or empty input. It should return an empty string in these
|
||||
* cases.
|
||||
*/
|
||||
@Test
|
||||
public void testSanitizeZipFilename_NullOrEmpty() {
|
||||
assertEquals("", FileToPdf.sanitizeZipFilename(null));
|
||||
assertEquals("", FileToPdf.sanitizeZipFilename(" "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test sanitizeZipFilename to ensure it removes path traversal sequences. This includes
|
||||
* removing both forward and backward slash sequences.
|
||||
*/
|
||||
@Test
|
||||
public void testSanitizeZipFilename_RemovesTraversalSequences() {
|
||||
String input = "../some/../path/..\\to\\file.txt";
|
||||
String expected = "some/path/to/file.txt";
|
||||
|
||||
// Print output for debugging purposes
|
||||
System.out.println("sanitizeZipFilename " + FileToPdf.sanitizeZipFilename(input));
|
||||
System.out.flush();
|
||||
|
||||
// Expect that the method replaces backslashes with forward slashes
|
||||
// and removes path traversal sequences
|
||||
assertEquals(expected, FileToPdf.sanitizeZipFilename(input));
|
||||
}
|
||||
|
||||
/** Test sanitizeZipFilename to ensure that it removes leading drive letters and slashes. */
|
||||
@Test
|
||||
public void testSanitizeZipFilename_RemovesLeadingDriveAndSlashes() {
|
||||
String input = "C:\\folder\\file.txt";
|
||||
String expected = "folder/file.txt";
|
||||
assertEquals(expected, FileToPdf.sanitizeZipFilename(input));
|
||||
|
||||
input = "/folder/file.txt";
|
||||
expected = "folder/file.txt";
|
||||
assertEquals(expected, FileToPdf.sanitizeZipFilename(input));
|
||||
}
|
||||
|
||||
/** Test sanitizeZipFilename to verify that safe filenames remain unchanged. */
|
||||
@Test
|
||||
public void testSanitizeZipFilename_NoChangeForSafeNames() {
|
||||
String input = "folder/subfolder/file.txt";
|
||||
assertEquals(input, FileToPdf.sanitizeZipFilename(input));
|
||||
}
|
||||
}
|
1
testResult/html/index.html
Normal file
BIN
testResult/html/jacoco-resources/branchfc.gif
Normal file
After Width: | Height: | Size: 91 B |
BIN
testResult/html/jacoco-resources/branchnc.gif
Normal file
After Width: | Height: | Size: 91 B |
BIN
testResult/html/jacoco-resources/branchpc.gif
Normal file
After Width: | Height: | Size: 91 B |
BIN
testResult/html/jacoco-resources/bundle.gif
Normal file
After Width: | Height: | Size: 709 B |
BIN
testResult/html/jacoco-resources/class.gif
Normal file
After Width: | Height: | Size: 586 B |
BIN
testResult/html/jacoco-resources/down.gif
Normal file
After Width: | Height: | Size: 67 B |
BIN
testResult/html/jacoco-resources/greenbar.gif
Normal file
After Width: | Height: | Size: 91 B |
BIN
testResult/html/jacoco-resources/group.gif
Normal file
After Width: | Height: | Size: 351 B |
BIN
testResult/html/jacoco-resources/method.gif
Normal file
After Width: | Height: | Size: 193 B |
BIN
testResult/html/jacoco-resources/package.gif
Normal file
After Width: | Height: | Size: 227 B |
13
testResult/html/jacoco-resources/prettify.css
Normal file
@ -0,0 +1,13 @@
|
||||
/* Pretty printing styles. Used with prettify.js. */
|
||||
|
||||
.str { color: #2A00FF; }
|
||||
.kwd { color: #7F0055; font-weight:bold; }
|
||||
.com { color: #3F5FBF; }
|
||||
.typ { color: #606; }
|
||||
.lit { color: #066; }
|
||||
.pun { color: #660; }
|
||||
.pln { color: #000; }
|
||||
.tag { color: #008; }
|
||||
.atn { color: #606; }
|
||||
.atv { color: #080; }
|
||||
.dec { color: #606; }
|
1510
testResult/html/jacoco-resources/prettify.js
Normal file
BIN
testResult/html/jacoco-resources/redbar.gif
Normal file
After Width: | Height: | Size: 91 B |
243
testResult/html/jacoco-resources/report.css
Normal file
@ -0,0 +1,243 @@
|
||||
body, td {
|
||||
font-family:sans-serif;
|
||||
font-size:10pt;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight:bold;
|
||||
font-size:18pt;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
border:#d6d3ce 1px solid;
|
||||
padding:2px 4px 2px 4px;
|
||||
}
|
||||
|
||||
.breadcrumb .info {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.breadcrumb .info a {
|
||||
margin-left:8px;
|
||||
}
|
||||
|
||||
.el_report {
|
||||
padding-left:18px;
|
||||
background-image:url(report.gif);
|
||||
background-position:left center;
|
||||
background-repeat:no-repeat;
|
||||
}
|
||||
|
||||
.el_group {
|
||||
padding-left:18px;
|
||||
background-image:url(group.gif);
|
||||
background-position:left center;
|
||||
background-repeat:no-repeat;
|
||||
}
|
||||
|
||||
.el_bundle {
|
||||
padding-left:18px;
|
||||
background-image:url(bundle.gif);
|
||||
background-position:left center;
|
||||
background-repeat:no-repeat;
|
||||
}
|
||||
|
||||
.el_package {
|
||||
padding-left:18px;
|
||||
background-image:url(package.gif);
|
||||
background-position:left center;
|
||||
background-repeat:no-repeat;
|
||||
}
|
||||
|
||||
.el_class {
|
||||
padding-left:18px;
|
||||
background-image:url(class.gif);
|
||||
background-position:left center;
|
||||
background-repeat:no-repeat;
|
||||
}
|
||||
|
||||
.el_source {
|
||||
padding-left:18px;
|
||||
background-image:url(source.gif);
|
||||
background-position:left center;
|
||||
background-repeat:no-repeat;
|
||||
}
|
||||
|
||||
.el_method {
|
||||
padding-left:18px;
|
||||
background-image:url(method.gif);
|
||||
background-position:left center;
|
||||
background-repeat:no-repeat;
|
||||
}
|
||||
|
||||
.el_session {
|
||||
padding-left:18px;
|
||||
background-image:url(session.gif);
|
||||
background-position:left center;
|
||||
background-repeat:no-repeat;
|
||||
}
|
||||
|
||||
pre.source {
|
||||
border:#d6d3ce 1px solid;
|
||||
font-family:monospace;
|
||||
}
|
||||
|
||||
pre.source ol {
|
||||
margin-bottom: 0px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
pre.source li {
|
||||
border-left: 1px solid #D6D3CE;
|
||||
color: #A0A0A0;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
pre.source span.fc {
|
||||
background-color:#ccffcc;
|
||||
}
|
||||
|
||||
pre.source span.nc {
|
||||
background-color:#ffaaaa;
|
||||
}
|
||||
|
||||
pre.source span.pc {
|
||||
background-color:#ffffcc;
|
||||
}
|
||||
|
||||
pre.source span.bfc {
|
||||
background-image: url(branchfc.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 2px center;
|
||||
}
|
||||
|
||||
pre.source span.bfc:hover {
|
||||
background-color:#80ff80;
|
||||
}
|
||||
|
||||
pre.source span.bnc {
|
||||
background-image: url(branchnc.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 2px center;
|
||||
}
|
||||
|
||||
pre.source span.bnc:hover {
|
||||
background-color:#ff8080;
|
||||
}
|
||||
|
||||
pre.source span.bpc {
|
||||
background-image: url(branchpc.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 2px center;
|
||||
}
|
||||
|
||||
pre.source span.bpc:hover {
|
||||
background-color:#ffff80;
|
||||
}
|
||||
|
||||
table.coverage {
|
||||
empty-cells:show;
|
||||
border-collapse:collapse;
|
||||
}
|
||||
|
||||
table.coverage thead {
|
||||
background-color:#e0e0e0;
|
||||
}
|
||||
|
||||
table.coverage thead td {
|
||||
white-space:nowrap;
|
||||
padding:2px 14px 0px 6px;
|
||||
border-bottom:#b0b0b0 1px solid;
|
||||
}
|
||||
|
||||
table.coverage thead td.bar {
|
||||
border-left:#cccccc 1px solid;
|
||||
}
|
||||
|
||||
table.coverage thead td.ctr1 {
|
||||
text-align:right;
|
||||
border-left:#cccccc 1px solid;
|
||||
}
|
||||
|
||||
table.coverage thead td.ctr2 {
|
||||
text-align:right;
|
||||
padding-left:2px;
|
||||
}
|
||||
|
||||
table.coverage thead td.sortable {
|
||||
cursor:pointer;
|
||||
background-image:url(sort.gif);
|
||||
background-position:right center;
|
||||
background-repeat:no-repeat;
|
||||
}
|
||||
|
||||
table.coverage thead td.up {
|
||||
background-image:url(up.gif);
|
||||
}
|
||||
|
||||
table.coverage thead td.down {
|
||||
background-image:url(down.gif);
|
||||
}
|
||||
|
||||
table.coverage tbody td {
|
||||
white-space:nowrap;
|
||||
padding:2px 6px 2px 6px;
|
||||
border-bottom:#d6d3ce 1px solid;
|
||||
}
|
||||
|
||||
table.coverage tbody tr:hover {
|
||||
background: #f0f0d0 !important;
|
||||
}
|
||||
|
||||
table.coverage tbody td.bar {
|
||||
border-left:#e8e8e8 1px solid;
|
||||
}
|
||||
|
||||
table.coverage tbody td.ctr1 {
|
||||
text-align:right;
|
||||
padding-right:14px;
|
||||
border-left:#e8e8e8 1px solid;
|
||||
}
|
||||
|
||||
table.coverage tbody td.ctr2 {
|
||||
text-align:right;
|
||||
padding-right:14px;
|
||||
padding-left:2px;
|
||||
}
|
||||
|
||||
table.coverage tfoot td {
|
||||
white-space:nowrap;
|
||||
padding:2px 6px 2px 6px;
|
||||
}
|
||||
|
||||
table.coverage tfoot td.bar {
|
||||
border-left:#e8e8e8 1px solid;
|
||||
}
|
||||
|
||||
table.coverage tfoot td.ctr1 {
|
||||
text-align:right;
|
||||
padding-right:14px;
|
||||
border-left:#e8e8e8 1px solid;
|
||||
}
|
||||
|
||||
table.coverage tfoot td.ctr2 {
|
||||
text-align:right;
|
||||
padding-right:14px;
|
||||
padding-left:2px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top:20px;
|
||||
border-top:#d6d3ce 1px solid;
|
||||
padding-top:2px;
|
||||
font-size:8pt;
|
||||
color:#a0a0a0;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color:#a0a0a0;
|
||||
}
|
||||
|
||||
.right {
|
||||
float:right;
|
||||
}
|
BIN
testResult/html/jacoco-resources/report.gif
Normal file
After Width: | Height: | Size: 363 B |
BIN
testResult/html/jacoco-resources/session.gif
Normal file
After Width: | Height: | Size: 213 B |
BIN
testResult/html/jacoco-resources/sort.gif
Normal file
After Width: | Height: | Size: 58 B |
148
testResult/html/jacoco-resources/sort.js
Normal file
@ -0,0 +1,148 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2024 Mountainminds GmbH & Co. KG and Contributors
|
||||
* This program and the accompanying materials are made available under
|
||||
* the terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Marc R. Hoffmann - initial API and implementation
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* Sets the initial sorting derived from the hash.
|
||||
*
|
||||
* @param linkelementids
|
||||
* list of element ids to search for links to add sort inidcator
|
||||
* hash links
|
||||
*/
|
||||
function initialSort(linkelementids) {
|
||||
window.linkelementids = linkelementids;
|
||||
var hash = window.location.hash;
|
||||
if (hash) {
|
||||
var m = hash.match(/up-./);
|
||||
if (m) {
|
||||
var header = window.document.getElementById(m[0].charAt(3));
|
||||
if (header) {
|
||||
sortColumn(header, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var m = hash.match(/dn-./);
|
||||
if (m) {
|
||||
var header = window.document.getElementById(m[0].charAt(3));
|
||||
if (header) {
|
||||
sortColumn(header, false);
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the columns with the given header dependening on the current sort state.
|
||||
*/
|
||||
function toggleSort(header) {
|
||||
var sortup = header.className.indexOf('down ') == 0;
|
||||
sortColumn(header, sortup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the columns with the given header in the given direction.
|
||||
*/
|
||||
function sortColumn(header, sortup) {
|
||||
var table = header.parentNode.parentNode.parentNode;
|
||||
var body = table.tBodies[0];
|
||||
var colidx = getNodePosition(header);
|
||||
|
||||
resetSortedStyle(table);
|
||||
|
||||
var rows = body.rows;
|
||||
var sortedrows = [];
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
r = rows[i];
|
||||
sortedrows[parseInt(r.childNodes[colidx].id.slice(1))] = r;
|
||||
}
|
||||
|
||||
var hash;
|
||||
|
||||
if (sortup) {
|
||||
for (var i = sortedrows.length - 1; i >= 0; i--) {
|
||||
body.appendChild(sortedrows[i]);
|
||||
}
|
||||
header.className = 'up ' + header.className;
|
||||
hash = 'up-' + header.id;
|
||||
} else {
|
||||
for (var i = 0; i < sortedrows.length; i++) {
|
||||
body.appendChild(sortedrows[i]);
|
||||
}
|
||||
header.className = 'down ' + header.className;
|
||||
hash = 'dn-' + header.id;
|
||||
}
|
||||
|
||||
setHash(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the sort indicator as a hash to the document URL and all links.
|
||||
*/
|
||||
function setHash(hash) {
|
||||
window.document.location.hash = hash;
|
||||
ids = window.linkelementids;
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
setHashOnAllLinks(document.getElementById(ids[i]), hash);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend all links within the given tag with the given hash.
|
||||
*/
|
||||
function setHashOnAllLinks(tag, hash) {
|
||||
links = tag.getElementsByTagName("a");
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
var a = links[i];
|
||||
var href = a.href;
|
||||
var hashpos = href.indexOf("#");
|
||||
if (hashpos != -1) {
|
||||
href = href.substring(0, hashpos);
|
||||
}
|
||||
a.href = href + "#" + hash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the position of a element within its parent.
|
||||
*/
|
||||
function getNodePosition(element) {
|
||||
var pos = -1;
|
||||
while (element) {
|
||||
element = element.previousSibling;
|
||||
pos++;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the sorting indicator style from all headers.
|
||||
*/
|
||||
function resetSortedStyle(table) {
|
||||
for (var c = table.tHead.firstChild.firstChild; c; c = c.nextSibling) {
|
||||
if (c.className) {
|
||||
if (c.className.indexOf('down ') == 0) {
|
||||
c.className = c.className.slice(5);
|
||||
}
|
||||
if (c.className.indexOf('up ') == 0) {
|
||||
c.className = c.className.slice(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window['initialSort'] = initialSort;
|
||||
window['toggleSort'] = toggleSort;
|
||||
|
||||
})();
|
BIN
testResult/html/jacoco-resources/source.gif
Normal file
After Width: | Height: | Size: 354 B |
BIN
testResult/html/jacoco-resources/up.gif
Normal file
After Width: | Height: | Size: 67 B |
1
testResult/html/jacoco-sessions.html
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CMSProcessableInputStream</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">org.apache.pdfbox.examples.signature</a> > <span class="el_class">CMSProcessableInputStream</span></div><h1>CMSProcessableInputStream</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">33 of 33</td><td class="ctr2">0%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">5</td><td class="ctr2">5</td><td class="ctr1">11</td><td class="ctr2">11</td><td class="ctr1">5</td><td class="ctr2">5</td></tr></tfoot><tbody><tr><td id="a1"><a href="CMSProcessableInputStream.java.html#L40" class="el_method">CMSProcessableInputStream(InputStream)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="9" alt="9"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">1</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h2">2</td><td class="ctr2" id="i2">2</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a0"><a href="CMSProcessableInputStream.java.html#L43" class="el_method">CMSProcessableInputStream(ASN1ObjectIdentifier, InputStream)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="9" alt="9"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h0">4</td><td class="ctr2" id="i0">4</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a4"><a href="CMSProcessableInputStream.java.html#L56" class="el_method">write(OutputStream)</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="9" alt="9"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h1">3</td><td class="ctr2" id="i1">3</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr><tr><td id="a2"><a href="CMSProcessableInputStream.java.html#L50" class="el_method">getContent()</a></td><td class="bar" id="b3"><img src="../jacoco-resources/redbar.gif" width="40" height="10" title="3" alt="3"/></td><td class="ctr2" id="c3">0%</td><td class="bar" id="d3"/><td class="ctr2" id="e3">n/a</td><td class="ctr1" id="f3">1</td><td class="ctr2" id="g3">1</td><td class="ctr1" id="h3">1</td><td class="ctr2" id="i3">1</td><td class="ctr1" id="j3">1</td><td class="ctr2" id="k3">1</td></tr><tr><td id="a3"><a href="CMSProcessableInputStream.java.html#L62" class="el_method">getContentType()</a></td><td class="bar" id="b4"><img src="../jacoco-resources/redbar.gif" width="40" height="10" title="3" alt="3"/></td><td class="ctr2" id="c4">0%</td><td class="bar" id="d4"/><td class="ctr2" id="e4">n/a</td><td class="ctr1" id="f4">1</td><td class="ctr2" id="g4">1</td><td class="ctr1" id="h4">1</td><td class="ctr2" id="i4">1</td><td class="ctr1" id="j4">1</td><td class="ctr2" id="k4">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CMSProcessableInputStream.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">org.apache.pdfbox.examples.signature</a> > <span class="el_source">CMSProcessableInputStream.java</span></div><h1>CMSProcessableInputStream.java</h1><pre class="source lang-java linenums">/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.pdfbox.examples.signature;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
|
||||
import org.bouncycastle.cms.CMSException;
|
||||
import org.bouncycastle.cms.CMSTypedData;
|
||||
|
||||
/**
|
||||
* Wraps a InputStream into a CMSProcessable object for bouncy castle. It's a memory saving
|
||||
* alternative to the {@link org.bouncycastle.cms.CMSProcessableByteArray CMSProcessableByteArray}
|
||||
* class.
|
||||
*
|
||||
* @author Thomas Chojecki
|
||||
*/
|
||||
class CMSProcessableInputStream implements CMSTypedData {
|
||||
private final InputStream in;
|
||||
private final ASN1ObjectIdentifier contentType;
|
||||
|
||||
CMSProcessableInputStream(InputStream is) {
|
||||
<span class="nc" id="L40"> this(new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()), is);</span>
|
||||
<span class="nc" id="L41"> }</span>
|
||||
|
||||
<span class="nc" id="L43"> CMSProcessableInputStream(ASN1ObjectIdentifier type, InputStream is) {</span>
|
||||
<span class="nc" id="L44"> contentType = type;</span>
|
||||
<span class="nc" id="L45"> in = is;</span>
|
||||
<span class="nc" id="L46"> }</span>
|
||||
|
||||
@Override
|
||||
public Object getContent() {
|
||||
<span class="nc" id="L50"> return in;</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream out) throws IOException, CMSException {
|
||||
// read the content only one time
|
||||
<span class="nc" id="L56"> in.transferTo(out);</span>
|
||||
<span class="nc" id="L57"> in.close();</span>
|
||||
<span class="nc" id="L58"> }</span>
|
||||
|
||||
@Override
|
||||
public ASN1ObjectIdentifier getContentType() {
|
||||
<span class="nc" id="L62"> return contentType;</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CreateSignatureBase.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">org.apache.pdfbox.examples.signature</a> > <span class="el_source">CreateSignatureBase.java</span></div><h1>CreateSignatureBase.java</h1><pre class="source lang-java linenums">/*
|
||||
* Copyright 2015 The Apache Software Foundation.
|
||||
*
|
||||
* 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,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.pdfbox.examples.signature;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
|
||||
import org.bouncycastle.cert.jcajce.JcaCertStore;
|
||||
import org.bouncycastle.cms.CMSException;
|
||||
import org.bouncycastle.cms.CMSSignedData;
|
||||
import org.bouncycastle.cms.CMSSignedDataGenerator;
|
||||
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.OperatorCreationException;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
|
||||
|
||||
public abstract class CreateSignatureBase implements SignatureInterface {
|
||||
private PrivateKey privateKey;
|
||||
private Certificate[] certificateChain;
|
||||
private String tsaUrl;
|
||||
private boolean externalSigning;
|
||||
|
||||
/**
|
||||
* Initialize the signature creator with a keystore (pkcs12) and pin that should be used for the
|
||||
* signature.
|
||||
*
|
||||
* @param keystore is a pkcs12 keystore.
|
||||
* @param pin is the pin for the keystore / private key
|
||||
* @throws KeyStoreException if the keystore has not been initialized (loaded)
|
||||
* @throws NoSuchAlgorithmException if the algorithm for recovering the key cannot be found
|
||||
* @throws UnrecoverableKeyException if the given password is wrong
|
||||
* @throws CertificateException if the certificate is not valid as signing time
|
||||
* @throws IOException if no certificate could be found
|
||||
*/
|
||||
public CreateSignatureBase(KeyStore keystore, char[] pin)
|
||||
throws KeyStoreException,
|
||||
UnrecoverableKeyException,
|
||||
NoSuchAlgorithmException,
|
||||
IOException,
|
||||
<span class="nc" id="L68"> CertificateException {</span>
|
||||
// grabs the first alias from the keystore and get the private key. An
|
||||
// alternative method or constructor could be used for setting a specific
|
||||
// alias that should be used.
|
||||
<span class="nc" id="L72"> Enumeration<String> aliases = keystore.aliases();</span>
|
||||
String alias;
|
||||
<span class="nc" id="L74"> Certificate cert = null;</span>
|
||||
<span class="nc bnc" id="L75" title="All 4 branches missed."> while (cert == null && aliases.hasMoreElements()) {</span>
|
||||
<span class="nc" id="L76"> alias = aliases.nextElement();</span>
|
||||
<span class="nc" id="L77"> setPrivateKey((PrivateKey) keystore.getKey(alias, pin));</span>
|
||||
<span class="nc" id="L78"> Certificate[] certChain = keystore.getCertificateChain(alias);</span>
|
||||
<span class="nc bnc" id="L79" title="All 2 branches missed."> if (certChain != null) {</span>
|
||||
<span class="nc" id="L80"> setCertificateChain(certChain);</span>
|
||||
<span class="nc" id="L81"> cert = certChain[0];</span>
|
||||
<span class="nc bnc" id="L82" title="All 2 branches missed."> if (cert instanceof X509Certificate) {</span>
|
||||
// avoid expired certificate
|
||||
<span class="nc" id="L84"> ((X509Certificate) cert).checkValidity();</span>
|
||||
|
||||
//// SigUtils.checkCertificateUsage((X509Certificate) cert);
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L89"> }</span>
|
||||
|
||||
<span class="nc bnc" id="L91" title="All 2 branches missed."> if (cert == null) {</span>
|
||||
<span class="nc" id="L92"> throw new IOException("Could not find certificate");</span>
|
||||
}
|
||||
<span class="nc" id="L94"> }</span>
|
||||
|
||||
public final void setPrivateKey(PrivateKey privateKey) {
|
||||
<span class="nc" id="L97"> this.privateKey = privateKey;</span>
|
||||
<span class="nc" id="L98"> }</span>
|
||||
|
||||
public Certificate[] getCertificateChain() {
|
||||
<span class="nc" id="L101"> return certificateChain;</span>
|
||||
}
|
||||
|
||||
public final void setCertificateChain(final Certificate[] certificateChain) {
|
||||
<span class="nc" id="L105"> this.certificateChain = certificateChain;</span>
|
||||
<span class="nc" id="L106"> }</span>
|
||||
|
||||
public void setTsaUrl(String tsaUrl) {
|
||||
<span class="nc" id="L109"> this.tsaUrl = tsaUrl;</span>
|
||||
<span class="nc" id="L110"> }</span>
|
||||
|
||||
/**
|
||||
* SignatureInterface sample implementation.
|
||||
*
|
||||
* <p>This method will be called from inside of the pdfbox and create the PKCS #7 signature. The
|
||||
* given InputStream contains the bytes that are given by the byte range.
|
||||
*
|
||||
* <p>This method is for internal use only.
|
||||
*
|
||||
* <p>Use your favorite cryptographic library to implement PKCS #7 signature creation. If you
|
||||
* want to create the hash and the signature separately (e.g. to transfer only the hash to an
|
||||
* external application), read <a href="https://stackoverflow.com/questions/41767351">this
|
||||
* answer</a> or <a href="https://stackoverflow.com/questions/56867465">this answer</a>.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public byte[] sign(InputStream content) throws IOException {
|
||||
// cannot be done private (interface)
|
||||
try {
|
||||
<span class="nc" id="L131"> CMSSignedDataGenerator gen = new CMSSignedDataGenerator();</span>
|
||||
<span class="nc" id="L132"> X509Certificate cert = (X509Certificate) certificateChain[0];</span>
|
||||
<span class="nc" id="L133"> ContentSigner sha1Signer =</span>
|
||||
<span class="nc" id="L134"> new JcaContentSignerBuilder("SHA256WithRSA").build(privateKey);</span>
|
||||
<span class="nc" id="L135"> gen.addSignerInfoGenerator(</span>
|
||||
new JcaSignerInfoGeneratorBuilder(
|
||||
<span class="nc" id="L137"> new JcaDigestCalculatorProviderBuilder().build())</span>
|
||||
<span class="nc" id="L138"> .build(sha1Signer, cert));</span>
|
||||
<span class="nc" id="L139"> gen.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));</span>
|
||||
<span class="nc" id="L140"> CMSProcessableInputStream msg = new CMSProcessableInputStream(content);</span>
|
||||
<span class="nc" id="L141"> CMSSignedData signedData = gen.generate(msg, false);</span>
|
||||
<span class="nc bnc" id="L142" title="All 4 branches missed."> if (tsaUrl != null && !tsaUrl.isEmpty()) {</span>
|
||||
<span class="nc" id="L143"> ValidationTimeStamp validation = new ValidationTimeStamp(tsaUrl);</span>
|
||||
<span class="nc" id="L144"> signedData = validation.addSignedTimeStamp(signedData);</span>
|
||||
}
|
||||
<span class="nc" id="L146"> return signedData.getEncoded();</span>
|
||||
<span class="nc" id="L147"> } catch (GeneralSecurityException</span>
|
||||
| CMSException
|
||||
| OperatorCreationException
|
||||
| URISyntaxException e) {
|
||||
<span class="nc" id="L151"> throw new IOException(e);</span>
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isExternalSigning() {
|
||||
<span class="nc" id="L156"> return externalSigning;</span>
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if external signing scenario should be used. If {@code false}, SignatureInterface would
|
||||
* be used for signing.
|
||||
*
|
||||
* <p>Default: {@code false}
|
||||
*
|
||||
* @param externalSigning {@code true} if external signing should be performed
|
||||
*/
|
||||
public void setExternalSigning(boolean externalSigning) {
|
||||
<span class="nc" id="L168"> this.externalSigning = externalSigning;</span>
|
||||
<span class="nc" id="L169"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>TSAClient</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">org.apache.pdfbox.examples.signature</a> > <span class="el_class">TSAClient</span></div><h1>TSAClient</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">208 of 208</td><td class="ctr2">0%</td><td class="bar">14 of 14</td><td class="ctr2">0%</td><td class="ctr1">11</td><td class="ctr2">11</td><td class="ctr1">59</td><td class="ctr2">59</td><td class="ctr1">4</td><td class="ctr2">4</td></tr></tfoot><tbody><tr><td id="a1"><a href="TSAClient.java.html#L127" class="el_method">getTSAResponse(byte[])</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="98" alt="98"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="10" alt="10"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">6</td><td class="ctr2" id="g0">6</td><td class="ctr1" id="h0">29</td><td class="ctr2" id="i0">29</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a0"><a href="TSAClient.java.html#L81" class="el_method">getTimeStampToken(InputStream)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="101" height="10" title="83" alt="83"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"><img src="../jacoco-resources/redbar.gif" width="48" height="10" title="4" alt="4"/></td><td class="ctr2" id="e1">0%</td><td class="ctr1" id="f1">3</td><td class="ctr2" id="g1">3</td><td class="ctr1" id="h1">21</td><td class="ctr2" id="i1">21</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a3"><a href="TSAClient.java.html#L67" class="el_method">TSAClient(URL, String, String, MessageDigest)</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="18" height="10" title="15" alt="15"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">6</td><td class="ctr2" id="i2">6</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr><tr><td id="a2"><a href="TSAClient.java.html#L50" class="el_method">static {...}</a></td><td class="bar" id="b3"><img src="../jacoco-resources/redbar.gif" width="14" height="10" title="12" alt="12"/></td><td class="ctr2" id="c3">0%</td><td class="bar" id="d3"/><td class="ctr2" id="e3">n/a</td><td class="ctr1" id="f3">1</td><td class="ctr2" id="g3">1</td><td class="ctr1" id="h3">3</td><td class="ctr2" id="i3">3</td><td class="ctr1" id="j3">1</td><td class="ctr2" id="k3">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>TSAClient.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">org.apache.pdfbox.examples.signature</a> > <span class="el_source">TSAClient.java</span></div><h1>TSAClient.java</h1><pre class="source lang-java linenums">/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.pdfbox.examples.signature;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
|
||||
import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
|
||||
import org.bouncycastle.tsp.TSPException;
|
||||
import org.bouncycastle.tsp.TimeStampRequest;
|
||||
import org.bouncycastle.tsp.TimeStampRequestGenerator;
|
||||
import org.bouncycastle.tsp.TimeStampResponse;
|
||||
import org.bouncycastle.tsp.TimeStampToken;
|
||||
|
||||
/**
|
||||
* Time Stamping Authority (TSA) Client [RFC 3161].
|
||||
*
|
||||
* @author Vakhtang Koroghlishvili
|
||||
* @author John Hewson
|
||||
*/
|
||||
public class TSAClient {
|
||||
<span class="nc" id="L50"> private static final Logger LOG = LogManager.getLogger(TSAClient.class);</span>
|
||||
|
||||
<span class="nc" id="L52"> private static final DigestAlgorithmIdentifierFinder ALGORITHM_OID_FINDER =</span>
|
||||
new DefaultDigestAlgorithmIdentifierFinder();
|
||||
// SecureRandom.getInstanceStrong() would be better, but sometimes blocks on Linux
|
||||
<span class="nc" id="L55"> private static final Random RANDOM = new SecureRandom();</span>
|
||||
private final URL url;
|
||||
private final String username;
|
||||
private final String password;
|
||||
private final MessageDigest digest;
|
||||
|
||||
/**
|
||||
* @param url the URL of the TSA service
|
||||
* @param username user name of TSA
|
||||
* @param password password of TSA
|
||||
* @param digest the message digest to use
|
||||
*/
|
||||
<span class="nc" id="L67"> public TSAClient(URL url, String username, String password, MessageDigest digest) {</span>
|
||||
<span class="nc" id="L68"> this.url = url;</span>
|
||||
<span class="nc" id="L69"> this.username = username;</span>
|
||||
<span class="nc" id="L70"> this.password = password;</span>
|
||||
<span class="nc" id="L71"> this.digest = digest;</span>
|
||||
<span class="nc" id="L72"> }</span>
|
||||
|
||||
/**
|
||||
* @param content
|
||||
* @return the time stamp token
|
||||
* @throws IOException if there was an error with the connection or data from the TSA server, or
|
||||
* if the time stamp response could not be validated
|
||||
*/
|
||||
public TimeStampToken getTimeStampToken(InputStream content) throws IOException {
|
||||
<span class="nc" id="L81"> digest.reset();</span>
|
||||
<span class="nc" id="L82"> DigestInputStream dis = new DigestInputStream(content, digest);</span>
|
||||
<span class="nc bnc" id="L83" title="All 2 branches missed."> while (dis.read() != -1) {</span>
|
||||
// do nothing
|
||||
}
|
||||
<span class="nc" id="L86"> byte[] hash = digest.digest();</span>
|
||||
|
||||
// 32-bit cryptographic nonce
|
||||
<span class="nc" id="L89"> int nonce = RANDOM.nextInt();</span>
|
||||
|
||||
// generate TSA request
|
||||
<span class="nc" id="L92"> TimeStampRequestGenerator tsaGenerator = new TimeStampRequestGenerator();</span>
|
||||
<span class="nc" id="L93"> tsaGenerator.setCertReq(true);</span>
|
||||
<span class="nc" id="L94"> ASN1ObjectIdentifier oid = ALGORITHM_OID_FINDER.find(digest.getAlgorithm()).getAlgorithm();</span>
|
||||
<span class="nc" id="L95"> TimeStampRequest request = tsaGenerator.generate(oid, hash, BigInteger.valueOf(nonce));</span>
|
||||
|
||||
// get TSA response
|
||||
<span class="nc" id="L98"> byte[] tsaResponse = getTSAResponse(request.getEncoded());</span>
|
||||
|
||||
TimeStampResponse response;
|
||||
try {
|
||||
<span class="nc" id="L102"> response = new TimeStampResponse(tsaResponse);</span>
|
||||
<span class="nc" id="L103"> response.validate(request);</span>
|
||||
<span class="nc" id="L104"> } catch (TSPException e) {</span>
|
||||
<span class="nc" id="L105"> throw new IOException(e);</span>
|
||||
<span class="nc" id="L106"> }</span>
|
||||
|
||||
<span class="nc" id="L108"> TimeStampToken timeStampToken = response.getTimeStampToken();</span>
|
||||
<span class="nc bnc" id="L109" title="All 2 branches missed."> if (timeStampToken == null) {</span>
|
||||
// https://www.ietf.org/rfc/rfc3161.html#section-2.4.2
|
||||
<span class="nc" id="L111"> throw new IOException(</span>
|
||||
"Response from "
|
||||
+ url
|
||||
+ " does not have a time stamp token, status: "
|
||||
<span class="nc" id="L115"> + response.getStatus()</span>
|
||||
+ " ("
|
||||
<span class="nc" id="L117"> + response.getStatusString()</span>
|
||||
+ ")");
|
||||
}
|
||||
|
||||
<span class="nc" id="L121"> return timeStampToken;</span>
|
||||
}
|
||||
|
||||
// gets response data for the given encoded TimeStampRequest data
|
||||
// throws IOException if a connection to the TSA cannot be established
|
||||
private byte[] getTSAResponse(byte[] request) throws IOException {
|
||||
<span class="nc" id="L127"> LOG.debug("Opening connection to TSA server");</span>
|
||||
|
||||
// todo: support proxy servers
|
||||
<span class="nc" id="L130"> URLConnection connection = url.openConnection();</span>
|
||||
<span class="nc" id="L131"> connection.setDoOutput(true);</span>
|
||||
<span class="nc" id="L132"> connection.setDoInput(true);</span>
|
||||
<span class="nc" id="L133"> connection.setRequestProperty("Content-Type", "application/timestamp-query");</span>
|
||||
|
||||
<span class="nc" id="L135"> LOG.debug("Established connection to TSA server");</span>
|
||||
|
||||
<span class="nc bnc" id="L137" title="All 8 branches missed."> if (username != null && password != null && !username.isEmpty() && !password.isEmpty()) {</span>
|
||||
<span class="nc" id="L138"> String contentEncoding = connection.getContentEncoding();</span>
|
||||
<span class="nc bnc" id="L139" title="All 2 branches missed."> if (contentEncoding == null) {</span>
|
||||
<span class="nc" id="L140"> contentEncoding = StandardCharsets.UTF_8.name();</span>
|
||||
}
|
||||
<span class="nc" id="L142"> connection.setRequestProperty(</span>
|
||||
"Authorization",
|
||||
"Basic "
|
||||
+ new String(
|
||||
<span class="nc" id="L146"> Base64.getEncoder()</span>
|
||||
<span class="nc" id="L147"> .encode(</span>
|
||||
(username + ":" + password)
|
||||
<span class="nc" id="L149"> .getBytes(contentEncoding))));</span>
|
||||
}
|
||||
|
||||
// read response
|
||||
<span class="nc" id="L153"> try (OutputStream output = connection.getOutputStream()) {</span>
|
||||
<span class="nc" id="L154"> output.write(request);</span>
|
||||
<span class="nc" id="L155"> } catch (IOException ex) {</span>
|
||||
<span class="nc" id="L156"> LOG.error("Exception when writing to {}", this.url, ex);</span>
|
||||
<span class="nc" id="L157"> throw ex;</span>
|
||||
<span class="nc" id="L158"> }</span>
|
||||
|
||||
<span class="nc" id="L160"> LOG.debug("Waiting for response from TSA server");</span>
|
||||
|
||||
byte[] response;
|
||||
<span class="nc" id="L163"> try (InputStream input = connection.getInputStream()) {</span>
|
||||
<span class="nc" id="L164"> response = input.readAllBytes();</span>
|
||||
<span class="nc" id="L165"> } catch (IOException ex) {</span>
|
||||
<span class="nc" id="L166"> LOG.error("Exception when reading from {}", this.url, ex);</span>
|
||||
<span class="nc" id="L167"> throw ex;</span>
|
||||
<span class="nc" id="L168"> }</span>
|
||||
|
||||
<span class="nc" id="L170"> LOG.debug("Received response from TSA server");</span>
|
||||
|
||||
<span class="nc" id="L172"> return response;</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ValidationTimeStamp</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">org.apache.pdfbox.examples.signature</a> > <span class="el_class">ValidationTimeStamp</span></div><h1>ValidationTimeStamp</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">112 of 112</td><td class="ctr2">0%</td><td class="bar">6 of 6</td><td class="ctr2">0%</td><td class="ctr1">7</td><td class="ctr2">7</td><td class="ctr1">26</td><td class="ctr2">26</td><td class="ctr1">4</td><td class="ctr2">4</td></tr></tfoot><tbody><tr><td id="a2"><a href="ValidationTimeStamp.java.html#L111" class="el_method">signTimeStamp(SignerInformation)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="51" alt="51"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">2</td><td class="ctr2" id="g0">2</td><td class="ctr1" id="h0">13</td><td class="ctr2" id="i0">13</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a0"><a href="ValidationTimeStamp.java.html#L89" class="el_method">addSignedTimeStamp(CMSSignedData)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="75" height="10" title="32" alt="32"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e1">0%</td><td class="ctr1" id="f1">2</td><td class="ctr2" id="g1">2</td><td class="ctr1" id="h1">6</td><td class="ctr2" id="i1">6</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a3"><a href="ValidationTimeStamp.java.html#L62" class="el_method">ValidationTimeStamp(String)</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="49" height="10" title="21" alt="21"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e2">0%</td><td class="ctr1" id="f2">2</td><td class="ctr2" id="g2">2</td><td class="ctr1" id="h2">5</td><td class="ctr2" id="i2">5</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr><tr><td id="a1"><a href="ValidationTimeStamp.java.html#L77" class="el_method">getTimeStampToken(InputStream)</a></td><td class="bar" id="b3"><img src="../jacoco-resources/redbar.gif" width="18" height="10" title="8" alt="8"/></td><td class="ctr2" id="c3">0%</td><td class="bar" id="d3"/><td class="ctr2" id="e3">n/a</td><td class="ctr1" id="f3">1</td><td class="ctr2" id="g3">1</td><td class="ctr1" id="h3">2</td><td class="ctr2" id="i3">2</td><td class="ctr1" id="j3">1</td><td class="ctr2" id="k3">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ValidationTimeStamp.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">org.apache.pdfbox.examples.signature</a> > <span class="el_source">ValidationTimeStamp.java</span></div><h1>ValidationTimeStamp.java</h1><pre class="source lang-java linenums">/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.pdfbox.examples.signature;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1Encodable;
|
||||
import org.bouncycastle.asn1.ASN1EncodableVector;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.ASN1Primitive;
|
||||
import org.bouncycastle.asn1.DERSet;
|
||||
import org.bouncycastle.asn1.cms.Attribute;
|
||||
import org.bouncycastle.asn1.cms.AttributeTable;
|
||||
import org.bouncycastle.asn1.cms.Attributes;
|
||||
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
||||
import org.bouncycastle.cms.CMSSignedData;
|
||||
import org.bouncycastle.cms.SignerInformation;
|
||||
import org.bouncycastle.cms.SignerInformationStore;
|
||||
import org.bouncycastle.tsp.TimeStampToken;
|
||||
|
||||
/**
|
||||
* This class wraps the TSAClient and the work that has to be done with it. Like Adding Signed
|
||||
* TimeStamps to a signature, or creating a CMS timestamp attribute (with a signed timestamp)
|
||||
*
|
||||
* @author Others
|
||||
* @author Alexis Suter
|
||||
*/
|
||||
public class ValidationTimeStamp {
|
||||
private TSAClient tsaClient;
|
||||
|
||||
/**
|
||||
* @param tsaUrl The url where TS-Request will be done.
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws MalformedURLException
|
||||
* @throws java.net.URISyntaxException
|
||||
*/
|
||||
public ValidationTimeStamp(String tsaUrl)
|
||||
<span class="nc" id="L62"> throws NoSuchAlgorithmException, MalformedURLException, URISyntaxException {</span>
|
||||
<span class="nc bnc" id="L63" title="All 2 branches missed."> if (tsaUrl != null) {</span>
|
||||
<span class="nc" id="L64"> MessageDigest digest = MessageDigest.getInstance("SHA-256");</span>
|
||||
<span class="nc" id="L65"> this.tsaClient = new TSAClient(new URI(tsaUrl).toURL(), null, null, digest);</span>
|
||||
}
|
||||
<span class="nc" id="L67"> }</span>
|
||||
|
||||
/**
|
||||
* Creates a signed timestamp token by the given input stream.
|
||||
*
|
||||
* @param content InputStream of the content to sign
|
||||
* @return the byte[] of the timestamp token
|
||||
* @throws IOException
|
||||
*/
|
||||
public byte[] getTimeStampToken(InputStream content) throws IOException {
|
||||
<span class="nc" id="L77"> TimeStampToken timeStampToken = tsaClient.getTimeStampToken(content);</span>
|
||||
<span class="nc" id="L78"> return timeStampToken.getEncoded();</span>
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend cms signed data with TimeStamp first or to all signers
|
||||
*
|
||||
* @param signedData Generated CMS signed data
|
||||
* @return CMSSignedData Extended CMS signed data
|
||||
* @throws IOException
|
||||
*/
|
||||
public CMSSignedData addSignedTimeStamp(CMSSignedData signedData) throws IOException {
|
||||
<span class="nc" id="L89"> SignerInformationStore signerStore = signedData.getSignerInfos();</span>
|
||||
<span class="nc" id="L90"> List<SignerInformation> newSigners = new ArrayList<>();</span>
|
||||
|
||||
<span class="nc bnc" id="L92" title="All 2 branches missed."> for (SignerInformation signer : signerStore.getSigners()) {</span>
|
||||
// This adds a timestamp to every signer (into his unsigned attributes) in the
|
||||
// signature.
|
||||
<span class="nc" id="L95"> newSigners.add(signTimeStamp(signer));</span>
|
||||
<span class="nc" id="L96"> }</span>
|
||||
|
||||
// Because new SignerInformation is created, new SignerInfoStore has to be created
|
||||
// and also be replaced in signedData. Which creates a new signedData object.
|
||||
<span class="nc" id="L100"> return CMSSignedData.replaceSigners(signedData, new SignerInformationStore(newSigners));</span>
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend CMS Signer Information with the TimeStampToken into the unsigned Attributes.
|
||||
*
|
||||
* @param signer information about signer
|
||||
* @return information about SignerInformation
|
||||
* @throws IOException
|
||||
*/
|
||||
private SignerInformation signTimeStamp(SignerInformation signer) throws IOException {
|
||||
<span class="nc" id="L111"> AttributeTable unsignedAttributes = signer.getUnsignedAttributes();</span>
|
||||
|
||||
<span class="nc" id="L113"> ASN1EncodableVector vector = new ASN1EncodableVector();</span>
|
||||
<span class="nc bnc" id="L114" title="All 2 branches missed."> if (unsignedAttributes != null) {</span>
|
||||
<span class="nc" id="L115"> vector = unsignedAttributes.toASN1EncodableVector();</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L118"> TimeStampToken timeStampToken =</span>
|
||||
<span class="nc" id="L119"> tsaClient.getTimeStampToken(new ByteArrayInputStream(signer.getSignature()));</span>
|
||||
<span class="nc" id="L120"> byte[] token = timeStampToken.getEncoded();</span>
|
||||
<span class="nc" id="L121"> ASN1ObjectIdentifier oid = PKCSObjectIdentifiers.id_aa_signatureTimeStampToken;</span>
|
||||
<span class="nc" id="L122"> ASN1Encodable signatureTimeStamp =</span>
|
||||
<span class="nc" id="L123"> new Attribute(oid, new DERSet(ASN1Primitive.fromByteArray(token)));</span>
|
||||
|
||||
<span class="nc" id="L125"> vector.add(signatureTimeStamp);</span>
|
||||
<span class="nc" id="L126"> Attributes signedAttributes = new Attributes(vector);</span>
|
||||
|
||||
// There is no other way changing the unsigned attributes of the signer information.
|
||||
// result is never null, new SignerInformation always returned,
|
||||
// see source code of replaceUnsignedAttributes
|
||||
<span class="nc" id="L131"> return SignerInformation.replaceUnsignedAttributes(</span>
|
||||
signer, new AttributeTable(signedAttributes));
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ConnectedInputStream.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">org.apache.pdfbox.examples.util</a> > <span class="el_source">ConnectedInputStream.java</span></div><h1>ConnectedInputStream.java</h1><pre class="source lang-java linenums">/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.pdfbox.examples.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
/**
|
||||
* Delegate class to close the connection when the class gets closed.
|
||||
*
|
||||
* @author Tilman Hausherr
|
||||
*/
|
||||
public class ConnectedInputStream extends InputStream {
|
||||
HttpURLConnection con;
|
||||
InputStream is;
|
||||
|
||||
<span class="nc" id="L32"> public ConnectedInputStream(HttpURLConnection con, InputStream is) {</span>
|
||||
<span class="nc" id="L33"> this.con = con;</span>
|
||||
<span class="nc" id="L34"> this.is = is;</span>
|
||||
<span class="nc" id="L35"> }</span>
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
<span class="nc" id="L39"> return is.read();</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
<span class="nc" id="L44"> return is.read(b);</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
<span class="nc" id="L49"> return is.read(b, off, len);</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
<span class="nc" id="L54"> return is.skip(n);</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
<span class="nc" id="L59"> return is.available();</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
<span class="nc" id="L64"> is.mark(readlimit);</span>
|
||||
<span class="nc" id="L65"> }</span>
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
<span class="nc" id="L69"> is.reset();</span>
|
||||
<span class="nc" id="L70"> }</span>
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
<span class="nc" id="L74"> return is.markSupported();</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
<span class="nc" id="L79"> is.close();</span>
|
||||
<span class="nc" id="L80"> con.disconnect();</span>
|
||||
<span class="nc" id="L81"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DeletingRandomAccessFile</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">org.apache.pdfbox.examples.util</a> > <span class="el_class">DeletingRandomAccessFile</span></div><h1>DeletingRandomAccessFile</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">40 of 40</td><td class="ctr2">0%</td><td class="bar">2 of 2</td><td class="ctr2">0%</td><td class="ctr1">4</td><td class="ctr2">4</td><td class="ctr1">13</td><td class="ctr2">13</td><td class="ctr1">3</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a0"><a href="DeletingRandomAccessFile.java.html#L25" class="el_method">close()</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="28" alt="28"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">2</td><td class="ctr2" id="g0">2</td><td class="ctr1" id="h0">9</td><td class="ctr2" id="i0">9</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="DeletingRandomAccessFile.java.html#L18" class="el_method">DeletingRandomAccessFile(File)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="34" height="10" title="8" alt="8"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">3</td><td class="ctr2" id="i1">3</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a2"><a href="DeletingRandomAccessFile.java.html#L13" class="el_method">static {...}</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="17" height="10" title="4" alt="4"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">1</td><td class="ctr2" id="i2">1</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DeletingRandomAccessFile.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">org.apache.pdfbox.examples.util</a> > <span class="el_source">DeletingRandomAccessFile.java</span></div><h1>DeletingRandomAccessFile.java</h1><pre class="source lang-java linenums">package org.apache.pdfbox.examples.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/** A custom RandomAccessRead implementation that deletes the file when closed */
|
||||
<span class="nc" id="L13">@Slf4j</span>
|
||||
public class DeletingRandomAccessFile extends RandomAccessReadBufferedFile {
|
||||
private final Path tempFilePath;
|
||||
|
||||
public DeletingRandomAccessFile(File file) throws IOException {
|
||||
<span class="nc" id="L18"> super(file);</span>
|
||||
<span class="nc" id="L19"> this.tempFilePath = file.toPath();</span>
|
||||
<span class="nc" id="L20"> }</span>
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
<span class="nc" id="L25"> super.close();</span>
|
||||
} finally {
|
||||
try {
|
||||
<span class="nc" id="L28"> boolean deleted = Files.deleteIfExists(tempFilePath);</span>
|
||||
<span class="nc bnc" id="L29" title="All 2 branches missed."> if (deleted) {</span>
|
||||
<span class="nc" id="L30"> log.info("Successfully deleted temp file: {}", tempFilePath);</span>
|
||||
} else {
|
||||
<span class="nc" id="L32"> log.warn("Failed to delete temp file (may not exist): {}", tempFilePath);</span>
|
||||
}
|
||||
<span class="nc" id="L34"> } catch (IOException e) {</span>
|
||||
<span class="nc" id="L35"> log.error("Error deleting temp file: {}", tempFilePath, e);</span>
|
||||
<span class="nc" id="L36"> }</span>
|
||||
}
|
||||
<span class="nc" id="L38"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>org.apache.pdfbox.examples.util</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb', 'coveragetable'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="index.source.html" class="el_source">Source Files</a><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <span class="el_package">org.apache.pdfbox.examples.util</span></div><h1>org.apache.pdfbox.examples.util</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td><td class="sortable ctr1" id="l" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="m" onclick="toggleSort(this)">Classes</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">94 of 94</td><td class="ctr2">0%</td><td class="bar">2 of 2</td><td class="ctr2">0%</td><td class="ctr1">14</td><td class="ctr2">14</td><td class="ctr1">30</td><td class="ctr2">30</td><td class="ctr1">13</td><td class="ctr2">13</td><td class="ctr1">2</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a0"><a href="ConnectedInputStream.html" class="el_class">ConnectedInputStream</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="54" alt="54"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f0">10</td><td class="ctr2" id="g0">10</td><td class="ctr1" id="h0">17</td><td class="ctr2" id="i0">17</td><td class="ctr1" id="j0">10</td><td class="ctr2" id="k0">10</td><td class="ctr1" id="l0">1</td><td class="ctr2" id="m0">1</td></tr><tr><td id="a1"><a href="DeletingRandomAccessFile.html" class="el_class">DeletingRandomAccessFile</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="88" height="10" title="40" alt="40"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f1">4</td><td class="ctr2" id="g1">4</td><td class="ctr1" id="h1">13</td><td class="ctr2" id="i1">13</td><td class="ctr1" id="j1">3</td><td class="ctr2" id="k1">3</td><td class="ctr1" id="l1">1</td><td class="ctr2" id="m1">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>org.apache.pdfbox.examples.util</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb', 'coveragetable'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="index.html" class="el_class">Classes</a><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <span class="el_package">org.apache.pdfbox.examples.util</span></div><h1>org.apache.pdfbox.examples.util</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td><td class="sortable ctr1" id="l" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="m" onclick="toggleSort(this)">Classes</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">94 of 94</td><td class="ctr2">0%</td><td class="bar">2 of 2</td><td class="ctr2">0%</td><td class="ctr1">14</td><td class="ctr2">14</td><td class="ctr1">30</td><td class="ctr2">30</td><td class="ctr1">13</td><td class="ctr2">13</td><td class="ctr1">2</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a0"><a href="ConnectedInputStream.java.html" class="el_source">ConnectedInputStream.java</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="54" alt="54"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f0">10</td><td class="ctr2" id="g0">10</td><td class="ctr1" id="h0">17</td><td class="ctr2" id="i0">17</td><td class="ctr1" id="j0">10</td><td class="ctr2" id="k0">10</td><td class="ctr1" id="l0">1</td><td class="ctr2" id="m0">1</td></tr><tr><td id="a1"><a href="DeletingRandomAccessFile.java.html" class="el_source">DeletingRandomAccessFile.java</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="88" height="10" title="40" alt="40"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f1">4</td><td class="ctr2" id="g1">4</td><td class="ctr1" id="h1">13</td><td class="ctr2" id="i1">13</td><td class="ctr1" id="j1">3</td><td class="ctr2" id="k1">3</td><td class="ctr1" id="l1">1</td><td class="ctr2" id="m1">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
126
testResult/html/stirling.software.SPDF.EE/EEAppConfig.java.html
Normal file
@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>EEAppConfig.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.EE</a> > <span class="el_source">EEAppConfig.java</span></div><h1>EEAppConfig.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.EE;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.EnterpriseEdition;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Premium;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Premium.ProFeatures.GoogleDrive;
|
||||
|
||||
@Configuration
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
<span class="nc" id="L18">@Slf4j</span>
|
||||
public class EEAppConfig {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
private final LicenseKeyChecker licenseKeyChecker;
|
||||
|
||||
public EEAppConfig(
|
||||
<span class="nc" id="L26"> ApplicationProperties applicationProperties, LicenseKeyChecker licenseKeyChecker) {</span>
|
||||
<span class="nc" id="L27"> this.applicationProperties = applicationProperties;</span>
|
||||
<span class="nc" id="L28"> this.licenseKeyChecker = licenseKeyChecker;</span>
|
||||
<span class="nc" id="L29"> migrateEnterpriseSettingsToPremium(this.applicationProperties);</span>
|
||||
<span class="nc" id="L30"> }</span>
|
||||
|
||||
@Bean(name = "runningProOrHigher")
|
||||
public boolean runningProOrHigher() {
|
||||
<span class="nc bnc" id="L34" title="All 2 branches missed."> return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL;</span>
|
||||
}
|
||||
|
||||
@Bean(name = "runningEE")
|
||||
public boolean runningEnterprise() {
|
||||
<span class="nc bnc" id="L39" title="All 2 branches missed."> return licenseKeyChecker.getPremiumLicenseEnabledResult() == License.ENTERPRISE;</span>
|
||||
}
|
||||
|
||||
@Bean(name = "SSOAutoLogin")
|
||||
public boolean ssoAutoLogin() {
|
||||
<span class="nc" id="L44"> return applicationProperties.getPremium().getProFeatures().isSsoAutoLogin();</span>
|
||||
}
|
||||
|
||||
@Bean(name = "GoogleDriveEnabled")
|
||||
public boolean googleDriveEnabled() {
|
||||
<span class="nc bnc" id="L49" title="All 2 branches missed."> return runningProOrHigher()</span>
|
||||
<span class="nc bnc" id="L50" title="All 2 branches missed."> && applicationProperties.getPremium().getProFeatures().getGoogleDrive().isEnabled();</span>
|
||||
}
|
||||
|
||||
@Bean(name = "GoogleDriveConfig")
|
||||
public GoogleDrive googleDriveConfig() {
|
||||
<span class="nc" id="L55"> return applicationProperties.getPremium().getProFeatures().getGoogleDrive();</span>
|
||||
}
|
||||
|
||||
// TODO: Remove post migration
|
||||
public void migrateEnterpriseSettingsToPremium(ApplicationProperties applicationProperties) {
|
||||
<span class="nc" id="L60"> EnterpriseEdition enterpriseEdition = applicationProperties.getEnterpriseEdition();</span>
|
||||
<span class="nc" id="L61"> Premium premium = applicationProperties.getPremium();</span>
|
||||
|
||||
// Only proceed if both objects exist
|
||||
<span class="nc bnc" id="L64" title="All 4 branches missed."> if (enterpriseEdition == null || premium == null) {</span>
|
||||
<span class="nc" id="L65"> return;</span>
|
||||
}
|
||||
|
||||
// Copy the license key if it's set in enterprise but not in premium
|
||||
<span class="nc bnc" id="L69" title="All 2 branches missed."> if (premium.getKey() == null</span>
|
||||
<span class="nc bnc" id="L70" title="All 2 branches missed."> || premium.getKey().equals("00000000-0000-0000-0000-000000000000")) {</span>
|
||||
<span class="nc bnc" id="L71" title="All 2 branches missed."> if (enterpriseEdition.getKey() != null</span>
|
||||
<span class="nc bnc" id="L72" title="All 2 branches missed."> && !enterpriseEdition.getKey().equals("00000000-0000-0000-0000-000000000000")) {</span>
|
||||
<span class="nc" id="L73"> premium.setKey(enterpriseEdition.getKey());</span>
|
||||
}
|
||||
}
|
||||
|
||||
// Copy enabled state if enterprise is enabled but premium is not
|
||||
<span class="nc bnc" id="L78" title="All 4 branches missed."> if (!premium.isEnabled() && enterpriseEdition.isEnabled()) {</span>
|
||||
<span class="nc" id="L79"> premium.setEnabled(true);</span>
|
||||
}
|
||||
|
||||
// Copy SSO auto login setting
|
||||
<span class="nc bnc" id="L83" title="All 4 branches missed."> if (!premium.getProFeatures().isSsoAutoLogin() && enterpriseEdition.isSsoAutoLogin()) {</span>
|
||||
<span class="nc" id="L84"> premium.getProFeatures().setSsoAutoLogin(true);</span>
|
||||
}
|
||||
|
||||
// Copy CustomMetadata settings
|
||||
<span class="nc" id="L88"> Premium.ProFeatures.CustomMetadata premiumMetadata =</span>
|
||||
<span class="nc" id="L89"> premium.getProFeatures().getCustomMetadata();</span>
|
||||
<span class="nc" id="L90"> EnterpriseEdition.CustomMetadata enterpriseMetadata = enterpriseEdition.getCustomMetadata();</span>
|
||||
|
||||
<span class="nc bnc" id="L92" title="All 4 branches missed."> if (enterpriseMetadata != null && premiumMetadata != null) {</span>
|
||||
// Copy autoUpdateMetadata setting
|
||||
<span class="nc bnc" id="L94" title="All 2 branches missed."> if (!premiumMetadata.isAutoUpdateMetadata()</span>
|
||||
<span class="nc bnc" id="L95" title="All 2 branches missed."> && enterpriseMetadata.isAutoUpdateMetadata()) {</span>
|
||||
<span class="nc" id="L96"> premiumMetadata.setAutoUpdateMetadata(true);</span>
|
||||
}
|
||||
|
||||
// Copy author if not set in premium but set in enterprise
|
||||
<span class="nc bnc" id="L100" title="All 2 branches missed."> if ((premiumMetadata.getAuthor() == null</span>
|
||||
<span class="nc bnc" id="L101" title="All 2 branches missed."> || premiumMetadata.getAuthor().trim().isEmpty()</span>
|
||||
<span class="nc bnc" id="L102" title="All 2 branches missed."> || "username".equals(premiumMetadata.getAuthor()))</span>
|
||||
<span class="nc bnc" id="L103" title="All 2 branches missed."> && enterpriseMetadata.getAuthor() != null</span>
|
||||
<span class="nc bnc" id="L104" title="All 2 branches missed."> && !enterpriseMetadata.getAuthor().trim().isEmpty()) {</span>
|
||||
<span class="nc" id="L105"> premiumMetadata.setAuthor(enterpriseMetadata.getAuthor());</span>
|
||||
}
|
||||
|
||||
// Copy creator if not set in premium but set in enterprise and different from default
|
||||
<span class="nc bnc" id="L109" title="All 2 branches missed."> if ((premiumMetadata.getCreator() == null</span>
|
||||
<span class="nc bnc" id="L110" title="All 2 branches missed."> || "Stirling-PDF".equals(premiumMetadata.getCreator()))</span>
|
||||
<span class="nc bnc" id="L111" title="All 2 branches missed."> && enterpriseMetadata.getCreator() != null</span>
|
||||
<span class="nc bnc" id="L112" title="All 2 branches missed."> && !"Stirling-PDF".equals(enterpriseMetadata.getCreator())) {</span>
|
||||
<span class="nc" id="L113"> premiumMetadata.setCreator(enterpriseMetadata.getCreator());</span>
|
||||
}
|
||||
|
||||
// Copy producer if not set in premium but set in enterprise and different from default
|
||||
<span class="nc bnc" id="L117" title="All 2 branches missed."> if ((premiumMetadata.getProducer() == null</span>
|
||||
<span class="nc bnc" id="L118" title="All 2 branches missed."> || "Stirling-PDF".equals(premiumMetadata.getProducer()))</span>
|
||||
<span class="nc bnc" id="L119" title="All 2 branches missed."> && enterpriseMetadata.getProducer() != null</span>
|
||||
<span class="nc bnc" id="L120" title="All 2 branches missed."> && !"Stirling-PDF".equals(enterpriseMetadata.getProducer())) {</span>
|
||||
<span class="nc" id="L121"> premiumMetadata.setProducer(enterpriseMetadata.getProducer());</span>
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L124"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>KeygenLicenseVerifier.License</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.EE</a> > <span class="el_class">KeygenLicenseVerifier.License</span></div><h1>KeygenLicenseVerifier.License</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">21 of 21</td><td class="ctr2">0%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">1</td><td class="ctr2">1</td><td class="ctr1">4</td><td class="ctr2">4</td><td class="ctr1">1</td><td class="ctr2">1</td></tr></tfoot><tbody><tr><td id="a0"><a href="KeygenLicenseVerifier.java.html#L29" class="el_method">static {...}</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="21" alt="21"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">1</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h0">4</td><td class="ctr2" id="i0">4</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,582 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>KeygenLicenseVerifier.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.EE</a> > <span class="el_source">KeygenLicenseVerifier.java</span></div><h1>KeygenLicenseVerifier.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.EE;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
|
||||
import org.bouncycastle.crypto.signers.Ed25519Signer;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.posthog.java.shaded.org.json.JSONException;
|
||||
import com.posthog.java.shaded.org.json.JSONObject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
||||
@Service
|
||||
<span class="nc" id="L26">@Slf4j</span>
|
||||
public class KeygenLicenseVerifier {
|
||||
|
||||
<span class="nc" id="L29"> enum License {</span>
|
||||
<span class="nc" id="L30"> NORMAL,</span>
|
||||
<span class="nc" id="L31"> PRO,</span>
|
||||
<span class="nc" id="L32"> ENTERPRISE</span>
|
||||
}
|
||||
|
||||
// License verification configuration
|
||||
private static final String ACCOUNT_ID = "e5430f69-e834-4ae4-befd-b602aae5f372";
|
||||
private static final String BASE_URL = "https://api.keygen.sh/v1/accounts";
|
||||
|
||||
private static final String PUBLIC_KEY =
|
||||
"9fbc0d78593dcfcf03c945146edd60083bf5fae77dbc08aaa3935f03ce94a58d";
|
||||
|
||||
private static final String CERT_PREFIX = "-----BEGIN LICENSE FILE-----";
|
||||
private static final String CERT_SUFFIX = "-----END LICENSE FILE-----";
|
||||
|
||||
private static final String JWT_PREFIX = "key/";
|
||||
|
||||
<span class="nc" id="L47"> private static final ObjectMapper objectMapper = new ObjectMapper();</span>
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
@Autowired
|
||||
<span class="nc" id="L51"> public KeygenLicenseVerifier(ApplicationProperties applicationProperties) {</span>
|
||||
<span class="nc" id="L52"> this.applicationProperties = applicationProperties;</span>
|
||||
<span class="nc" id="L53"> }</span>
|
||||
|
||||
public License verifyLicense(String licenseKeyOrCert) {
|
||||
<span class="nc bnc" id="L56" title="All 2 branches missed."> if (isCertificateLicense(licenseKeyOrCert)) {</span>
|
||||
<span class="nc" id="L57"> log.info("Detected certificate-based license. Processing...");</span>
|
||||
<span class="nc" id="L58"> return resultToEnum(verifyCertificateLicense(licenseKeyOrCert), License.ENTERPRISE);</span>
|
||||
<span class="nc bnc" id="L59" title="All 2 branches missed."> } else if (isJWTLicense(licenseKeyOrCert)) {</span>
|
||||
<span class="nc" id="L60"> log.info("Detected JWT-style license key. Processing...");</span>
|
||||
<span class="nc" id="L61"> return resultToEnum(verifyJWTLicense(licenseKeyOrCert), License.ENTERPRISE);</span>
|
||||
} else {
|
||||
<span class="nc" id="L63"> log.info("Detected standard license key. Processing...");</span>
|
||||
<span class="nc" id="L64"> return resultToEnum(verifyStandardLicense(licenseKeyOrCert), License.PRO);</span>
|
||||
}
|
||||
}
|
||||
|
||||
private License resultToEnum(boolean result, License option) {
|
||||
<span class="nc bnc" id="L69" title="All 2 branches missed."> if (result) {</span>
|
||||
<span class="nc" id="L70"> return option;</span>
|
||||
}
|
||||
<span class="nc" id="L72"> return License.NORMAL;</span>
|
||||
}
|
||||
|
||||
private boolean isCertificateLicense(String license) {
|
||||
<span class="nc bnc" id="L76" title="All 4 branches missed."> return license != null && license.trim().startsWith(CERT_PREFIX);</span>
|
||||
}
|
||||
|
||||
private boolean isJWTLicense(String license) {
|
||||
<span class="nc bnc" id="L80" title="All 4 branches missed."> return license != null && license.trim().startsWith(JWT_PREFIX);</span>
|
||||
}
|
||||
|
||||
private boolean verifyCertificateLicense(String licenseFile) {
|
||||
try {
|
||||
<span class="nc" id="L85"> log.info("Verifying certificate-based license");</span>
|
||||
|
||||
<span class="nc" id="L87"> String encodedPayload = licenseFile;</span>
|
||||
// Remove the header
|
||||
<span class="nc" id="L89"> encodedPayload = encodedPayload.replace(CERT_PREFIX, "");</span>
|
||||
// Remove the footer
|
||||
<span class="nc" id="L91"> encodedPayload = encodedPayload.replace(CERT_SUFFIX, "");</span>
|
||||
// Remove all newlines
|
||||
<span class="nc" id="L93"> encodedPayload = encodedPayload.replaceAll("\\r?\\n", "");</span>
|
||||
|
||||
<span class="nc" id="L95"> byte[] payloadBytes = Base64.getDecoder().decode(encodedPayload);</span>
|
||||
<span class="nc" id="L96"> String payload = new String(payloadBytes);</span>
|
||||
|
||||
<span class="nc" id="L98"> log.info("Decoded certificate payload: {}", payload);</span>
|
||||
|
||||
<span class="nc" id="L100"> String encryptedData = "";</span>
|
||||
<span class="nc" id="L101"> String encodedSignature = "";</span>
|
||||
<span class="nc" id="L102"> String algorithm = "";</span>
|
||||
|
||||
try {
|
||||
<span class="nc" id="L105"> JSONObject attrs = new JSONObject(payload);</span>
|
||||
<span class="nc" id="L106"> encryptedData = (String) attrs.get("enc");</span>
|
||||
<span class="nc" id="L107"> encodedSignature = (String) attrs.get("sig");</span>
|
||||
<span class="nc" id="L108"> algorithm = (String) attrs.get("alg");</span>
|
||||
|
||||
<span class="nc" id="L110"> log.info("Certificate algorithm: {}", algorithm);</span>
|
||||
<span class="nc" id="L111"> } catch (JSONException e) {</span>
|
||||
<span class="nc" id="L112"> log.error("Failed to parse license file: {}", e.getMessage());</span>
|
||||
<span class="nc" id="L113"> return false;</span>
|
||||
<span class="nc" id="L114"> }</span>
|
||||
|
||||
// Verify license file algorithm
|
||||
<span class="nc bnc" id="L117" title="All 2 branches missed."> if (!algorithm.equals("base64+ed25519")) {</span>
|
||||
<span class="nc" id="L118"> log.error(</span>
|
||||
"Unsupported algorithm: {}. Only base64+ed25519 is supported.", algorithm);
|
||||
<span class="nc" id="L120"> return false;</span>
|
||||
}
|
||||
|
||||
// Verify signature
|
||||
<span class="nc" id="L124"> boolean isSignatureValid = verifyEd25519Signature(encryptedData, encodedSignature);</span>
|
||||
<span class="nc bnc" id="L125" title="All 2 branches missed."> if (!isSignatureValid) {</span>
|
||||
<span class="nc" id="L126"> log.error("License file signature is invalid");</span>
|
||||
<span class="nc" id="L127"> return false;</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L130"> log.info("License file signature is valid");</span>
|
||||
|
||||
// Decode the base64 data
|
||||
String decodedData;
|
||||
try {
|
||||
<span class="nc" id="L135"> decodedData = new String(Base64.getDecoder().decode(encryptedData));</span>
|
||||
<span class="nc" id="L136"> } catch (IllegalArgumentException e) {</span>
|
||||
<span class="nc" id="L137"> log.error("Failed to decode license data: {}", e.getMessage());</span>
|
||||
<span class="nc" id="L138"> return false;</span>
|
||||
<span class="nc" id="L139"> }</span>
|
||||
|
||||
// Process the certificate data
|
||||
<span class="nc" id="L142"> boolean isValid = processCertificateData(decodedData);</span>
|
||||
|
||||
<span class="nc" id="L144"> return isValid;</span>
|
||||
<span class="nc" id="L145"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L146"> log.error("Error verifying certificate license: {}", e.getMessage(), e);</span>
|
||||
<span class="nc" id="L147"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
private boolean verifyEd25519Signature(String encryptedData, String encodedSignature) {
|
||||
try {
|
||||
<span class="nc" id="L153"> log.info("Signature to verify: {}", encodedSignature);</span>
|
||||
<span class="nc" id="L154"> log.info("Public key being used: {}", PUBLIC_KEY);</span>
|
||||
|
||||
<span class="nc" id="L156"> byte[] signatureBytes = Base64.getDecoder().decode(encodedSignature);</span>
|
||||
|
||||
// Create the signing data format - prefix with "license/"
|
||||
<span class="nc" id="L159"> String signingData = String.format("license/%s", encryptedData);</span>
|
||||
<span class="nc" id="L160"> byte[] signingDataBytes = signingData.getBytes();</span>
|
||||
|
||||
<span class="nc" id="L162"> log.info("Signing data length: {} bytes", signingDataBytes.length);</span>
|
||||
|
||||
<span class="nc" id="L164"> byte[] publicKeyBytes = Hex.decode(PUBLIC_KEY);</span>
|
||||
|
||||
<span class="nc" id="L166"> Ed25519PublicKeyParameters verifierParams =</span>
|
||||
new Ed25519PublicKeyParameters(publicKeyBytes, 0);
|
||||
<span class="nc" id="L168"> Ed25519Signer verifier = new Ed25519Signer();</span>
|
||||
|
||||
<span class="nc" id="L170"> verifier.init(false, verifierParams);</span>
|
||||
<span class="nc" id="L171"> verifier.update(signingDataBytes, 0, signingDataBytes.length);</span>
|
||||
|
||||
// Verify the signature
|
||||
<span class="nc" id="L174"> boolean result = verifier.verifySignature(signatureBytes);</span>
|
||||
<span class="nc bnc" id="L175" title="All 2 branches missed."> if (!result) {</span>
|
||||
<span class="nc" id="L176"> log.error("Signature verification failed with standard public key");</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L179"> return result;</span>
|
||||
<span class="nc" id="L180"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L181"> log.error("Error verifying Ed25519 signature: {}", e.getMessage(), e);</span>
|
||||
<span class="nc" id="L182"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
private boolean processCertificateData(String certData) {
|
||||
try {
|
||||
<span class="nc" id="L188"> log.info("Processing certificate data: {}", certData);</span>
|
||||
|
||||
<span class="nc" id="L190"> JSONObject licenseData = new JSONObject(certData);</span>
|
||||
<span class="nc" id="L191"> JSONObject metaObj = licenseData.optJSONObject("meta");</span>
|
||||
<span class="nc bnc" id="L192" title="All 2 branches missed."> if (metaObj != null) {</span>
|
||||
<span class="nc" id="L193"> String issuedStr = metaObj.optString("issued", null);</span>
|
||||
<span class="nc" id="L194"> String expiryStr = metaObj.optString("expiry", null);</span>
|
||||
|
||||
<span class="nc bnc" id="L196" title="All 4 branches missed."> if (issuedStr != null && expiryStr != null) {</span>
|
||||
<span class="nc" id="L197"> java.time.Instant issued = java.time.Instant.parse(issuedStr);</span>
|
||||
<span class="nc" id="L198"> java.time.Instant expiry = java.time.Instant.parse(expiryStr);</span>
|
||||
<span class="nc" id="L199"> java.time.Instant now = java.time.Instant.now();</span>
|
||||
|
||||
<span class="nc bnc" id="L201" title="All 2 branches missed."> if (issued.isAfter(now)) {</span>
|
||||
<span class="nc" id="L202"> log.error(</span>
|
||||
"License file issued date is in the future. Please adjust system time or request a new license");
|
||||
<span class="nc" id="L204"> return false;</span>
|
||||
}
|
||||
|
||||
// Check if the license file has expired
|
||||
<span class="nc bnc" id="L208" title="All 2 branches missed."> if (expiry.isBefore(now)) {</span>
|
||||
<span class="nc" id="L209"> log.error("License file has expired on {}", expiryStr);</span>
|
||||
<span class="nc" id="L210"> return false;</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L213"> log.info("License file valid until {}", expiryStr);</span>
|
||||
}
|
||||
}
|
||||
|
||||
// Get the main license data
|
||||
<span class="nc" id="L218"> JSONObject dataObj = licenseData.optJSONObject("data");</span>
|
||||
<span class="nc bnc" id="L219" title="All 2 branches missed."> if (dataObj == null) {</span>
|
||||
<span class="nc" id="L220"> log.error("No data object found in certificate");</span>
|
||||
<span class="nc" id="L221"> return false;</span>
|
||||
}
|
||||
|
||||
// Extract license or machine information
|
||||
<span class="nc" id="L225"> JSONObject attributesObj = dataObj.optJSONObject("attributes");</span>
|
||||
<span class="nc bnc" id="L226" title="All 2 branches missed."> if (attributesObj != null) {</span>
|
||||
<span class="nc" id="L227"> log.info("Found attributes in certificate data");</span>
|
||||
|
||||
// Extract metadata
|
||||
<span class="nc" id="L230"> JSONObject metadataObj = attributesObj.optJSONObject("metadata");</span>
|
||||
<span class="nc bnc" id="L231" title="All 2 branches missed."> if (metadataObj != null) {</span>
|
||||
<span class="nc" id="L232"> int users = metadataObj.optInt("users", 0);</span>
|
||||
<span class="nc bnc" id="L233" title="All 2 branches missed."> if (users > 0) {</span>
|
||||
<span class="nc" id="L234"> applicationProperties.getPremium().setMaxUsers(users);</span>
|
||||
<span class="nc" id="L235"> log.info("License allows for {} users", users);</span>
|
||||
}
|
||||
}
|
||||
|
||||
// Check maxUsers directly in attributes if present from policy definition
|
||||
// if (attributesObj.has("maxUsers")) {
|
||||
// int maxUsers = attributesObj.optInt("maxUsers", 0);
|
||||
// if (maxUsers > 0) {
|
||||
// applicationProperties.getPremium().setMaxUsers(maxUsers);
|
||||
// log.info("License directly specifies {} max users",
|
||||
// maxUsers);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Check license status if available
|
||||
<span class="nc" id="L250"> String status = attributesObj.optString("status", null);</span>
|
||||
<span class="nc bnc" id="L251" title="All 2 branches missed."> if (status != null</span>
|
||||
<span class="nc bnc" id="L252" title="All 2 branches missed."> && !status.equals("ACTIVE")</span>
|
||||
<span class="nc bnc" id="L253" title="All 2 branches missed."> && !status.equals("EXPIRING")) { // Accept "EXPIRING" status as valid</span>
|
||||
<span class="nc" id="L254"> log.error("License status is not active: {}", status);</span>
|
||||
<span class="nc" id="L255"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
<span class="nc" id="L259"> return true;</span>
|
||||
<span class="nc" id="L260"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L261"> log.error("Error processing certificate data: {}", e.getMessage(), e);</span>
|
||||
<span class="nc" id="L262"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
private boolean verifyJWTLicense(String licenseKey) {
|
||||
try {
|
||||
<span class="nc" id="L268"> log.info("Verifying ED25519_SIGN format license key");</span>
|
||||
|
||||
// Remove the "key/" prefix
|
||||
<span class="nc" id="L271"> String licenseData = licenseKey.substring(JWT_PREFIX.length());</span>
|
||||
|
||||
// Split into payload and signature
|
||||
<span class="nc" id="L274"> String[] parts = licenseData.split("\\.", 2);</span>
|
||||
<span class="nc bnc" id="L275" title="All 2 branches missed."> if (parts.length != 2) {</span>
|
||||
<span class="nc" id="L276"> log.error(</span>
|
||||
"Invalid ED25519_SIGN license format. Expected format: key/payload.signature");
|
||||
<span class="nc" id="L278"> return false;</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L281"> String encodedPayload = parts[0];</span>
|
||||
<span class="nc" id="L282"> String encodedSignature = parts[1];</span>
|
||||
|
||||
// Verify signature
|
||||
<span class="nc" id="L285"> boolean isSignatureValid = verifyJWTSignature(encodedPayload, encodedSignature);</span>
|
||||
<span class="nc bnc" id="L286" title="All 2 branches missed."> if (!isSignatureValid) {</span>
|
||||
<span class="nc" id="L287"> log.error("ED25519_SIGN license signature is invalid");</span>
|
||||
<span class="nc" id="L288"> return false;</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L291"> log.info("ED25519_SIGN license signature is valid");</span>
|
||||
|
||||
// Decode and process payload - first convert from URL-safe base64 if needed
|
||||
<span class="nc" id="L294"> String base64Payload = encodedPayload.replace('-', '+').replace('_', '/');</span>
|
||||
<span class="nc" id="L295"> byte[] payloadBytes = Base64.getDecoder().decode(base64Payload);</span>
|
||||
<span class="nc" id="L296"> String payload = new String(payloadBytes);</span>
|
||||
|
||||
// Process the license payload
|
||||
<span class="nc" id="L299"> boolean isValid = processJWTLicensePayload(payload);</span>
|
||||
|
||||
<span class="nc" id="L301"> return isValid;</span>
|
||||
<span class="nc" id="L302"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L303"> log.error("Error verifying ED25519_SIGN license: {}", e.getMessage());</span>
|
||||
<span class="nc" id="L304"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
private boolean verifyJWTSignature(String encodedPayload, String encodedSignature) {
|
||||
try {
|
||||
// Decode base64 signature
|
||||
byte[] signatureBytes =
|
||||
<span class="nc" id="L312"> Base64.getDecoder()</span>
|
||||
<span class="nc" id="L313"> .decode(encodedSignature.replace('-', '+').replace('_', '/'));</span>
|
||||
|
||||
// For ED25519_SIGN format, the signing data is "key/" + encodedPayload
|
||||
<span class="nc" id="L316"> String signingData = String.format("key/%s", encodedPayload);</span>
|
||||
<span class="nc" id="L317"> byte[] dataBytes = signingData.getBytes();</span>
|
||||
|
||||
<span class="nc" id="L319"> byte[] publicKeyBytes = Hex.decode(PUBLIC_KEY);</span>
|
||||
<span class="nc" id="L320"> Ed25519PublicKeyParameters verifierParams =</span>
|
||||
new Ed25519PublicKeyParameters(publicKeyBytes, 0);
|
||||
<span class="nc" id="L322"> Ed25519Signer verifier = new Ed25519Signer();</span>
|
||||
|
||||
<span class="nc" id="L324"> verifier.init(false, verifierParams);</span>
|
||||
<span class="nc" id="L325"> verifier.update(dataBytes, 0, dataBytes.length);</span>
|
||||
|
||||
// Verify the signature
|
||||
<span class="nc" id="L328"> return verifier.verifySignature(signatureBytes);</span>
|
||||
<span class="nc" id="L329"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L330"> log.error("Error verifying JWT signature: {}", e.getMessage());</span>
|
||||
<span class="nc" id="L331"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
private boolean processJWTLicensePayload(String payload) {
|
||||
try {
|
||||
<span class="nc" id="L337"> log.info("Processing license payload: {}", payload);</span>
|
||||
|
||||
<span class="nc" id="L339"> JSONObject licenseData = new JSONObject(payload);</span>
|
||||
|
||||
<span class="nc" id="L341"> JSONObject licenseObj = licenseData.optJSONObject("license");</span>
|
||||
<span class="nc bnc" id="L342" title="All 2 branches missed."> if (licenseObj == null) {</span>
|
||||
<span class="nc" id="L343"> String id = licenseData.optString("id", null);</span>
|
||||
<span class="nc bnc" id="L344" title="All 2 branches missed."> if (id != null) {</span>
|
||||
<span class="nc" id="L345"> log.info("Found license ID: {}", id);</span>
|
||||
<span class="nc" id="L346"> licenseObj = licenseData; // Use the root object as the license object</span>
|
||||
} else {
|
||||
<span class="nc" id="L348"> log.error("License data not found in payload");</span>
|
||||
<span class="nc" id="L349"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
<span class="nc" id="L353"> String licenseId = licenseObj.optString("id", "unknown");</span>
|
||||
<span class="nc" id="L354"> log.info("Processing license with ID: {}", licenseId);</span>
|
||||
|
||||
// Check expiry date
|
||||
<span class="nc" id="L357"> String expiryStr = licenseObj.optString("expiry", null);</span>
|
||||
<span class="nc bnc" id="L358" title="All 4 branches missed."> if (expiryStr != null && !expiryStr.equals("null")) {</span>
|
||||
<span class="nc" id="L359"> java.time.Instant expiry = java.time.Instant.parse(expiryStr);</span>
|
||||
<span class="nc" id="L360"> java.time.Instant now = java.time.Instant.now();</span>
|
||||
|
||||
<span class="nc bnc" id="L362" title="All 2 branches missed."> if (now.isAfter(expiry)) {</span>
|
||||
<span class="nc" id="L363"> log.error("License has expired on {}", expiryStr);</span>
|
||||
<span class="nc" id="L364"> return false;</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L367"> log.info("License valid until {}", expiryStr);</span>
|
||||
<span class="nc" id="L368"> } else {</span>
|
||||
<span class="nc" id="L369"> log.info("License has no expiration date");</span>
|
||||
}
|
||||
|
||||
// Extract account, product, policy info
|
||||
<span class="nc" id="L373"> JSONObject accountObj = licenseData.optJSONObject("account");</span>
|
||||
<span class="nc bnc" id="L374" title="All 2 branches missed."> if (accountObj != null) {</span>
|
||||
<span class="nc" id="L375"> String accountId = accountObj.optString("id", "unknown");</span>
|
||||
<span class="nc" id="L376"> log.info("License belongs to account: {}", accountId);</span>
|
||||
|
||||
// Verify this matches your expected account ID
|
||||
<span class="nc bnc" id="L379" title="All 2 branches missed."> if (!ACCOUNT_ID.equals(accountId)) {</span>
|
||||
<span class="nc" id="L380"> log.warn("License account ID does not match expected account ID");</span>
|
||||
// You might want to fail verification here depending on your requirements
|
||||
}
|
||||
}
|
||||
|
||||
// Extract policy information if available
|
||||
<span class="nc" id="L386"> JSONObject policyObj = licenseData.optJSONObject("policy");</span>
|
||||
<span class="nc bnc" id="L387" title="All 2 branches missed."> if (policyObj != null) {</span>
|
||||
<span class="nc" id="L388"> String policyId = policyObj.optString("id", "unknown");</span>
|
||||
<span class="nc" id="L389"> log.info("License uses policy: {}", policyId);</span>
|
||||
|
||||
// Extract max users from policy if available (customize based on your policy
|
||||
// structure)
|
||||
<span class="nc" id="L393"> int users = policyObj.optInt("users", 0);</span>
|
||||
<span class="nc bnc" id="L394" title="All 2 branches missed."> if (users > 0) {</span>
|
||||
<span class="nc" id="L395"> applicationProperties.getPremium().setMaxUsers(users);</span>
|
||||
<span class="nc" id="L396"> log.info("License allows for {} users", users);</span>
|
||||
} else {
|
||||
// Try to get users from metadata if present
|
||||
<span class="nc" id="L399"> Object metadataObj = policyObj.opt("metadata");</span>
|
||||
<span class="nc bnc" id="L400" title="All 2 branches missed."> if (metadataObj instanceof JSONObject) {</span>
|
||||
<span class="nc" id="L401"> JSONObject metadata = (JSONObject) metadataObj;</span>
|
||||
<span class="nc" id="L402"> users = metadata.optInt("users", 1);</span>
|
||||
<span class="nc" id="L403"> applicationProperties.getPremium().setMaxUsers(users);</span>
|
||||
<span class="nc" id="L404"> log.info("License allows for {} users (from metadata)", users);</span>
|
||||
<span class="nc" id="L405"> } else {</span>
|
||||
// Default value
|
||||
<span class="nc" id="L407"> applicationProperties.getPremium().setMaxUsers(1);</span>
|
||||
<span class="nc" id="L408"> log.info("Using default of 1 user for license");</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<span class="nc" id="L413"> return true;</span>
|
||||
<span class="nc" id="L414"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L415"> log.error("Error processing license payload: {}", e.getMessage(), e);</span>
|
||||
<span class="nc" id="L416"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
private boolean verifyStandardLicense(String licenseKey) {
|
||||
try {
|
||||
<span class="nc" id="L422"> log.info("Checking standard license key");</span>
|
||||
<span class="nc" id="L423"> String machineFingerprint = generateMachineFingerprint();</span>
|
||||
|
||||
// First, try to validate the license
|
||||
<span class="nc" id="L426"> JsonNode validationResponse = validateLicense(licenseKey, machineFingerprint);</span>
|
||||
<span class="nc bnc" id="L427" title="All 2 branches missed."> if (validationResponse != null) {</span>
|
||||
<span class="nc" id="L428"> boolean isValid = validationResponse.path("meta").path("valid").asBoolean();</span>
|
||||
<span class="nc" id="L429"> String licenseId = validationResponse.path("data").path("id").asText();</span>
|
||||
<span class="nc bnc" id="L430" title="All 2 branches missed."> if (!isValid) {</span>
|
||||
<span class="nc" id="L431"> String code = validationResponse.path("meta").path("code").asText();</span>
|
||||
<span class="nc" id="L432"> log.info(code);</span>
|
||||
<span class="nc bnc" id="L433" title="All 2 branches missed."> if ("NO_MACHINE".equals(code)</span>
|
||||
<span class="nc bnc" id="L434" title="All 2 branches missed."> || "NO_MACHINES".equals(code)</span>
|
||||
<span class="nc bnc" id="L435" title="All 2 branches missed."> || "FINGERPRINT_SCOPE_MISMATCH".equals(code)) {</span>
|
||||
<span class="nc" id="L436"> log.info(</span>
|
||||
"License not activated for this machine. Attempting to activate...");
|
||||
<span class="nc" id="L438"> boolean activated =</span>
|
||||
<span class="nc" id="L439"> activateMachine(licenseKey, licenseId, machineFingerprint);</span>
|
||||
<span class="nc bnc" id="L440" title="All 2 branches missed."> if (activated) {</span>
|
||||
// Revalidate after activation
|
||||
<span class="nc" id="L442"> validationResponse = validateLicense(licenseKey, machineFingerprint);</span>
|
||||
<span class="nc bnc" id="L443" title="All 2 branches missed."> isValid =</span>
|
||||
validationResponse != null
|
||||
&& validationResponse
|
||||
<span class="nc" id="L446"> .path("meta")</span>
|
||||
<span class="nc" id="L447"> .path("valid")</span>
|
||||
<span class="nc bnc" id="L448" title="All 2 branches missed."> .asBoolean();</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L452"> return isValid;</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L455"> return false;</span>
|
||||
<span class="nc" id="L456"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L457"> log.error("Error verifying standard license: {}", e.getMessage());</span>
|
||||
<span class="nc" id="L458"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
private JsonNode validateLicense(String licenseKey, String machineFingerprint)
|
||||
throws Exception {
|
||||
<span class="nc" id="L464"> HttpClient client = HttpClient.newHttpClient();</span>
|
||||
<span class="nc" id="L465"> String requestBody =</span>
|
||||
<span class="nc" id="L466"> String.format(</span>
|
||||
"{\"meta\":{\"key\":\"%s\",\"scope\":{\"fingerprint\":\"%s\"}}}",
|
||||
licenseKey, machineFingerprint);
|
||||
HttpRequest request =
|
||||
<span class="nc" id="L470"> HttpRequest.newBuilder()</span>
|
||||
<span class="nc" id="L471"> .uri(</span>
|
||||
<span class="nc" id="L472"> URI.create(</span>
|
||||
BASE_URL
|
||||
+ "/"
|
||||
+ ACCOUNT_ID
|
||||
+ "/licenses/actions/validate-key"))
|
||||
<span class="nc" id="L477"> .header("Content-Type", "application/vnd.api+json")</span>
|
||||
<span class="nc" id="L478"> .header("Accept", "application/vnd.api+json")</span>
|
||||
// .header("Authorization", "License " + licenseKey)
|
||||
<span class="nc" id="L480"> .POST(HttpRequest.BodyPublishers.ofString(requestBody))</span>
|
||||
<span class="nc" id="L481"> .build();</span>
|
||||
|
||||
<span class="nc" id="L483"> HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());</span>
|
||||
<span class="nc" id="L484"> log.info("ValidateLicenseResponse body: {}", response.body());</span>
|
||||
<span class="nc" id="L485"> JsonNode jsonResponse = objectMapper.readTree(response.body());</span>
|
||||
<span class="nc bnc" id="L486" title="All 2 branches missed."> if (response.statusCode() == 200) {</span>
|
||||
<span class="nc" id="L487"> JsonNode metaNode = jsonResponse.path("meta");</span>
|
||||
<span class="nc" id="L488"> boolean isValid = metaNode.path("valid").asBoolean();</span>
|
||||
|
||||
<span class="nc" id="L490"> String detail = metaNode.path("detail").asText();</span>
|
||||
<span class="nc" id="L491"> String code = metaNode.path("code").asText();</span>
|
||||
|
||||
<span class="nc" id="L493"> log.info("License validity: " + isValid);</span>
|
||||
<span class="nc" id="L494"> log.info("Validation detail: " + detail);</span>
|
||||
<span class="nc" id="L495"> log.info("Validation code: " + code);</span>
|
||||
|
||||
<span class="nc" id="L497"> int users =</span>
|
||||
jsonResponse
|
||||
<span class="nc" id="L499"> .path("data")</span>
|
||||
<span class="nc" id="L500"> .path("attributes")</span>
|
||||
<span class="nc" id="L501"> .path("metadata")</span>
|
||||
<span class="nc" id="L502"> .path("users")</span>
|
||||
<span class="nc" id="L503"> .asInt(0);</span>
|
||||
<span class="nc" id="L504"> applicationProperties.getPremium().setMaxUsers(users);</span>
|
||||
<span class="nc" id="L505"> log.info(applicationProperties.toString());</span>
|
||||
|
||||
<span class="nc" id="L507"> } else {</span>
|
||||
<span class="nc" id="L508"> log.error("Error validating license. Status code: {}", response.statusCode());</span>
|
||||
}
|
||||
<span class="nc" id="L510"> return jsonResponse;</span>
|
||||
}
|
||||
|
||||
private boolean activateMachine(String licenseKey, String licenseId, String machineFingerprint)
|
||||
throws Exception {
|
||||
<span class="nc" id="L515"> HttpClient client = HttpClient.newHttpClient();</span>
|
||||
|
||||
String hostname;
|
||||
try {
|
||||
<span class="nc" id="L519"> hostname = java.net.InetAddress.getLocalHost().getHostName();</span>
|
||||
<span class="nc" id="L520"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L521"> hostname = "Unknown";</span>
|
||||
<span class="nc" id="L522"> }</span>
|
||||
|
||||
<span class="nc" id="L524"> JSONObject body =</span>
|
||||
new JSONObject()
|
||||
<span class="nc" id="L526"> .put(</span>
|
||||
"data",
|
||||
new JSONObject()
|
||||
<span class="nc" id="L529"> .put("type", "machines")</span>
|
||||
<span class="nc" id="L530"> .put(</span>
|
||||
"attributes",
|
||||
new JSONObject()
|
||||
<span class="nc" id="L533"> .put("fingerprint", machineFingerprint)</span>
|
||||
<span class="nc" id="L534"> .put(</span>
|
||||
"platform",
|
||||
<span class="nc" id="L536"> System.getProperty("os.name"))</span>
|
||||
<span class="nc" id="L537"> .put("name", hostname))</span>
|
||||
<span class="nc" id="L538"> .put(</span>
|
||||
"relationships",
|
||||
new JSONObject()
|
||||
<span class="nc" id="L541"> .put(</span>
|
||||
"license",
|
||||
new JSONObject()
|
||||
<span class="nc" id="L544"> .put(</span>
|
||||
"data",
|
||||
new JSONObject()
|
||||
<span class="nc" id="L547"> .put(</span>
|
||||
"type",
|
||||
"licenses")
|
||||
<span class="nc" id="L550"> .put(</span>
|
||||
"id",
|
||||
licenseId)))));
|
||||
|
||||
HttpRequest request =
|
||||
<span class="nc" id="L555"> HttpRequest.newBuilder()</span>
|
||||
<span class="nc" id="L556"> .uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines"))</span>
|
||||
<span class="nc" id="L557"> .header("Content-Type", "application/vnd.api+json")</span>
|
||||
<span class="nc" id="L558"> .header("Accept", "application/vnd.api+json")</span>
|
||||
<span class="nc" id="L559"> .header("Authorization", "License " + licenseKey)</span>
|
||||
<span class="nc" id="L560"> .POST(HttpRequest.BodyPublishers.ofString(body.toString()))</span>
|
||||
<span class="nc" id="L561"> .build();</span>
|
||||
|
||||
<span class="nc" id="L563"> HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());</span>
|
||||
<span class="nc" id="L564"> log.info("activateMachine Response body: " + response.body());</span>
|
||||
<span class="nc bnc" id="L565" title="All 2 branches missed."> if (response.statusCode() == 201) {</span>
|
||||
<span class="nc" id="L566"> log.info("Machine activated successfully");</span>
|
||||
<span class="nc" id="L567"> return true;</span>
|
||||
} else {
|
||||
<span class="nc" id="L569"> log.error(</span>
|
||||
"Error activating machine. Status code: {}, error: {}",
|
||||
<span class="nc" id="L571"> response.statusCode(),</span>
|
||||
<span class="nc" id="L572"> response.body());</span>
|
||||
|
||||
<span class="nc" id="L574"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
private String generateMachineFingerprint() {
|
||||
<span class="nc" id="L579"> return GeneralUtils.generateMachineFingerprint();</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>LicenseKeyChecker.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.EE</a> > <span class="el_source">LicenseKeyChecker.java</span></div><h1>LicenseKeyChecker.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.EE;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
||||
@Component
|
||||
<span class="nc" id="L19">@Slf4j</span>
|
||||
public class LicenseKeyChecker {
|
||||
|
||||
private static final String FILE_PREFIX = "file:";
|
||||
|
||||
private final KeygenLicenseVerifier licenseService;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
<span class="nc" id="L28"> private License premiumEnabledResult = License.NORMAL;</span>
|
||||
|
||||
@Autowired
|
||||
public LicenseKeyChecker(
|
||||
<span class="nc" id="L32"> KeygenLicenseVerifier licenseService, ApplicationProperties applicationProperties) {</span>
|
||||
<span class="nc" id="L33"> this.licenseService = licenseService;</span>
|
||||
<span class="nc" id="L34"> this.applicationProperties = applicationProperties;</span>
|
||||
<span class="nc" id="L35"> this.checkLicense();</span>
|
||||
<span class="nc" id="L36"> }</span>
|
||||
|
||||
@Scheduled(initialDelay = 604800000, fixedRate = 604800000) // 7 days in milliseconds
|
||||
public void checkLicensePeriodically() {
|
||||
<span class="nc" id="L40"> checkLicense();</span>
|
||||
<span class="nc" id="L41"> }</span>
|
||||
|
||||
private void checkLicense() {
|
||||
<span class="nc bnc" id="L44" title="All 2 branches missed."> if (!applicationProperties.getPremium().isEnabled()) {</span>
|
||||
<span class="nc" id="L45"> premiumEnabledResult = License.NORMAL;</span>
|
||||
} else {
|
||||
<span class="nc" id="L47"> String licenseKey = getLicenseKeyContent(applicationProperties.getPremium().getKey());</span>
|
||||
<span class="nc bnc" id="L48" title="All 2 branches missed."> if (licenseKey != null) {</span>
|
||||
<span class="nc" id="L49"> premiumEnabledResult = licenseService.verifyLicense(licenseKey);</span>
|
||||
<span class="nc bnc" id="L50" title="All 2 branches missed."> if (License.ENTERPRISE == premiumEnabledResult) {</span>
|
||||
<span class="nc" id="L51"> log.info("License key is Enterprise.");</span>
|
||||
<span class="nc bnc" id="L52" title="All 2 branches missed."> } else if (License.PRO == premiumEnabledResult) {</span>
|
||||
<span class="nc" id="L53"> log.info("License key is Pro.");</span>
|
||||
} else {
|
||||
<span class="nc" id="L55"> log.info("License key is invalid, defaulting to non pro license.");</span>
|
||||
}
|
||||
} else {
|
||||
<span class="nc" id="L58"> log.error("Failed to obtain license key content.");</span>
|
||||
<span class="nc" id="L59"> premiumEnabledResult = License.NORMAL;</span>
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L62"> }</span>
|
||||
|
||||
private String getLicenseKeyContent(String keyOrFilePath) {
|
||||
<span class="nc bnc" id="L65" title="All 4 branches missed."> if (keyOrFilePath == null || keyOrFilePath.trim().isEmpty()) {</span>
|
||||
<span class="nc" id="L66"> log.error("License key is not specified");</span>
|
||||
<span class="nc" id="L67"> return null;</span>
|
||||
}
|
||||
|
||||
// Check if it's a file reference
|
||||
<span class="nc bnc" id="L71" title="All 2 branches missed."> if (keyOrFilePath.startsWith(FILE_PREFIX)) {</span>
|
||||
<span class="nc" id="L72"> String filePath = keyOrFilePath.substring(FILE_PREFIX.length());</span>
|
||||
try {
|
||||
<span class="nc" id="L74"> Path path = Paths.get(filePath);</span>
|
||||
<span class="nc bnc" id="L75" title="All 2 branches missed."> if (!Files.exists(path)) {</span>
|
||||
<span class="nc" id="L76"> log.error("License file does not exist: {}", filePath);</span>
|
||||
<span class="nc" id="L77"> return null;</span>
|
||||
}
|
||||
<span class="nc" id="L79"> log.info("Reading license from file: {}", filePath);</span>
|
||||
<span class="nc" id="L80"> return Files.readString(path);</span>
|
||||
<span class="nc" id="L81"> } catch (IOException e) {</span>
|
||||
<span class="nc" id="L82"> log.error("Failed to read license file: {}", e.getMessage());</span>
|
||||
<span class="nc" id="L83"> return null;</span>
|
||||
}
|
||||
}
|
||||
|
||||
// It's a direct license key
|
||||
<span class="nc" id="L88"> return keyOrFilePath;</span>
|
||||
}
|
||||
|
||||
public void updateLicenseKey(String newKey) throws IOException {
|
||||
<span class="nc" id="L92"> applicationProperties.getPremium().setKey(newKey);</span>
|
||||
<span class="nc" id="L93"> GeneralUtils.saveKeyToSettings("EnterpriseEdition.key", newKey);</span>
|
||||
<span class="nc" id="L94"> checkLicense();</span>
|
||||
<span class="nc" id="L95"> }</span>
|
||||
|
||||
public License getPremiumLicenseEnabledResult() {
|
||||
<span class="nc" id="L98"> return premiumEnabledResult;</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
1
testResult/html/stirling.software.SPDF.EE/index.html
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>stirling.software.SPDF.EE</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb', 'coveragetable'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="index.html" class="el_class">Classes</a><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <span class="el_package">stirling.software.SPDF.EE</span></div><h1>stirling.software.SPDF.EE</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td><td class="sortable ctr1" id="l" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="m" onclick="toggleSort(this)">Classes</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">1,397 of 1,397</td><td class="ctr2">0%</td><td class="bar">166 of 166</td><td class="ctr2">0%</td><td class="ctr1">115</td><td class="ctr2">115</td><td class="ctr1">385</td><td class="ctr2">385</td><td class="ctr1">32</td><td class="ctr2">32</td><td class="ctr1">4</td><td class="ctr2">4</td></tr></tfoot><tbody><tr><td id="a1"><a href="KeygenLicenseVerifier.java.html" class="el_source">KeygenLicenseVerifier.java</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="1,060" alt="1,060"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="88" alt="88"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">61</td><td class="ctr2" id="g0">61</td><td class="ctr1" id="h0">294</td><td class="ctr2" id="i0">294</td><td class="ctr1" id="j0">17</td><td class="ctr2" id="k0">17</td><td class="ctr1" id="l0">2</td><td class="ctr2" id="m0">2</td></tr><tr><td id="a0"><a href="EEAppConfig.java.html" class="el_source">EEAppConfig.java</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="22" height="10" title="198" alt="198"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"><img src="../jacoco-resources/redbar.gif" width="84" height="10" title="62" alt="62"/></td><td class="ctr2" id="e1">0%</td><td class="ctr1" id="f1">39</td><td class="ctr2" id="g1">39</td><td class="ctr1" id="h1">49</td><td class="ctr2" id="i1">49</td><td class="ctr1" id="j1">8</td><td class="ctr2" id="k1">8</td><td class="ctr1" id="l1">1</td><td class="ctr2" id="m1">1</td></tr><tr><td id="a2"><a href="LicenseKeyChecker.java.html" class="el_source">LicenseKeyChecker.java</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="15" height="10" title="139" alt="139"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"><img src="../jacoco-resources/redbar.gif" width="21" height="10" title="16" alt="16"/></td><td class="ctr2" id="e2">0%</td><td class="ctr1" id="f2">15</td><td class="ctr2" id="g2">15</td><td class="ctr1" id="h2">42</td><td class="ctr2" id="i2">42</td><td class="ctr1" id="j2">7</td><td class="ctr2" id="k2">7</td><td class="ctr1" id="l2">1</td><td class="ctr2" id="m2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ReplaceAndInvertColorFactory</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.Factories</a> > <span class="el_class">ReplaceAndInvertColorFactory</span></div><h1>ReplaceAndInvertColorFactory</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">29 of 29</td><td class="ctr2">0%</td><td class="bar">6 of 6</td><td class="ctr2">0%</td><td class="ctr1">5</td><td class="ctr2">5</td><td class="ctr1">6</td><td class="ctr2">6</td><td class="ctr1">2</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a0"><a href="ReplaceAndInvertColorFactory.java.html#L22" class="el_method">replaceAndInvert(MultipartFile, ReplaceAndInvert, HighContrastColorCombination, String, String)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="26" alt="26"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="6" alt="6"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">4</td><td class="ctr2" id="g0">4</td><td class="ctr1" id="h0">5</td><td class="ctr2" id="i0">5</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="ReplaceAndInvertColorFactory.java.html#L13" class="el_method">ReplaceAndInvertColorFactory()</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="13" height="10" title="3" alt="3"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">1</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ReplaceAndInvertColorFactory.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.Factories</a> > <span class="el_source">ReplaceAndInvertColorFactory.java</span></div><h1>ReplaceAndInvertColorFactory.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.Factories;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.HighContrastColorCombination;
|
||||
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
|
||||
import stirling.software.SPDF.utils.misc.CustomColorReplaceStrategy;
|
||||
import stirling.software.SPDF.utils.misc.InvertFullColorStrategy;
|
||||
import stirling.software.SPDF.utils.misc.ReplaceAndInvertColorStrategy;
|
||||
|
||||
@Component
|
||||
<span class="nc" id="L13">public class ReplaceAndInvertColorFactory {</span>
|
||||
|
||||
public ReplaceAndInvertColorStrategy replaceAndInvert(
|
||||
MultipartFile file,
|
||||
ReplaceAndInvert replaceAndInvertOption,
|
||||
HighContrastColorCombination highContrastColorCombination,
|
||||
String backGroundColor,
|
||||
String textColor) {
|
||||
|
||||
<span class="nc bnc" id="L22" title="All 4 branches missed."> if (replaceAndInvertOption == ReplaceAndInvert.CUSTOM_COLOR</span>
|
||||
|| replaceAndInvertOption == ReplaceAndInvert.HIGH_CONTRAST_COLOR) {
|
||||
|
||||
<span class="nc" id="L25"> return new CustomColorReplaceStrategy(</span>
|
||||
file,
|
||||
replaceAndInvertOption,
|
||||
textColor,
|
||||
backGroundColor,
|
||||
highContrastColorCombination);
|
||||
|
||||
<span class="nc bnc" id="L32" title="All 2 branches missed."> } else if (replaceAndInvertOption == ReplaceAndInvert.FULL_INVERSION) {</span>
|
||||
|
||||
<span class="nc" id="L34"> return new InvertFullColorStrategy(file, replaceAndInvertOption);</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L37"> return null;</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>stirling.software.SPDF.Factories</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb', 'coveragetable'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="index.source.html" class="el_source">Source Files</a><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <span class="el_package">stirling.software.SPDF.Factories</span></div><h1>stirling.software.SPDF.Factories</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td><td class="sortable ctr1" id="l" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="m" onclick="toggleSort(this)">Classes</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">29 of 29</td><td class="ctr2">0%</td><td class="bar">6 of 6</td><td class="ctr2">0%</td><td class="ctr1">5</td><td class="ctr2">5</td><td class="ctr1">6</td><td class="ctr2">6</td><td class="ctr1">2</td><td class="ctr2">2</td><td class="ctr1">1</td><td class="ctr2">1</td></tr></tfoot><tbody><tr><td id="a0"><a href="ReplaceAndInvertColorFactory.html" class="el_class">ReplaceAndInvertColorFactory</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="29" alt="29"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="6" alt="6"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">5</td><td class="ctr2" id="g0">5</td><td class="ctr1" id="h0">6</td><td class="ctr2" id="i0">6</td><td class="ctr1" id="j0">2</td><td class="ctr2" id="k0">2</td><td class="ctr1" id="l0">1</td><td class="ctr2" id="m0">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>stirling.software.SPDF.Factories</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb', 'coveragetable'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="index.html" class="el_class">Classes</a><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <span class="el_package">stirling.software.SPDF.Factories</span></div><h1>stirling.software.SPDF.Factories</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td><td class="sortable ctr1" id="l" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="m" onclick="toggleSort(this)">Classes</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">29 of 29</td><td class="ctr2">0%</td><td class="bar">6 of 6</td><td class="ctr2">0%</td><td class="ctr1">5</td><td class="ctr2">5</td><td class="ctr1">6</td><td class="ctr2">6</td><td class="ctr1">2</td><td class="ctr2">2</td><td class="ctr1">1</td><td class="ctr2">1</td></tr></tfoot><tbody><tr><td id="a0"><a href="ReplaceAndInvertColorFactory.java.html" class="el_source">ReplaceAndInvertColorFactory.java</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="29" alt="29"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="6" alt="6"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">5</td><td class="ctr2" id="g0">5</td><td class="ctr1" id="h0">6</td><td class="ctr2" id="i0">6</td><td class="ctr1" id="j0">2</td><td class="ctr2" id="k0">2</td><td class="ctr1" id="l0">1</td><td class="ctr2" id="m0">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DesktopBrowser.new MavenCefAppHandlerAdapter() {...}</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.UI.impl</a> > <span class="el_class">DesktopBrowser.new MavenCefAppHandlerAdapter() {...}</span></div><h1>DesktopBrowser.new MavenCefAppHandlerAdapter() {...}</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">17 of 17</td><td class="ctr2">0%</td><td class="bar">2 of 2</td><td class="ctr2">0%</td><td class="ctr1">3</td><td class="ctr2">3</td><td class="ctr1">5</td><td class="ctr2">5</td><td class="ctr1">2</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a0"><a href="DesktopBrowser.java.html#L139" class="el_method">stateHasChanged(CefApp.CefAppState)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="11" alt="11"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">2</td><td class="ctr2" id="g0">2</td><td class="ctr1" id="h0">4</td><td class="ctr2" id="i0">4</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="DesktopBrowser.java.html#L136" class="el_method">{...}</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="65" height="10" title="6" alt="6"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">1</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DesktopBrowser.new CefDownloadHandlerAdapter() {...}</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.UI.impl</a> > <span class="el_class">DesktopBrowser.new CefDownloadHandlerAdapter() {...}</span></div><h1>DesktopBrowser.new CefDownloadHandlerAdapter() {...}</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">30 of 30</td><td class="ctr2">0%</td><td class="bar">4 of 4</td><td class="ctr2">0%</td><td class="ctr1">5</td><td class="ctr2">5</td><td class="ctr1">8</td><td class="ctr2">8</td><td class="ctr1">3</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a1"><a href="DesktopBrowser.java.html#L165" class="el_method">onDownloadUpdated(CefBrowser, CefDownloadItem, CefDownloadItemCallback)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="18" alt="18"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="4" alt="4"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">3</td><td class="ctr2" id="g0">3</td><td class="ctr1" id="h0">5</td><td class="ctr2" id="i0">5</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a2"><a href="DesktopBrowser.java.html#L149" class="el_method">{...}</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="40" height="10" title="6" alt="6"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h2">1</td><td class="ctr2" id="i2">1</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a0"><a href="DesktopBrowser.java.html#L156" class="el_method">onBeforeDownload(CefBrowser, CefDownloadItem, String, CefBeforeDownloadCallback)</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="40" height="10" title="6" alt="6"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h1">2</td><td class="ctr2" id="i1">2</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DesktopBrowser.new ConsoleProgressHandler() {...}</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.UI.impl</a> > <span class="el_class">DesktopBrowser.new ConsoleProgressHandler() {...}</span></div><h1>DesktopBrowser.new ConsoleProgressHandler() {...}</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">70 of 70</td><td class="ctr2">0%</td><td class="bar">10 of 10</td><td class="ctr2">0%</td><td class="ctr1">10</td><td class="ctr2">10</td><td class="ctr1">23</td><td class="ctr2">23</td><td class="ctr1">3</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a1"><a href="DesktopBrowser.java.html#L181" class="el_method">lambda$handleProgress$0(EnumProgress, float)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="55" alt="55"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="10" alt="10"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">8</td><td class="ctr2" id="g0">8</td><td class="ctr1" id="h0">19</td><td class="ctr2" id="i0">19</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a0"><a href="DesktopBrowser.java.html#L178" class="el_method">handleProgress(EnumProgress, float)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="19" height="10" title="9" alt="9"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">3</td><td class="ctr2" id="i1">3</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a2"><a href="DesktopBrowser.java.html#L175" class="el_method">{...}</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="13" height="10" title="6" alt="6"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">1</td><td class="ctr2" id="i2">1</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DesktopBrowser.new WindowAdapter() {...}</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.UI.impl</a> > <span class="el_class">DesktopBrowser.new WindowAdapter() {...}</span></div><h1>DesktopBrowser.new WindowAdapter() {...}</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">12 of 12</td><td class="ctr2">0%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">2</td><td class="ctr2">2</td><td class="ctr1">4</td><td class="ctr2">4</td><td class="ctr1">2</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a1"><a href="DesktopBrowser.java.html#L227" class="el_method">{...}</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="6" alt="6"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">1</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h1">1</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a0"><a href="DesktopBrowser.java.html#L230" class="el_method">windowClosing(WindowEvent)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="6" alt="6"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h0">3</td><td class="ctr2" id="i0">3</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DesktopBrowser.new CefLoadHandlerAdapter() {...}</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.UI.impl</a> > <span class="el_class">DesktopBrowser.new CefLoadHandlerAdapter() {...}</span></div><h1>DesktopBrowser.new CefLoadHandlerAdapter() {...}</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">150 of 150</td><td class="ctr2">0%</td><td class="bar">10 of 10</td><td class="ctr2">0%</td><td class="ctr1">9</td><td class="ctr2">9</td><td class="ctr1">52</td><td class="ctr2">52</td><td class="ctr1">4</td><td class="ctr2">4</td></tr></tfoot><tbody><tr><td id="a1"><a href="DesktopBrowser.java.html#L270" class="el_method">lambda$onLoadingStateChange$1(CefBrowser)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="82" alt="82"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="6" alt="6"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">4</td><td class="ctr2" id="g0">4</td><td class="ctr1" id="h0">33</td><td class="ctr2" id="i0">33</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a2"><a href="DesktopBrowser.java.html#L253" class="el_method">onLoadingStateChange(CefBrowser, boolean, boolean, boolean)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="67" height="10" title="46" alt="46"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"><img src="../jacoco-resources/redbar.gif" width="80" height="10" title="4" alt="4"/></td><td class="ctr2" id="e1">0%</td><td class="ctr1" id="f1">3</td><td class="ctr2" id="g1">3</td><td class="ctr1" id="h1">11</td><td class="ctr2" id="i1">11</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a0"><a href="DesktopBrowser.java.html#L303" class="el_method">lambda$onLoadingStateChange$0(CefBrowser, ActionEvent)</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="19" height="10" title="13" alt="13"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">7</td><td class="ctr2" id="i2">7</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr><tr><td id="a3"><a href="DesktopBrowser.java.html#L246" class="el_method">{...}</a></td><td class="bar" id="b3"><img src="../jacoco-resources/redbar.gif" width="13" height="10" title="9" alt="9"/></td><td class="ctr2" id="c3">0%</td><td class="bar" id="d3"/><td class="ctr2" id="e3">n/a</td><td class="ctr1" id="f3">1</td><td class="ctr2" id="g3">1</td><td class="ctr1" id="h3">1</td><td class="ctr2" id="i3">1</td><td class="ctr1" id="j3">1</td><td class="ctr2" id="k3">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DesktopBrowser.new WindowStateListener() {...}</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.UI.impl</a> > <span class="el_class">DesktopBrowser.new WindowStateListener() {...}</span></div><h1>DesktopBrowser.new WindowStateListener() {...}</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">14 of 14</td><td class="ctr2">0%</td><td class="bar">2 of 2</td><td class="ctr2">0%</td><td class="ctr1">3</td><td class="ctr2">3</td><td class="ctr1">4</td><td class="ctr2">4</td><td class="ctr1">2</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a0"><a href="DesktopBrowser.java.html#L386" class="el_method">windowStateChanged(WindowEvent)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="8" alt="8"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">2</td><td class="ctr2" id="g0">2</td><td class="ctr1" id="h0">3</td><td class="ctr2" id="i0">3</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="DesktopBrowser.java.html#L384" class="el_method">{...}</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="90" height="10" title="6" alt="6"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">1</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,498 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DesktopBrowser.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.UI.impl</a> > <span class="el_source">DesktopBrowser.java</span></div><h1>DesktopBrowser.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.UI.impl;
|
||||
|
||||
import java.awt.AWTException;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Image;
|
||||
import java.awt.MenuItem;
|
||||
import java.awt.PopupMenu;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowStateListener;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.Timer;
|
||||
|
||||
import org.cef.CefApp;
|
||||
import org.cef.CefClient;
|
||||
import org.cef.CefSettings;
|
||||
import org.cef.browser.CefBrowser;
|
||||
import org.cef.callback.CefBeforeDownloadCallback;
|
||||
import org.cef.callback.CefDownloadItem;
|
||||
import org.cef.callback.CefDownloadItemCallback;
|
||||
import org.cef.handler.CefDownloadHandlerAdapter;
|
||||
import org.cef.handler.CefLoadHandlerAdapter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PreDestroy;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import me.friwi.jcefmaven.CefAppBuilder;
|
||||
import me.friwi.jcefmaven.EnumProgress;
|
||||
import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
|
||||
import me.friwi.jcefmaven.impl.progress.ConsoleProgressHandler;
|
||||
|
||||
import stirling.software.SPDF.UI.WebBrowser;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.utils.UIScaling;
|
||||
|
||||
@Component
|
||||
<span class="nc" id="L50">@Slf4j</span>
|
||||
@ConditionalOnProperty(
|
||||
name = "STIRLING_PDF_DESKTOP_UI",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public class DesktopBrowser implements WebBrowser {
|
||||
private static CefApp cefApp;
|
||||
private static CefClient client;
|
||||
private static CefBrowser browser;
|
||||
private static JFrame frame;
|
||||
private static LoadingWindow loadingWindow;
|
||||
<span class="nc" id="L61"> private static volatile boolean browserInitialized = false;</span>
|
||||
private static TrayIcon trayIcon;
|
||||
private static SystemTray systemTray;
|
||||
|
||||
<span class="nc" id="L65"> public DesktopBrowser() {</span>
|
||||
<span class="nc" id="L66"> SwingUtilities.invokeLater(</span>
|
||||
() -> {
|
||||
<span class="nc" id="L68"> loadingWindow = new LoadingWindow(null, "Initializing...");</span>
|
||||
<span class="nc" id="L69"> loadingWindow.setVisible(true);</span>
|
||||
<span class="nc" id="L70"> });</span>
|
||||
<span class="nc" id="L71"> }</span>
|
||||
|
||||
public void initWebUI(String url) {
|
||||
<span class="nc" id="L74"> CompletableFuture.runAsync(</span>
|
||||
() -> {
|
||||
try {
|
||||
<span class="nc" id="L77"> CefAppBuilder builder = new CefAppBuilder();</span>
|
||||
<span class="nc" id="L78"> configureCefSettings(builder);</span>
|
||||
<span class="nc" id="L79"> builder.setProgressHandler(createProgressHandler());</span>
|
||||
<span class="nc" id="L80"> builder.setInstallDir(</span>
|
||||
<span class="nc" id="L81"> new File(InstallationPathConfig.getClientWebUIPath()));</span>
|
||||
// Build and initialize CEF
|
||||
<span class="nc" id="L83"> cefApp = builder.build();</span>
|
||||
<span class="nc" id="L84"> client = cefApp.createClient();</span>
|
||||
|
||||
// Set up download handler
|
||||
<span class="nc" id="L87"> setupDownloadHandler();</span>
|
||||
|
||||
// Create browser and frame on EDT
|
||||
<span class="nc" id="L90"> SwingUtilities.invokeAndWait(</span>
|
||||
() -> {
|
||||
<span class="nc" id="L92"> browser = client.createBrowser(url, false, false);</span>
|
||||
<span class="nc" id="L93"> setupMainFrame();</span>
|
||||
<span class="nc" id="L94"> setupLoadHandler();</span>
|
||||
|
||||
// Force initialize UI after 7 seconds if not already done
|
||||
<span class="nc" id="L97"> Timer timeoutTimer =</span>
|
||||
new Timer(
|
||||
2500,
|
||||
e -> {
|
||||
<span class="nc" id="L101"> log.warn(</span>
|
||||
"Loading timeout reached. Forcing"
|
||||
+ " UI transition.");
|
||||
<span class="nc bnc" id="L104" title="All 2 branches missed."> if (!browserInitialized) {</span>
|
||||
// Force UI initialization
|
||||
<span class="nc" id="L106"> forceInitializeUI();</span>
|
||||
}
|
||||
<span class="nc" id="L108"> });</span>
|
||||
<span class="nc" id="L109"> timeoutTimer.setRepeats(false);</span>
|
||||
<span class="nc" id="L110"> timeoutTimer.start();</span>
|
||||
<span class="nc" id="L111"> });</span>
|
||||
<span class="nc" id="L112"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L113"> log.error("Error initializing JCEF browser: ", e);</span>
|
||||
<span class="nc" id="L114"> cleanup();</span>
|
||||
<span class="nc" id="L115"> }</span>
|
||||
<span class="nc" id="L116"> });</span>
|
||||
<span class="nc" id="L117"> }</span>
|
||||
|
||||
private void configureCefSettings(CefAppBuilder builder) {
|
||||
<span class="nc" id="L120"> CefSettings settings = builder.getCefSettings();</span>
|
||||
<span class="nc" id="L121"> String basePath = InstallationPathConfig.getClientWebUIPath();</span>
|
||||
<span class="nc" id="L122"> log.info("basePath " + basePath);</span>
|
||||
<span class="nc" id="L123"> settings.cache_path = new File(basePath + "cache").getAbsolutePath();</span>
|
||||
<span class="nc" id="L124"> settings.root_cache_path = new File(basePath + "root_cache").getAbsolutePath();</span>
|
||||
// settings.browser_subprocess_path = new File(basePath +
|
||||
// "subprocess").getAbsolutePath();
|
||||
// settings.resources_dir_path = new File(basePath + "resources").getAbsolutePath();
|
||||
// settings.locales_dir_path = new File(basePath + "locales").getAbsolutePath();
|
||||
<span class="nc" id="L129"> settings.log_file = new File(basePath, "debug.log").getAbsolutePath();</span>
|
||||
|
||||
<span class="nc" id="L131"> settings.persist_session_cookies = true;</span>
|
||||
<span class="nc" id="L132"> settings.windowless_rendering_enabled = false;</span>
|
||||
<span class="nc" id="L133"> settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_INFO;</span>
|
||||
|
||||
<span class="nc" id="L135"> builder.setAppHandler(</span>
|
||||
<span class="nc" id="L136"> new MavenCefAppHandlerAdapter() {</span>
|
||||
@Override
|
||||
public void stateHasChanged(org.cef.CefApp.CefAppState state) {
|
||||
<span class="nc" id="L139"> log.info("CEF state changed: " + state);</span>
|
||||
<span class="nc bnc" id="L140" title="All 2 branches missed."> if (state == CefApp.CefAppState.TERMINATED) {</span>
|
||||
<span class="nc" id="L141"> System.exit(0);</span>
|
||||
}
|
||||
<span class="nc" id="L143"> }</span>
|
||||
});
|
||||
<span class="nc" id="L145"> }</span>
|
||||
|
||||
private void setupDownloadHandler() {
|
||||
<span class="nc" id="L148"> client.addDownloadHandler(</span>
|
||||
<span class="nc" id="L149"> new CefDownloadHandlerAdapter() {</span>
|
||||
@Override
|
||||
public boolean onBeforeDownload(
|
||||
CefBrowser browser,
|
||||
CefDownloadItem downloadItem,
|
||||
String suggestedName,
|
||||
CefBeforeDownloadCallback callback) {
|
||||
<span class="nc" id="L156"> callback.Continue("", true);</span>
|
||||
<span class="nc" id="L157"> return true;</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadUpdated(
|
||||
CefBrowser browser,
|
||||
CefDownloadItem downloadItem,
|
||||
CefDownloadItemCallback callback) {
|
||||
<span class="nc bnc" id="L165" title="All 2 branches missed."> if (downloadItem.isComplete()) {</span>
|
||||
<span class="nc" id="L166"> log.info("Download completed: " + downloadItem.getFullPath());</span>
|
||||
<span class="nc bnc" id="L167" title="All 2 branches missed."> } else if (downloadItem.isCanceled()) {</span>
|
||||
<span class="nc" id="L168"> log.info("Download canceled: " + downloadItem.getFullPath());</span>
|
||||
}
|
||||
<span class="nc" id="L170"> }</span>
|
||||
});
|
||||
<span class="nc" id="L172"> }</span>
|
||||
|
||||
private ConsoleProgressHandler createProgressHandler() {
|
||||
<span class="nc" id="L175"> return new ConsoleProgressHandler() {</span>
|
||||
@Override
|
||||
public void handleProgress(EnumProgress state, float percent) {
|
||||
<span class="nc" id="L178"> Objects.requireNonNull(state, "state cannot be null");</span>
|
||||
<span class="nc" id="L179"> SwingUtilities.invokeLater(</span>
|
||||
() -> {
|
||||
<span class="nc bnc" id="L181" title="All 2 branches missed."> if (loadingWindow != null) {</span>
|
||||
<span class="nc bnc" id="L182" title="All 6 branches missed."> switch (state) {</span>
|
||||
case LOCATING:
|
||||
<span class="nc" id="L184"> loadingWindow.setStatus("Locating Files...");</span>
|
||||
<span class="nc" id="L185"> loadingWindow.setProgress(0);</span>
|
||||
<span class="nc" id="L186"> break;</span>
|
||||
case DOWNLOADING:
|
||||
<span class="nc bnc" id="L188" title="All 2 branches missed."> if (percent >= 0) {</span>
|
||||
<span class="nc" id="L189"> loadingWindow.setStatus(</span>
|
||||
<span class="nc" id="L190"> String.format(</span>
|
||||
"Downloading additional files: %.0f%%",
|
||||
<span class="nc" id="L192"> percent));</span>
|
||||
<span class="nc" id="L193"> loadingWindow.setProgress((int) percent);</span>
|
||||
}
|
||||
break;
|
||||
case EXTRACTING:
|
||||
<span class="nc" id="L197"> loadingWindow.setStatus("Extracting files...");</span>
|
||||
<span class="nc" id="L198"> loadingWindow.setProgress(60);</span>
|
||||
<span class="nc" id="L199"> break;</span>
|
||||
case INITIALIZING:
|
||||
<span class="nc" id="L201"> loadingWindow.setStatus("Initializing UI...");</span>
|
||||
<span class="nc" id="L202"> loadingWindow.setProgress(80);</span>
|
||||
<span class="nc" id="L203"> break;</span>
|
||||
case INITIALIZED:
|
||||
<span class="nc" id="L205"> loadingWindow.setStatus("Finalising startup...");</span>
|
||||
<span class="nc" id="L206"> loadingWindow.setProgress(90);</span>
|
||||
break;
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L210"> });</span>
|
||||
<span class="nc" id="L211"> }</span>
|
||||
};
|
||||
}
|
||||
|
||||
private void setupMainFrame() {
|
||||
<span class="nc" id="L216"> frame = new JFrame("Stirling-PDF");</span>
|
||||
<span class="nc" id="L217"> frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);</span>
|
||||
<span class="nc" id="L218"> frame.setUndecorated(true);</span>
|
||||
<span class="nc" id="L219"> frame.setOpacity(0.0f);</span>
|
||||
|
||||
<span class="nc" id="L221"> JPanel contentPane = new JPanel(new BorderLayout());</span>
|
||||
<span class="nc" id="L222"> contentPane.setDoubleBuffered(true);</span>
|
||||
<span class="nc" id="L223"> contentPane.add(browser.getUIComponent(), BorderLayout.CENTER);</span>
|
||||
<span class="nc" id="L224"> frame.setContentPane(contentPane);</span>
|
||||
|
||||
<span class="nc" id="L226"> frame.addWindowListener(</span>
|
||||
<span class="nc" id="L227"> new java.awt.event.WindowAdapter() {</span>
|
||||
@Override
|
||||
public void windowClosing(java.awt.event.WindowEvent windowEvent) {
|
||||
<span class="nc" id="L230"> cleanup();</span>
|
||||
<span class="nc" id="L231"> System.exit(0);</span>
|
||||
<span class="nc" id="L232"> }</span>
|
||||
});
|
||||
|
||||
<span class="nc" id="L235"> frame.setSize(UIScaling.scaleWidth(1280), UIScaling.scaleHeight(800));</span>
|
||||
<span class="nc" id="L236"> frame.setLocationRelativeTo(null);</span>
|
||||
|
||||
<span class="nc" id="L238"> loadIcon();</span>
|
||||
<span class="nc" id="L239"> }</span>
|
||||
|
||||
private void setupLoadHandler() {
|
||||
<span class="nc" id="L242"> final long initStartTime = System.currentTimeMillis();</span>
|
||||
<span class="nc" id="L243"> log.info("Setting up load handler at: {}", initStartTime);</span>
|
||||
|
||||
<span class="nc" id="L245"> client.addLoadHandler(</span>
|
||||
<span class="nc" id="L246"> new CefLoadHandlerAdapter() {</span>
|
||||
@Override
|
||||
public void onLoadingStateChange(
|
||||
CefBrowser browser,
|
||||
boolean isLoading,
|
||||
boolean canGoBack,
|
||||
boolean canGoForward) {
|
||||
<span class="nc" id="L253"> log.debug(</span>
|
||||
"Loading state change - isLoading: {}, canGoBack: {}, canGoForward:"
|
||||
+ " {}, browserInitialized: {}, Time elapsed: {}ms",
|
||||
<span class="nc" id="L256"> isLoading,</span>
|
||||
<span class="nc" id="L257"> canGoBack,</span>
|
||||
<span class="nc" id="L258"> canGoForward,</span>
|
||||
<span class="nc" id="L259"> browserInitialized,</span>
|
||||
<span class="nc" id="L260"> System.currentTimeMillis() - initStartTime);</span>
|
||||
|
||||
<span class="nc bnc" id="L262" title="All 4 branches missed."> if (!isLoading && !browserInitialized) {</span>
|
||||
<span class="nc" id="L263"> log.info(</span>
|
||||
"Browser finished loading, preparing to initialize UI"
|
||||
+ " components");
|
||||
<span class="nc" id="L266"> browserInitialized = true;</span>
|
||||
<span class="nc" id="L267"> SwingUtilities.invokeLater(</span>
|
||||
() -> {
|
||||
try {
|
||||
<span class="nc bnc" id="L270" title="All 2 branches missed."> if (loadingWindow != null) {</span>
|
||||
<span class="nc" id="L271"> log.info("Starting UI initialization sequence");</span>
|
||||
|
||||
// Close loading window first
|
||||
<span class="nc" id="L274"> loadingWindow.setVisible(false);</span>
|
||||
<span class="nc" id="L275"> loadingWindow.dispose();</span>
|
||||
<span class="nc" id="L276"> loadingWindow = null;</span>
|
||||
<span class="nc" id="L277"> log.info("Loading window disposed");</span>
|
||||
|
||||
// Then setup the main frame
|
||||
<span class="nc" id="L280"> frame.setVisible(false);</span>
|
||||
<span class="nc" id="L281"> frame.dispose();</span>
|
||||
<span class="nc" id="L282"> frame.setOpacity(1.0f);</span>
|
||||
<span class="nc" id="L283"> frame.setUndecorated(false);</span>
|
||||
<span class="nc" id="L284"> frame.pack();</span>
|
||||
<span class="nc" id="L285"> frame.setSize(</span>
|
||||
<span class="nc" id="L286"> UIScaling.scaleWidth(1280),</span>
|
||||
<span class="nc" id="L287"> UIScaling.scaleHeight(800));</span>
|
||||
<span class="nc" id="L288"> frame.setLocationRelativeTo(null);</span>
|
||||
<span class="nc" id="L289"> log.debug("Frame reconfigured");</span>
|
||||
|
||||
// Show the main frame
|
||||
<span class="nc" id="L292"> frame.setVisible(true);</span>
|
||||
<span class="nc" id="L293"> frame.requestFocus();</span>
|
||||
<span class="nc" id="L294"> frame.toFront();</span>
|
||||
<span class="nc" id="L295"> log.info("Main frame displayed and focused");</span>
|
||||
|
||||
// Focus the browser component
|
||||
<span class="nc" id="L298"> Timer focusTimer =</span>
|
||||
new Timer(
|
||||
100,
|
||||
e -> {
|
||||
try {
|
||||
<span class="nc" id="L303"> browser.getUIComponent()</span>
|
||||
<span class="nc" id="L304"> .requestFocus();</span>
|
||||
<span class="nc" id="L305"> log.info(</span>
|
||||
"Browser component"
|
||||
+ " focused");
|
||||
<span class="nc" id="L308"> } catch (Exception ex) {</span>
|
||||
<span class="nc" id="L309"> log.error(</span>
|
||||
"Error focusing"
|
||||
+ " browser",
|
||||
ex);
|
||||
<span class="nc" id="L313"> }</span>
|
||||
<span class="nc" id="L314"> });</span>
|
||||
<span class="nc" id="L315"> focusTimer.setRepeats(false);</span>
|
||||
<span class="nc" id="L316"> focusTimer.start();</span>
|
||||
}
|
||||
<span class="nc" id="L318"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L319"> log.error("Error during UI initialization", e);</span>
|
||||
// Attempt cleanup on error
|
||||
<span class="nc bnc" id="L321" title="All 2 branches missed."> if (loadingWindow != null) {</span>
|
||||
<span class="nc" id="L322"> loadingWindow.dispose();</span>
|
||||
<span class="nc" id="L323"> loadingWindow = null;</span>
|
||||
}
|
||||
<span class="nc bnc" id="L325" title="All 2 branches missed."> if (frame != null) {</span>
|
||||
<span class="nc" id="L326"> frame.setVisible(true);</span>
|
||||
<span class="nc" id="L327"> frame.requestFocus();</span>
|
||||
}
|
||||
<span class="nc" id="L329"> }</span>
|
||||
<span class="nc" id="L330"> });</span>
|
||||
}
|
||||
<span class="nc" id="L332"> }</span>
|
||||
});
|
||||
<span class="nc" id="L334"> }</span>
|
||||
|
||||
private void setupTrayIcon(Image icon) {
|
||||
<span class="nc bnc" id="L337" title="All 2 branches missed."> if (!SystemTray.isSupported()) {</span>
|
||||
<span class="nc" id="L338"> log.warn("System tray is not supported");</span>
|
||||
<span class="nc" id="L339"> return;</span>
|
||||
}
|
||||
|
||||
try {
|
||||
<span class="nc" id="L343"> systemTray = SystemTray.getSystemTray();</span>
|
||||
|
||||
// Create popup menu
|
||||
<span class="nc" id="L346"> PopupMenu popup = new PopupMenu();</span>
|
||||
|
||||
// Create menu items
|
||||
<span class="nc" id="L349"> MenuItem showItem = new MenuItem("Show");</span>
|
||||
<span class="nc" id="L350"> showItem.addActionListener(</span>
|
||||
e -> {
|
||||
<span class="nc" id="L352"> frame.setVisible(true);</span>
|
||||
<span class="nc" id="L353"> frame.setState(Frame.NORMAL);</span>
|
||||
<span class="nc" id="L354"> });</span>
|
||||
|
||||
<span class="nc" id="L356"> MenuItem exitItem = new MenuItem("Exit");</span>
|
||||
<span class="nc" id="L357"> exitItem.addActionListener(</span>
|
||||
e -> {
|
||||
<span class="nc" id="L359"> cleanup();</span>
|
||||
<span class="nc" id="L360"> System.exit(0);</span>
|
||||
<span class="nc" id="L361"> });</span>
|
||||
|
||||
// Add menu items to popup menu
|
||||
<span class="nc" id="L364"> popup.add(showItem);</span>
|
||||
<span class="nc" id="L365"> popup.addSeparator();</span>
|
||||
<span class="nc" id="L366"> popup.add(exitItem);</span>
|
||||
|
||||
// Create tray icon
|
||||
<span class="nc" id="L369"> trayIcon = new TrayIcon(icon, "Stirling-PDF", popup);</span>
|
||||
<span class="nc" id="L370"> trayIcon.setImageAutoSize(true);</span>
|
||||
|
||||
// Add double-click behavior
|
||||
<span class="nc" id="L373"> trayIcon.addActionListener(</span>
|
||||
e -> {
|
||||
<span class="nc" id="L375"> frame.setVisible(true);</span>
|
||||
<span class="nc" id="L376"> frame.setState(Frame.NORMAL);</span>
|
||||
<span class="nc" id="L377"> });</span>
|
||||
|
||||
// Add tray icon to system tray
|
||||
<span class="nc" id="L380"> systemTray.add(trayIcon);</span>
|
||||
|
||||
// Modify frame behavior to minimize to tray
|
||||
<span class="nc" id="L383"> frame.addWindowStateListener(</span>
|
||||
<span class="nc" id="L384"> new WindowStateListener() {</span>
|
||||
public void windowStateChanged(WindowEvent e) {
|
||||
<span class="nc bnc" id="L386" title="All 2 branches missed."> if (e.getNewState() == Frame.ICONIFIED) {</span>
|
||||
<span class="nc" id="L387"> frame.setVisible(false);</span>
|
||||
}
|
||||
<span class="nc" id="L389"> }</span>
|
||||
});
|
||||
|
||||
<span class="nc" id="L392"> } catch (AWTException e) {</span>
|
||||
<span class="nc" id="L393"> log.error("Error setting up system tray icon", e);</span>
|
||||
<span class="nc" id="L394"> }</span>
|
||||
<span class="nc" id="L395"> }</span>
|
||||
|
||||
private void loadIcon() {
|
||||
try {
|
||||
<span class="nc" id="L399"> Image icon = null;</span>
|
||||
<span class="nc" id="L400"> String[] iconPaths = {"/static/favicon.ico"};</span>
|
||||
|
||||
<span class="nc bnc" id="L402" title="All 2 branches missed."> for (String path : iconPaths) {</span>
|
||||
<span class="nc bnc" id="L403" title="All 2 branches missed."> if (icon != null) break;</span>
|
||||
try {
|
||||
<span class="nc" id="L405"> try (InputStream is = getClass().getResourceAsStream(path)) {</span>
|
||||
<span class="nc bnc" id="L406" title="All 2 branches missed."> if (is != null) {</span>
|
||||
<span class="nc" id="L407"> icon = ImageIO.read(is);</span>
|
||||
<span class="nc" id="L408"> break;</span>
|
||||
}
|
||||
<span class="nc bnc" id="L410" title="All 2 branches missed."> }</span>
|
||||
<span class="nc" id="L411"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L412"> log.debug("Could not load icon from " + path, e);</span>
|
||||
<span class="nc" id="L413"> }</span>
|
||||
}
|
||||
|
||||
<span class="nc bnc" id="L416" title="All 2 branches missed."> if (icon != null) {</span>
|
||||
<span class="nc" id="L417"> frame.setIconImage(icon);</span>
|
||||
<span class="nc" id="L418"> setupTrayIcon(icon);</span>
|
||||
} else {
|
||||
<span class="nc" id="L420"> log.warn("Could not load icon from any source");</span>
|
||||
}
|
||||
<span class="nc" id="L422"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L423"> log.error("Error loading icon", e);</span>
|
||||
<span class="nc" id="L424"> }</span>
|
||||
<span class="nc" id="L425"> }</span>
|
||||
|
||||
@PreDestroy
|
||||
public void cleanup() {
|
||||
<span class="nc bnc" id="L429" title="All 2 branches missed."> if (browser != null) browser.close(true);</span>
|
||||
<span class="nc bnc" id="L430" title="All 2 branches missed."> if (client != null) client.dispose();</span>
|
||||
<span class="nc bnc" id="L431" title="All 2 branches missed."> if (cefApp != null) cefApp.dispose();</span>
|
||||
<span class="nc bnc" id="L432" title="All 2 branches missed."> if (loadingWindow != null) loadingWindow.dispose();</span>
|
||||
<span class="nc" id="L433"> }</span>
|
||||
|
||||
public static void forceInitializeUI() {
|
||||
try {
|
||||
<span class="nc bnc" id="L437" title="All 2 branches missed."> if (loadingWindow != null) {</span>
|
||||
<span class="nc" id="L438"> log.info("Forcing start of UI initialization sequence");</span>
|
||||
|
||||
// Close loading window first
|
||||
<span class="nc" id="L441"> loadingWindow.setVisible(false);</span>
|
||||
<span class="nc" id="L442"> loadingWindow.dispose();</span>
|
||||
<span class="nc" id="L443"> loadingWindow = null;</span>
|
||||
<span class="nc" id="L444"> log.info("Loading window disposed");</span>
|
||||
|
||||
// Then setup the main frame
|
||||
<span class="nc" id="L447"> frame.setVisible(false);</span>
|
||||
<span class="nc" id="L448"> frame.dispose();</span>
|
||||
<span class="nc" id="L449"> frame.setOpacity(1.0f);</span>
|
||||
<span class="nc" id="L450"> frame.setUndecorated(false);</span>
|
||||
<span class="nc" id="L451"> frame.pack();</span>
|
||||
<span class="nc" id="L452"> frame.setSize(UIScaling.scaleWidth(1280), UIScaling.scaleHeight(800));</span>
|
||||
<span class="nc" id="L453"> frame.setLocationRelativeTo(null);</span>
|
||||
<span class="nc" id="L454"> log.debug("Frame reconfigured");</span>
|
||||
|
||||
// Show the main frame
|
||||
<span class="nc" id="L457"> frame.setVisible(true);</span>
|
||||
<span class="nc" id="L458"> frame.requestFocus();</span>
|
||||
<span class="nc" id="L459"> frame.toFront();</span>
|
||||
<span class="nc" id="L460"> log.info("Main frame displayed and focused");</span>
|
||||
|
||||
// Focus the browser component if available
|
||||
<span class="nc bnc" id="L463" title="All 2 branches missed."> if (browser != null) {</span>
|
||||
<span class="nc" id="L464"> Timer focusTimer =</span>
|
||||
new Timer(
|
||||
100,
|
||||
e -> {
|
||||
try {
|
||||
<span class="nc" id="L469"> browser.getUIComponent().requestFocus();</span>
|
||||
<span class="nc" id="L470"> log.info("Browser component focused");</span>
|
||||
<span class="nc" id="L471"> } catch (Exception ex) {</span>
|
||||
<span class="nc" id="L472"> log.error(</span>
|
||||
"Error focusing browser during force ui"
|
||||
+ " initialization.",
|
||||
ex);
|
||||
<span class="nc" id="L476"> }</span>
|
||||
<span class="nc" id="L477"> });</span>
|
||||
<span class="nc" id="L478"> focusTimer.setRepeats(false);</span>
|
||||
<span class="nc" id="L479"> focusTimer.start();</span>
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L482"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L483"> log.error("Error during Forced UI initialization.", e);</span>
|
||||
// Attempt cleanup on error
|
||||
<span class="nc bnc" id="L485" title="All 2 branches missed."> if (loadingWindow != null) {</span>
|
||||
<span class="nc" id="L486"> loadingWindow.dispose();</span>
|
||||
<span class="nc" id="L487"> loadingWindow = null;</span>
|
||||
}
|
||||
<span class="nc bnc" id="L489" title="All 2 branches missed."> if (frame != null) {</span>
|
||||
<span class="nc" id="L490"> frame.setVisible(true);</span>
|
||||
<span class="nc" id="L491"> frame.setOpacity(1.0f);</span>
|
||||
<span class="nc" id="L492"> frame.setUndecorated(false);</span>
|
||||
<span class="nc" id="L493"> frame.requestFocus();</span>
|
||||
}
|
||||
<span class="nc" id="L495"> }</span>
|
||||
<span class="nc" id="L496"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,352 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>LoadingWindow.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.UI.impl</a> > <span class="el_source">LoadingWindow.java</span></div><h1>LoadingWindow.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.UI.impl;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
|
||||
import io.github.pixee.security.BoundedLineReader;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.utils.UIScaling;
|
||||
|
||||
<span class="nc" id="L20">@Slf4j</span>
|
||||
public class LoadingWindow extends JDialog {
|
||||
private final JProgressBar progressBar;
|
||||
private final JLabel statusLabel;
|
||||
private final JPanel mainPanel;
|
||||
private final JLabel brandLabel;
|
||||
private long startTime;
|
||||
|
||||
private Timer stuckTimer;
|
||||
<span class="nc" id="L29"> private long stuckThreshold = 4000;</span>
|
||||
<span class="nc" id="L30"> private long timeAt90Percent = -1;</span>
|
||||
private volatile Process explorerProcess;
|
||||
<span class="nc" id="L32"> private static final boolean IS_WINDOWS =</span>
|
||||
<span class="nc" id="L33"> System.getProperty("os.name").toLowerCase().contains("win");</span>
|
||||
|
||||
public LoadingWindow(Frame parent, String initialUrl) {
|
||||
<span class="nc" id="L36"> super(parent, "Initializing Stirling-PDF", true);</span>
|
||||
<span class="nc" id="L37"> startTime = System.currentTimeMillis();</span>
|
||||
<span class="nc" id="L38"> log.info("Creating LoadingWindow - initialization started at: {}", startTime);</span>
|
||||
|
||||
// Initialize components
|
||||
<span class="nc" id="L41"> mainPanel = new JPanel();</span>
|
||||
<span class="nc" id="L42"> mainPanel.setBackground(Color.WHITE);</span>
|
||||
<span class="nc" id="L43"> mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 30, 20, 30));</span>
|
||||
<span class="nc" id="L44"> mainPanel.setLayout(new GridBagLayout());</span>
|
||||
<span class="nc" id="L45"> GridBagConstraints gbc = new GridBagConstraints();</span>
|
||||
|
||||
// Configure GridBagConstraints
|
||||
<span class="nc" id="L48"> gbc.gridwidth = GridBagConstraints.REMAINDER;</span>
|
||||
<span class="nc" id="L49"> gbc.fill = GridBagConstraints.HORIZONTAL;</span>
|
||||
<span class="nc" id="L50"> gbc.insets = new Insets(5, 5, 5, 5);</span>
|
||||
<span class="nc" id="L51"> gbc.weightx = 1.0;</span>
|
||||
<span class="nc" id="L52"> gbc.weighty = 0.0;</span>
|
||||
|
||||
// Add icon
|
||||
try {
|
||||
<span class="nc" id="L56"> try (InputStream is = getClass().getResourceAsStream("/static/favicon.ico")) {</span>
|
||||
<span class="nc bnc" id="L57" title="All 2 branches missed."> if (is != null) {</span>
|
||||
<span class="nc" id="L58"> Image img = ImageIO.read(is);</span>
|
||||
<span class="nc bnc" id="L59" title="All 2 branches missed."> if (img != null) {</span>
|
||||
<span class="nc" id="L60"> Image scaledImg = UIScaling.scaleIcon(img, 48, 48);</span>
|
||||
<span class="nc" id="L61"> JLabel iconLabel = new JLabel(new ImageIcon(scaledImg));</span>
|
||||
<span class="nc" id="L62"> iconLabel.setHorizontalAlignment(SwingConstants.CENTER);</span>
|
||||
<span class="nc" id="L63"> gbc.gridy = 0;</span>
|
||||
<span class="nc" id="L64"> mainPanel.add(iconLabel, gbc);</span>
|
||||
<span class="nc" id="L65"> log.info("Icon loaded and scaled successfully");</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L69"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L70"> log.error("Failed to load icon", e);</span>
|
||||
<span class="nc" id="L71"> }</span>
|
||||
|
||||
// URL Label with explicit size
|
||||
<span class="nc" id="L74"> brandLabel = new JLabel(initialUrl);</span>
|
||||
<span class="nc" id="L75"> brandLabel.setHorizontalAlignment(SwingConstants.CENTER);</span>
|
||||
<span class="nc" id="L76"> brandLabel.setPreferredSize(new Dimension(300, 25));</span>
|
||||
<span class="nc" id="L77"> brandLabel.setText("Stirling-PDF");</span>
|
||||
<span class="nc" id="L78"> gbc.gridy = 1;</span>
|
||||
<span class="nc" id="L79"> mainPanel.add(brandLabel, gbc);</span>
|
||||
|
||||
// Status label with explicit size
|
||||
<span class="nc" id="L82"> statusLabel = new JLabel("Initializing...");</span>
|
||||
<span class="nc" id="L83"> statusLabel.setHorizontalAlignment(SwingConstants.CENTER);</span>
|
||||
<span class="nc" id="L84"> statusLabel.setPreferredSize(new Dimension(300, 25));</span>
|
||||
<span class="nc" id="L85"> gbc.gridy = 2;</span>
|
||||
<span class="nc" id="L86"> mainPanel.add(statusLabel, gbc);</span>
|
||||
|
||||
// Progress bar with explicit size
|
||||
<span class="nc" id="L89"> progressBar = new JProgressBar(0, 100);</span>
|
||||
<span class="nc" id="L90"> progressBar.setStringPainted(true);</span>
|
||||
<span class="nc" id="L91"> progressBar.setPreferredSize(new Dimension(300, 25));</span>
|
||||
<span class="nc" id="L92"> gbc.gridy = 3;</span>
|
||||
<span class="nc" id="L93"> mainPanel.add(progressBar, gbc);</span>
|
||||
|
||||
// Set dialog properties
|
||||
<span class="nc" id="L96"> setContentPane(mainPanel);</span>
|
||||
<span class="nc" id="L97"> setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);</span>
|
||||
<span class="nc" id="L98"> setResizable(false);</span>
|
||||
<span class="nc" id="L99"> setUndecorated(false);</span>
|
||||
|
||||
// Set size and position
|
||||
<span class="nc" id="L102"> setSize(UIScaling.scaleWidth(400), UIScaling.scaleHeight(200));</span>
|
||||
|
||||
<span class="nc" id="L104"> setLocationRelativeTo(parent);</span>
|
||||
<span class="nc" id="L105"> setAlwaysOnTop(true);</span>
|
||||
<span class="nc" id="L106"> setProgress(0);</span>
|
||||
<span class="nc" id="L107"> setStatus("Starting...");</span>
|
||||
|
||||
<span class="nc" id="L109"> log.info(</span>
|
||||
"LoadingWindow initialization completed in {}ms",
|
||||
<span class="nc" id="L111"> System.currentTimeMillis() - startTime);</span>
|
||||
<span class="nc" id="L112"> }</span>
|
||||
|
||||
private void checkAndRefreshExplorer() {
|
||||
<span class="nc bnc" id="L115" title="All 2 branches missed."> if (!IS_WINDOWS) {</span>
|
||||
<span class="nc" id="L116"> return;</span>
|
||||
}
|
||||
<span class="nc bnc" id="L118" title="All 2 branches missed."> if (timeAt90Percent == -1) {</span>
|
||||
<span class="nc" id="L119"> timeAt90Percent = System.currentTimeMillis();</span>
|
||||
<span class="nc" id="L120"> stuckTimer =</span>
|
||||
new Timer(
|
||||
1000,
|
||||
e -> {
|
||||
<span class="nc" id="L124"> long currentTime = System.currentTimeMillis();</span>
|
||||
<span class="nc bnc" id="L125" title="All 2 branches missed."> if (currentTime - timeAt90Percent > stuckThreshold) {</span>
|
||||
try {
|
||||
<span class="nc" id="L127"> log.debug(</span>
|
||||
"Attempting Windows explorer refresh due to 90% stuck state");
|
||||
<span class="nc" id="L129"> String currentDir = System.getProperty("user.dir");</span>
|
||||
|
||||
// Store current explorer PIDs before we start new one
|
||||
<span class="nc" id="L132"> Set<String> existingPids = new HashSet<>();</span>
|
||||
<span class="nc" id="L133"> ProcessBuilder listExplorer =</span>
|
||||
new ProcessBuilder(
|
||||
"cmd",
|
||||
"/c",
|
||||
"wmic",
|
||||
"process",
|
||||
"where",
|
||||
"name='explorer.exe'",
|
||||
"get",
|
||||
"ProcessId",
|
||||
"/format:csv");
|
||||
<span class="nc" id="L144"> Process process = listExplorer.start();</span>
|
||||
<span class="nc" id="L145"> BufferedReader reader =</span>
|
||||
new BufferedReader(
|
||||
new InputStreamReader(
|
||||
<span class="nc" id="L148"> process.getInputStream()));</span>
|
||||
String line;
|
||||
<span class="nc" id="L150"> while ((line =</span>
|
||||
<span class="nc bnc" id="L151" title="All 2 branches missed."> BoundedLineReader.readLine(</span>
|
||||
reader, 5_000_000))
|
||||
!= null) {
|
||||
<span class="nc bnc" id="L154" title="All 2 branches missed."> if (line.matches(".*\\d+.*")) { // Contains numbers</span>
|
||||
<span class="nc" id="L155"> String[] parts = line.trim().split(",");</span>
|
||||
<span class="nc bnc" id="L156" title="All 2 branches missed."> if (parts.length >= 2) {</span>
|
||||
<span class="nc" id="L157"> existingPids.add(</span>
|
||||
<span class="nc" id="L158"> parts[parts.length - 1].trim());</span>
|
||||
}
|
||||
<span class="nc" id="L160"> }</span>
|
||||
}
|
||||
<span class="nc" id="L162"> process.waitFor(2, TimeUnit.SECONDS);</span>
|
||||
|
||||
// Start new explorer
|
||||
<span class="nc" id="L165"> ProcessBuilder pb =</span>
|
||||
new ProcessBuilder(
|
||||
"cmd",
|
||||
"/c",
|
||||
"start",
|
||||
"/min",
|
||||
"/b",
|
||||
"explorer.exe",
|
||||
currentDir);
|
||||
<span class="nc" id="L174"> pb.redirectErrorStream(true);</span>
|
||||
<span class="nc" id="L175"> explorerProcess = pb.start();</span>
|
||||
|
||||
// Schedule cleanup
|
||||
<span class="nc" id="L178"> Timer cleanupTimer =</span>
|
||||
new Timer(
|
||||
2000,
|
||||
cleanup -> {
|
||||
try {
|
||||
// Find new explorer processes
|
||||
<span class="nc" id="L184"> ProcessBuilder findNewExplorer =</span>
|
||||
new ProcessBuilder(
|
||||
"cmd",
|
||||
"/c",
|
||||
"wmic",
|
||||
"process",
|
||||
"where",
|
||||
"name='explorer.exe'",
|
||||
"get",
|
||||
"ProcessId",
|
||||
"/format:csv");
|
||||
<span class="nc" id="L195"> Process newProcess =</span>
|
||||
<span class="nc" id="L196"> findNewExplorer.start();</span>
|
||||
<span class="nc" id="L197"> BufferedReader newReader =</span>
|
||||
new BufferedReader(
|
||||
new InputStreamReader(
|
||||
newProcess
|
||||
<span class="nc" id="L201"> .getInputStream()));</span>
|
||||
String newLine;
|
||||
<span class="nc" id="L203"> while ((newLine =</span>
|
||||
BoundedLineReader
|
||||
<span class="nc bnc" id="L205" title="All 2 branches missed."> .readLine(</span>
|
||||
newReader,
|
||||
5_000_000))
|
||||
!= null) {
|
||||
<span class="nc bnc" id="L209" title="All 2 branches missed."> if (newLine.matches(</span>
|
||||
".*\\d+.*")) {
|
||||
<span class="nc" id="L211"> String[] parts =</span>
|
||||
<span class="nc" id="L212"> newLine.trim()</span>
|
||||
<span class="nc" id="L213"> .split(",");</span>
|
||||
<span class="nc bnc" id="L214" title="All 2 branches missed."> if (parts.length >= 2) {</span>
|
||||
<span class="nc" id="L215"> String pid =</span>
|
||||
parts[
|
||||
parts.length
|
||||
- 1]
|
||||
<span class="nc" id="L219"> .trim();</span>
|
||||
<span class="nc" id="L220"> if (!existingPids</span>
|
||||
<span class="nc bnc" id="L221" title="All 2 branches missed."> .contains(</span>
|
||||
pid)) {
|
||||
<span class="nc" id="L223"> log.debug(</span>
|
||||
"Found new explorer.exe with PID: "
|
||||
+ pid);
|
||||
ProcessBuilder
|
||||
<span class="nc" id="L227"> killProcess =</span>
|
||||
new ProcessBuilder(
|
||||
"taskkill",
|
||||
"/PID",
|
||||
pid,
|
||||
"/F");
|
||||
<span class="nc" id="L233"> killProcess</span>
|
||||
<span class="nc" id="L234"> .redirectErrorStream(</span>
|
||||
true);
|
||||
<span class="nc" id="L236"> Process killResult =</span>
|
||||
killProcess
|
||||
<span class="nc" id="L238"> .start();</span>
|
||||
<span class="nc" id="L239"> killResult.waitFor(</span>
|
||||
2,
|
||||
TimeUnit
|
||||
.SECONDS);
|
||||
<span class="nc" id="L243"> log.debug(</span>
|
||||
"Explorer process terminated: "
|
||||
+ pid);
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L248"> }</span>
|
||||
}
|
||||
<span class="nc" id="L250"> newProcess.waitFor(</span>
|
||||
2, TimeUnit.SECONDS);
|
||||
<span class="nc" id="L252"> } catch (Exception ex) {</span>
|
||||
<span class="nc" id="L253"> log.error(</span>
|
||||
"Error cleaning up Windows explorer process",
|
||||
ex);
|
||||
<span class="nc" id="L256"> }</span>
|
||||
<span class="nc" id="L257"> });</span>
|
||||
<span class="nc" id="L258"> cleanupTimer.setRepeats(false);</span>
|
||||
<span class="nc" id="L259"> cleanupTimer.start();</span>
|
||||
<span class="nc" id="L260"> stuckTimer.stop();</span>
|
||||
<span class="nc" id="L261"> } catch (Exception ex) {</span>
|
||||
<span class="nc" id="L262"> log.error("Error refreshing Windows explorer", ex);</span>
|
||||
<span class="nc" id="L263"> }</span>
|
||||
}
|
||||
<span class="nc" id="L265"> });</span>
|
||||
<span class="nc" id="L266"> stuckTimer.setRepeats(true);</span>
|
||||
<span class="nc" id="L267"> stuckTimer.start();</span>
|
||||
}
|
||||
<span class="nc" id="L269"> }</span>
|
||||
|
||||
public void setProgress(final int progress) {
|
||||
<span class="nc" id="L272"> SwingUtilities.invokeLater(</span>
|
||||
() -> {
|
||||
try {
|
||||
<span class="nc" id="L275"> int validProgress = Math.min(Math.max(progress, 0), 100);</span>
|
||||
<span class="nc" id="L276"> log.info(</span>
|
||||
"Setting progress to {}% at {}ms since start",
|
||||
<span class="nc" id="L278"> validProgress, System.currentTimeMillis() - startTime);</span>
|
||||
|
||||
// Log additional details when near 90%
|
||||
<span class="nc bnc" id="L281" title="All 4 branches missed."> if (validProgress >= 85 && validProgress <= 95) {</span>
|
||||
<span class="nc" id="L282"> log.info(</span>
|
||||
"Near 90% progress - Current status: {}, Window visible: {}, "
|
||||
+ "Progress bar responding: {}, Memory usage: {}MB",
|
||||
<span class="nc" id="L285"> statusLabel.getText(),</span>
|
||||
<span class="nc" id="L286"> isVisible(),</span>
|
||||
<span class="nc" id="L287"> progressBar.isEnabled(),</span>
|
||||
<span class="nc" id="L288"> Runtime.getRuntime().totalMemory() / (1024 * 1024));</span>
|
||||
|
||||
// Add thread state logging
|
||||
<span class="nc" id="L291"> Thread currentThread = Thread.currentThread();</span>
|
||||
<span class="nc" id="L292"> log.info(</span>
|
||||
"Current thread state - Name: {}, State: {}, Priority: {}",
|
||||
<span class="nc" id="L294"> currentThread.getName(),</span>
|
||||
<span class="nc" id="L295"> currentThread.getState(),</span>
|
||||
<span class="nc" id="L296"> currentThread.getPriority());</span>
|
||||
|
||||
<span class="nc bnc" id="L298" title="All 4 branches missed."> if (validProgress >= 90 && validProgress < 95) {</span>
|
||||
<span class="nc" id="L299"> checkAndRefreshExplorer();</span>
|
||||
} else {
|
||||
// Reset the timer if we move past 95%
|
||||
<span class="nc bnc" id="L302" title="All 2 branches missed."> if (validProgress >= 95) {</span>
|
||||
<span class="nc bnc" id="L303" title="All 2 branches missed."> if (stuckTimer != null) {</span>
|
||||
<span class="nc" id="L304"> stuckTimer.stop();</span>
|
||||
}
|
||||
<span class="nc" id="L306"> timeAt90Percent = -1;</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<span class="nc" id="L311"> progressBar.setValue(validProgress);</span>
|
||||
<span class="nc" id="L312"> progressBar.setString(validProgress + "%");</span>
|
||||
<span class="nc" id="L313"> mainPanel.revalidate();</span>
|
||||
<span class="nc" id="L314"> mainPanel.repaint();</span>
|
||||
<span class="nc" id="L315"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L316"> log.error("Error updating progress to " + progress, e);</span>
|
||||
<span class="nc" id="L317"> }</span>
|
||||
<span class="nc" id="L318"> });</span>
|
||||
<span class="nc" id="L319"> }</span>
|
||||
|
||||
public void setStatus(final String status) {
|
||||
<span class="nc" id="L322"> log.info(</span>
|
||||
"Status update at {}ms - Setting status to: {}",
|
||||
<span class="nc" id="L324"> System.currentTimeMillis() - startTime,</span>
|
||||
status);
|
||||
|
||||
<span class="nc" id="L327"> SwingUtilities.invokeLater(</span>
|
||||
() -> {
|
||||
try {
|
||||
<span class="nc bnc" id="L330" title="All 2 branches missed."> String validStatus = status != null ? status : "";</span>
|
||||
<span class="nc" id="L331"> statusLabel.setText(validStatus);</span>
|
||||
|
||||
// Log UI state when status changes
|
||||
<span class="nc" id="L334"> log.info(</span>
|
||||
"UI State - Window visible: {}, Progress: {}%, Status: {}",
|
||||
<span class="nc" id="L336"> isVisible(), progressBar.getValue(), validStatus);</span>
|
||||
|
||||
<span class="nc" id="L338"> mainPanel.revalidate();</span>
|
||||
<span class="nc" id="L339"> mainPanel.repaint();</span>
|
||||
<span class="nc" id="L340"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L341"> log.error("Error updating status to: " + status, e);</span>
|
||||
<span class="nc" id="L342"> }</span>
|
||||
<span class="nc" id="L343"> });</span>
|
||||
<span class="nc" id="L344"> }</span>
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
<span class="nc" id="L348"> log.info("LoadingWindow disposing after {}ms", System.currentTimeMillis() - startTime);</span>
|
||||
<span class="nc" id="L349"> super.dispose();</span>
|
||||
<span class="nc" id="L350"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>stirling.software.SPDF.UI.impl</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb', 'coveragetable'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="index.html" class="el_class">Classes</a><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <span class="el_package">stirling.software.SPDF.UI.impl</span></div><h1>stirling.software.SPDF.UI.impl</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td><td class="sortable ctr1" id="l" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="m" onclick="toggleSort(this)">Classes</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">1,577 of 1,577</td><td class="ctr2">0%</td><td class="bar">96 of 96</td><td class="ctr2">0%</td><td class="ctr1">96</td><td class="ctr2">96</td><td class="ctr1">429</td><td class="ctr2">429</td><td class="ctr1">46</td><td class="ctr2">46</td><td class="ctr1">8</td><td class="ctr2">8</td></tr></tfoot><tbody><tr><td id="a0"><a href="DesktopBrowser.java.html" class="el_source">DesktopBrowser.java</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="791" alt="791"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="58" alt="58"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">67</td><td class="ctr2" id="g0">67</td><td class="ctr1" id="h0">256</td><td class="ctr2" id="i0">256</td><td class="ctr1" id="j0">36</td><td class="ctr2" id="k0">36</td><td class="ctr1" id="l0">7</td><td class="ctr2" id="m0">7</td></tr><tr><td id="a1"><a href="LoadingWindow.java.html" class="el_source">LoadingWindow.java</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="119" height="10" title="786" alt="786"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"><img src="../jacoco-resources/redbar.gif" width="78" height="10" title="38" alt="38"/></td><td class="ctr2" id="e1">0%</td><td class="ctr1" id="f1">29</td><td class="ctr2" id="g1">29</td><td class="ctr1" id="h1">173</td><td class="ctr2" id="i1">173</td><td class="ctr1" id="j1">10</td><td class="ctr2" id="k1">10</td><td class="ctr1" id="l1">1</td><td class="ctr2" id="m1">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ShowAdminInterface</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.config.interfaces</a> > <span class="el_class">ShowAdminInterface</span></div><h1>ShowAdminInterface</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">2 of 2</td><td class="ctr2">0%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">1</td><td class="ctr2">1</td><td class="ctr1">1</td><td class="ctr2">1</td><td class="ctr1">1</td><td class="ctr2">1</td></tr></tfoot><tbody><tr><td id="a0"><a href="ShowAdminInterface.java.html#L5" class="el_method">getShowUpdateOnlyAdmins()</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">1</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h0">1</td><td class="ctr2" id="i0">1</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ShowAdminInterface.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.interfaces</a> > <span class="el_source">ShowAdminInterface.java</span></div><h1>ShowAdminInterface.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.interfaces;
|
||||
|
||||
public interface ShowAdminInterface {
|
||||
default boolean getShowUpdateOnlyAdmins() {
|
||||
<span class="nc" id="L5"> return true;</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>stirling.software.SPDF.config.interfaces</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb', 'coveragetable'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="index.source.html" class="el_source">Source Files</a><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <span class="el_package">stirling.software.SPDF.config.interfaces</span></div><h1>stirling.software.SPDF.config.interfaces</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td><td class="sortable ctr1" id="l" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="m" onclick="toggleSort(this)">Classes</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">2 of 2</td><td class="ctr2">0%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">1</td><td class="ctr2">1</td><td class="ctr1">1</td><td class="ctr2">1</td><td class="ctr1">1</td><td class="ctr2">1</td><td class="ctr1">1</td><td class="ctr2">1</td></tr></tfoot><tbody><tr><td id="a0"><a href="ShowAdminInterface.html" class="el_class">ShowAdminInterface</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">1</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h0">1</td><td class="ctr2" id="i0">1</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td><td class="ctr1" id="l0">1</td><td class="ctr2" id="m0">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>stirling.software.SPDF.config.interfaces</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb', 'coveragetable'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="index.html" class="el_class">Classes</a><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <span class="el_package">stirling.software.SPDF.config.interfaces</span></div><h1>stirling.software.SPDF.config.interfaces</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td><td class="sortable ctr1" id="l" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="m" onclick="toggleSort(this)">Classes</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">2 of 2</td><td class="ctr2">0%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">1</td><td class="ctr2">1</td><td class="ctr1">1</td><td class="ctr2">1</td><td class="ctr1">1</td><td class="ctr2">1</td><td class="ctr1">1</td><td class="ctr2">1</td></tr></tfoot><tbody><tr><td id="a0"><a href="ShowAdminInterface.java.html" class="el_source">ShowAdminInterface.java</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">1</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h0">1</td><td class="ctr2" id="i0">1</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td><td class="ctr1" id="l0">1</td><td class="ctr2" id="m0">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DatabaseConfig.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.database</a> > <span class="el_source">DatabaseConfig.java</span></div><h1>DatabaseConfig.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.database;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.exception.UnsupportedProviderException;
|
||||
|
||||
<span class="nc" id="L17">@Slf4j</span>
|
||||
@Getter
|
||||
@Configuration
|
||||
public class DatabaseConfig {
|
||||
|
||||
public final String DATASOURCE_DEFAULT_URL;
|
||||
|
||||
public static final String DATASOURCE_URL_TEMPLATE = "jdbc:%s://%s:%4d/%s";
|
||||
public static final String DEFAULT_DRIVER = "org.h2.Driver";
|
||||
public static final String DEFAULT_USERNAME = "sa";
|
||||
public static final String POSTGRES_DRIVER = "org.postgresql.Driver";
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final boolean runningProOrHigher;
|
||||
|
||||
public DatabaseConfig(
|
||||
ApplicationProperties applicationProperties,
|
||||
<span class="nc" id="L34"> @Qualifier("runningProOrHigher") boolean runningProOrHigher) {</span>
|
||||
<span class="nc" id="L35"> DATASOURCE_DEFAULT_URL =</span>
|
||||
"jdbc:h2:file:"
|
||||
<span class="nc" id="L37"> + InstallationPathConfig.getConfigPath()</span>
|
||||
+ "stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE";
|
||||
<span class="nc" id="L39"> log.debug("Database URL: {}", DATASOURCE_DEFAULT_URL);</span>
|
||||
<span class="nc" id="L40"> this.applicationProperties = applicationProperties;</span>
|
||||
<span class="nc" id="L41"> this.runningProOrHigher = runningProOrHigher;</span>
|
||||
<span class="nc" id="L42"> }</span>
|
||||
|
||||
/**
|
||||
* Creates the <code>DataSource</code> for the connection to the DB. If <code>useDefault</code>
|
||||
* is set to <code>true</code>, it will use the default H2 DB. If it is set to <code>false
|
||||
* </code>, it will use the user's custom configuration set in the settings.yml.
|
||||
*
|
||||
* @return a <code>DataSource</code> using the configuration settings in the settings.yml
|
||||
* @throws UnsupportedProviderException if the type of database selected is not supported
|
||||
*/
|
||||
@Bean
|
||||
@Qualifier("dataSource")
|
||||
public DataSource dataSource() throws UnsupportedProviderException {
|
||||
<span class="nc" id="L55"> DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();</span>
|
||||
|
||||
<span class="nc bnc" id="L57" title="All 2 branches missed."> if (!runningProOrHigher) {</span>
|
||||
<span class="nc" id="L58"> return useDefaultDataSource(dataSourceBuilder);</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L61"> ApplicationProperties.System system = applicationProperties.getSystem();</span>
|
||||
<span class="nc" id="L62"> ApplicationProperties.Datasource datasource = system.getDatasource();</span>
|
||||
|
||||
<span class="nc bnc" id="L64" title="All 2 branches missed."> if (!datasource.isEnableCustomDatabase()) {</span>
|
||||
<span class="nc" id="L65"> return useDefaultDataSource(dataSourceBuilder);</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L68"> log.info("Using custom database configuration");</span>
|
||||
|
||||
<span class="nc bnc" id="L70" title="All 2 branches missed."> if (!datasource.getCustomDatabaseUrl().isBlank()) {</span>
|
||||
<span class="nc bnc" id="L71" title="All 2 branches missed."> if (datasource.getCustomDatabaseUrl().contains("postgresql")) {</span>
|
||||
<span class="nc" id="L72"> dataSourceBuilder.driverClassName(POSTGRES_DRIVER);</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L75"> dataSourceBuilder.url(datasource.getCustomDatabaseUrl());</span>
|
||||
} else {
|
||||
<span class="nc" id="L77"> dataSourceBuilder.driverClassName(getDriverClassName(datasource.getType()));</span>
|
||||
<span class="nc" id="L78"> dataSourceBuilder.url(</span>
|
||||
<span class="nc" id="L79"> generateCustomDataSourceUrl(</span>
|
||||
<span class="nc" id="L80"> datasource.getType(),</span>
|
||||
<span class="nc" id="L81"> datasource.getHostName(),</span>
|
||||
<span class="nc" id="L82"> datasource.getPort(),</span>
|
||||
<span class="nc" id="L83"> datasource.getName()));</span>
|
||||
}
|
||||
<span class="nc" id="L85"> dataSourceBuilder.username(datasource.getUsername());</span>
|
||||
<span class="nc" id="L86"> dataSourceBuilder.password(datasource.getPassword());</span>
|
||||
|
||||
<span class="nc" id="L88"> return dataSourceBuilder.build();</span>
|
||||
}
|
||||
|
||||
private DataSource useDefaultDataSource(DataSourceBuilder<?> dataSourceBuilder) {
|
||||
<span class="nc" id="L92"> log.info("Using default H2 database");</span>
|
||||
|
||||
<span class="nc" id="L94"> dataSourceBuilder.url(DATASOURCE_DEFAULT_URL);</span>
|
||||
<span class="nc" id="L95"> dataSourceBuilder.username(DEFAULT_USERNAME);</span>
|
||||
|
||||
<span class="nc" id="L97"> return dataSourceBuilder.build();</span>
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the URL the <code>DataSource</code> will use to connect to the database
|
||||
*
|
||||
* @param dataSourceType the type of the database
|
||||
* @param hostname the host name
|
||||
* @param port the port number to use for the database
|
||||
* @param dataSourceName the name the database to connect to
|
||||
* @return the <code>DataSource</code> URL
|
||||
*/
|
||||
private String generateCustomDataSourceUrl(
|
||||
String dataSourceType, String hostname, Integer port, String dataSourceName) {
|
||||
<span class="nc" id="L111"> return DATASOURCE_URL_TEMPLATE.formatted(dataSourceType, hostname, port, dataSourceName);</span>
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the database driver based on the type of database chosen.
|
||||
*
|
||||
* @param driverName the type of the driver (e.g. 'h2', 'postgresql')
|
||||
* @return the fully qualified driver for the database chosen
|
||||
* @throws UnsupportedProviderException when an unsupported database is selected
|
||||
*/
|
||||
private String getDriverClassName(String driverName) throws UnsupportedProviderException {
|
||||
try {
|
||||
<span class="nc" id="L123"> ApplicationProperties.Driver driver =</span>
|
||||
<span class="nc" id="L124"> ApplicationProperties.Driver.valueOf(driverName.toUpperCase());</span>
|
||||
|
||||
<span class="nc bnc" id="L126" title="All 3 branches missed."> switch (driver) {</span>
|
||||
case H2 -> {
|
||||
<span class="nc" id="L128"> log.debug("H2 driver selected");</span>
|
||||
<span class="nc" id="L129"> return DEFAULT_DRIVER;</span>
|
||||
}
|
||||
case POSTGRESQL -> {
|
||||
<span class="nc" id="L132"> log.debug("Postgres driver selected");</span>
|
||||
<span class="nc" id="L133"> return POSTGRES_DRIVER;</span>
|
||||
}
|
||||
default -> {
|
||||
<span class="nc" id="L136"> log.warn("{} driver selected", driverName);</span>
|
||||
<span class="nc" id="L137"> throw new UnsupportedProviderException(</span>
|
||||
driverName + " is not currently supported");
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L141"> } catch (IllegalArgumentException e) {</span>
|
||||
<span class="nc" id="L142"> log.warn("Unknown driver: {}", driverName);</span>
|
||||
<span class="nc" id="L143"> throw new UnsupportedProviderException(driverName + " is not currently supported");</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,318 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>DatabaseService.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.database</a> > <span class="el_source">DatabaseService.java</span></div><h1>DatabaseService.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.jdbc.datasource.init.CannotReadScriptException;
|
||||
import org.springframework.jdbc.datasource.init.ScriptException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.exception.BackupNotFoundException;
|
||||
import stirling.software.SPDF.utils.FileInfo;
|
||||
|
||||
<span class="nc" id="L36">@Slf4j</span>
|
||||
@Service
|
||||
public class DatabaseService implements DatabaseInterface {
|
||||
|
||||
public static final String BACKUP_PREFIX = "backup_";
|
||||
public static final String SQL_SUFFIX = ".sql";
|
||||
private final Path BACKUP_DIR;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final DataSource dataSource;
|
||||
|
||||
<span class="nc" id="L47"> public DatabaseService(ApplicationProperties applicationProperties, DataSource dataSource) {</span>
|
||||
<span class="nc" id="L48"> this.BACKUP_DIR =</span>
|
||||
<span class="nc" id="L49"> Paths.get(InstallationPathConfig.getConfigPath(), "db", "backup").normalize();</span>
|
||||
<span class="nc" id="L50"> this.applicationProperties = applicationProperties;</span>
|
||||
<span class="nc" id="L51"> this.dataSource = dataSource;</span>
|
||||
<span class="nc" id="L52"> }</span>
|
||||
|
||||
/**
|
||||
* Checks if there is at least one backup. First checks if the directory exists, then checks if
|
||||
* there are backup scripts within the directory
|
||||
*
|
||||
* @return true if there are backup scripts, false if there are not
|
||||
*/
|
||||
@Override
|
||||
public boolean hasBackup() {
|
||||
<span class="nc" id="L62"> createBackupDirectory();</span>
|
||||
|
||||
<span class="nc bnc" id="L64" title="All 2 branches missed."> if (Files.exists(BACKUP_DIR)) {</span>
|
||||
<span class="nc bnc" id="L65" title="All 2 branches missed."> return !getBackupList().isEmpty();</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L68"> return false;</span>
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the backup directory and filter for files with the prefix "backup_" and suffix ".sql"
|
||||
*
|
||||
* @return a <code>List</code> of backup files
|
||||
*/
|
||||
@Override
|
||||
public List<FileInfo> getBackupList() {
|
||||
<span class="nc" id="L78"> List<FileInfo> backupFiles = new ArrayList<>();</span>
|
||||
|
||||
<span class="nc bnc" id="L80" title="All 2 branches missed."> if (isH2Database()) {</span>
|
||||
<span class="nc" id="L81"> createBackupDirectory();</span>
|
||||
|
||||
<span class="nc" id="L83"> try (DirectoryStream<Path> stream =</span>
|
||||
<span class="nc" id="L84"> Files.newDirectoryStream(</span>
|
||||
BACKUP_DIR,
|
||||
path ->
|
||||
<span class="nc bnc" id="L87" title="All 2 branches missed."> path.getFileName().toString().startsWith(BACKUP_PREFIX)</span>
|
||||
<span class="nc" id="L88"> && path.getFileName()</span>
|
||||
<span class="nc" id="L89"> .toString()</span>
|
||||
<span class="nc bnc" id="L90" title="All 2 branches missed."> .endsWith(SQL_SUFFIX))) {</span>
|
||||
<span class="nc bnc" id="L91" title="All 2 branches missed."> for (Path entry : stream) {</span>
|
||||
<span class="nc" id="L92"> BasicFileAttributes attrs =</span>
|
||||
<span class="nc" id="L93"> Files.readAttributes(entry, BasicFileAttributes.class);</span>
|
||||
<span class="nc" id="L94"> LocalDateTime modificationDate =</span>
|
||||
<span class="nc" id="L95"> LocalDateTime.ofInstant(</span>
|
||||
<span class="nc" id="L96"> attrs.lastModifiedTime().toInstant(), ZoneId.systemDefault());</span>
|
||||
<span class="nc" id="L97"> LocalDateTime creationDate =</span>
|
||||
<span class="nc" id="L98"> LocalDateTime.ofInstant(</span>
|
||||
<span class="nc" id="L99"> attrs.creationTime().toInstant(), ZoneId.systemDefault());</span>
|
||||
<span class="nc" id="L100"> long fileSize = attrs.size();</span>
|
||||
<span class="nc" id="L101"> backupFiles.add(</span>
|
||||
new FileInfo(
|
||||
<span class="nc" id="L103"> entry.getFileName().toString(),</span>
|
||||
<span class="nc" id="L104"> entry.toString(),</span>
|
||||
modificationDate,
|
||||
fileSize,
|
||||
creationDate));
|
||||
<span class="nc" id="L108"> }</span>
|
||||
<span class="nc" id="L109"> } catch (IOException e) {</span>
|
||||
<span class="nc" id="L110"> log.error("Error reading backup directory: {}", e.getMessage(), e);</span>
|
||||
<span class="nc" id="L111"> }</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L114"> return backupFiles;</span>
|
||||
}
|
||||
|
||||
private void createBackupDirectory() {
|
||||
<span class="nc bnc" id="L118" title="All 2 branches missed."> if (!Files.exists(BACKUP_DIR)) {</span>
|
||||
try {
|
||||
<span class="nc" id="L120"> Files.createDirectories(BACKUP_DIR);</span>
|
||||
<span class="nc" id="L121"> log.debug("create backup directory: {}", BACKUP_DIR);</span>
|
||||
<span class="nc" id="L122"> } catch (IOException e) {</span>
|
||||
<span class="nc" id="L123"> log.error("Error create backup directory: {}", e.getMessage(), e);</span>
|
||||
<span class="nc" id="L124"> }</span>
|
||||
}
|
||||
<span class="nc" id="L126"> }</span>
|
||||
|
||||
@Override
|
||||
public void importDatabase() {
|
||||
<span class="nc bnc" id="L130" title="All 2 branches missed."> if (!hasBackup()) throw new BackupNotFoundException("No backup scripts were found.");</span>
|
||||
|
||||
<span class="nc" id="L132"> List<FileInfo> backupList = this.getBackupList();</span>
|
||||
<span class="nc" id="L133"> backupList.sort(Comparator.comparing(FileInfo::getModificationDate).reversed());</span>
|
||||
|
||||
<span class="nc" id="L135"> Path latestExport = Paths.get(backupList.get(0).getFilePath());</span>
|
||||
|
||||
<span class="nc" id="L137"> executeDatabaseScript(latestExport);</span>
|
||||
<span class="nc" id="L138"> }</span>
|
||||
|
||||
/** Imports a database backup from the specified file. */
|
||||
public boolean importDatabaseFromUI(String fileName) {
|
||||
try {
|
||||
<span class="nc" id="L143"> importDatabaseFromUI(getBackupFilePath(fileName));</span>
|
||||
<span class="nc" id="L144"> return true;</span>
|
||||
<span class="nc" id="L145"> } catch (IOException e) {</span>
|
||||
<span class="nc" id="L146"> log.error(</span>
|
||||
"Error importing database from file: {}, message: {}",
|
||||
fileName,
|
||||
<span class="nc" id="L149"> e.getMessage(),</span>
|
||||
<span class="nc" id="L150"> e.getCause());</span>
|
||||
<span class="nc" id="L151"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
/** Imports a database backup from the specified path. */
|
||||
public boolean importDatabaseFromUI(Path tempTemplatePath) throws IOException {
|
||||
<span class="nc" id="L157"> executeDatabaseScript(tempTemplatePath);</span>
|
||||
<span class="nc" id="L158"> LocalDateTime dateNow = LocalDateTime.now();</span>
|
||||
<span class="nc" id="L159"> DateTimeFormatter myFormatObj = DateTimeFormatter.ofPattern("yyyyMMddHHmm");</span>
|
||||
<span class="nc" id="L160"> Path insertOutputFilePath =</span>
|
||||
<span class="nc" id="L161"> this.getBackupFilePath(</span>
|
||||
<span class="nc" id="L162"> BACKUP_PREFIX + "user_" + dateNow.format(myFormatObj) + SQL_SUFFIX);</span>
|
||||
<span class="nc" id="L163"> Files.copy(tempTemplatePath, insertOutputFilePath);</span>
|
||||
<span class="nc" id="L164"> Files.deleteIfExists(tempTemplatePath);</span>
|
||||
<span class="nc" id="L165"> return true;</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportDatabase() {
|
||||
<span class="nc" id="L170"> List<FileInfo> filteredBackupList =</span>
|
||||
<span class="nc" id="L171"> this.getBackupList().stream()</span>
|
||||
<span class="nc bnc" id="L172" title="All 2 branches missed."> .filter(backup -> !backup.getFileName().startsWith(BACKUP_PREFIX + "user_"))</span>
|
||||
<span class="nc" id="L173"> .collect(Collectors.toList());</span>
|
||||
|
||||
<span class="nc bnc" id="L175" title="All 2 branches missed."> if (filteredBackupList.size() > 5) {</span>
|
||||
<span class="nc" id="L176"> deleteOldestBackup(filteredBackupList);</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L179"> LocalDateTime dateNow = LocalDateTime.now();</span>
|
||||
<span class="nc" id="L180"> DateTimeFormatter myFormatObj = DateTimeFormatter.ofPattern("yyyyMMddHHmm");</span>
|
||||
<span class="nc" id="L181"> Path insertOutputFilePath =</span>
|
||||
<span class="nc" id="L182"> this.getBackupFilePath(BACKUP_PREFIX + dateNow.format(myFormatObj) + SQL_SUFFIX);</span>
|
||||
|
||||
<span class="nc bnc" id="L184" title="All 2 branches missed."> if (isH2Database()) {</span>
|
||||
<span class="nc" id="L185"> String query = "SCRIPT SIMPLE COLUMNS DROP to ?;";</span>
|
||||
|
||||
<span class="nc" id="L187"> try (Connection conn = dataSource.getConnection();</span>
|
||||
<span class="nc" id="L188"> PreparedStatement stmt = conn.prepareStatement(query)) {</span>
|
||||
<span class="nc" id="L189"> stmt.setString(1, insertOutputFilePath.toString());</span>
|
||||
<span class="nc" id="L190"> stmt.execute();</span>
|
||||
<span class="nc" id="L191"> } catch (SQLException e) {</span>
|
||||
<span class="nc" id="L192"> log.error("Error during database export: {}", e.getMessage(), e);</span>
|
||||
<span class="nc" id="L193"> } catch (CannotReadScriptException e) {</span>
|
||||
<span class="nc" id="L194"> log.error("Error during database export: File {} not found", insertOutputFilePath);</span>
|
||||
<span class="nc" id="L195"> }</span>
|
||||
|
||||
<span class="nc" id="L197"> log.info("Database export completed: {}", insertOutputFilePath);</span>
|
||||
}
|
||||
<span class="nc" id="L199"> }</span>
|
||||
|
||||
private static void deleteOldestBackup(List<FileInfo> filteredBackupList) {
|
||||
try {
|
||||
<span class="nc" id="L203"> filteredBackupList.sort(</span>
|
||||
<span class="nc" id="L204"> Comparator.comparing(</span>
|
||||
<span class="nc" id="L205"> p -> p.getFileName().substring(7, p.getFileName().length() - 4)));</span>
|
||||
|
||||
<span class="nc" id="L207"> FileInfo oldestFile = filteredBackupList.get(0);</span>
|
||||
<span class="nc" id="L208"> Files.deleteIfExists(Paths.get(oldestFile.getFilePath()));</span>
|
||||
<span class="nc" id="L209"> log.info("Deleted oldest backup: {}", oldestFile.getFileName());</span>
|
||||
<span class="nc" id="L210"> } catch (IOException e) {</span>
|
||||
<span class="nc" id="L211"> log.error("Unable to delete oldest backup, message: {}", e.getMessage(), e);</span>
|
||||
<span class="nc" id="L212"> }</span>
|
||||
<span class="nc" id="L213"> }</span>
|
||||
|
||||
/**
|
||||
* Retrieves the H2 database version.
|
||||
*
|
||||
* @return <code>String</code> of the H2 version
|
||||
*/
|
||||
public String getH2Version() {
|
||||
<span class="nc" id="L221"> String version = "Unknown";</span>
|
||||
|
||||
<span class="nc bnc" id="L223" title="All 2 branches missed."> if (isH2Database()) {</span>
|
||||
<span class="nc" id="L224"> try (Connection conn = dataSource.getConnection()) {</span>
|
||||
<span class="nc" id="L225"> try (Statement stmt = conn.createStatement();</span>
|
||||
<span class="nc" id="L226"> ResultSet rs = stmt.executeQuery("SELECT H2VERSION() AS version")) {</span>
|
||||
<span class="nc bnc" id="L227" title="All 2 branches missed."> if (rs.next()) {</span>
|
||||
<span class="nc" id="L228"> version = rs.getString("version");</span>
|
||||
<span class="nc" id="L229"> log.info("H2 Database Version: {}", version);</span>
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L232"> } catch (SQLException e) {</span>
|
||||
<span class="nc" id="L233"> log.error("Error retrieving H2 version: {}", e.getMessage(), e);</span>
|
||||
<span class="nc" id="L234"> }</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L237"> return version;</span>
|
||||
}
|
||||
|
||||
private boolean isH2Database() {
|
||||
<span class="nc" id="L241"> ApplicationProperties.Datasource datasource =</span>
|
||||
<span class="nc" id="L242"> applicationProperties.getSystem().getDatasource();</span>
|
||||
<span class="nc bnc" id="L243" title="All 2 branches missed."> return !datasource.isEnableCustomDatabase()</span>
|
||||
<span class="nc bnc" id="L244" title="All 2 branches missed."> || datasource.getType().equalsIgnoreCase(ApplicationProperties.Driver.H2.name());</span>
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a backup file.
|
||||
*
|
||||
* @return true if successful, false if not
|
||||
*/
|
||||
public boolean deleteBackupFile(String fileName) throws IOException {
|
||||
<span class="nc bnc" id="L253" title="All 2 branches missed."> if (!isValidFileName(fileName)) {</span>
|
||||
<span class="nc" id="L254"> log.error("Invalid file name: {}", fileName);</span>
|
||||
<span class="nc" id="L255"> return false;</span>
|
||||
}
|
||||
<span class="nc" id="L257"> Path filePath = this.getBackupFilePath(fileName);</span>
|
||||
<span class="nc bnc" id="L258" title="All 2 branches missed."> if (Files.deleteIfExists(filePath)) {</span>
|
||||
<span class="nc" id="L259"> log.info("Deleted backup file: {}", fileName);</span>
|
||||
<span class="nc" id="L260"> return true;</span>
|
||||
} else {
|
||||
<span class="nc" id="L262"> log.error("File not found or could not be deleted: {}", fileName);</span>
|
||||
<span class="nc" id="L263"> return false;</span>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Path for a given backup file name.
|
||||
*
|
||||
* @return the <code>Path</code> object for the given file name
|
||||
*/
|
||||
public Path getBackupFilePath(String fileName) {
|
||||
<span class="nc" id="L273"> createBackupDirectory();</span>
|
||||
<span class="nc" id="L274"> Path filePath = BACKUP_DIR.resolve(fileName).normalize();</span>
|
||||
<span class="nc bnc" id="L275" title="All 2 branches missed."> if (!filePath.startsWith(BACKUP_DIR)) {</span>
|
||||
<span class="nc" id="L276"> throw new SecurityException("Path traversal detected");</span>
|
||||
}
|
||||
<span class="nc" id="L278"> return filePath;</span>
|
||||
}
|
||||
|
||||
private void executeDatabaseScript(Path scriptPath) {
|
||||
<span class="nc bnc" id="L282" title="All 2 branches missed."> if (isH2Database()) {</span>
|
||||
<span class="nc" id="L283"> String query = "RUNSCRIPT from ?;";</span>
|
||||
|
||||
<span class="nc" id="L285"> try (Connection conn = dataSource.getConnection();</span>
|
||||
<span class="nc" id="L286"> PreparedStatement stmt = conn.prepareStatement(query)) {</span>
|
||||
<span class="nc" id="L287"> stmt.setString(1, scriptPath.toString());</span>
|
||||
<span class="nc" id="L288"> stmt.execute();</span>
|
||||
<span class="nc" id="L289"> } catch (SQLException e) {</span>
|
||||
<span class="nc" id="L290"> log.error("Error during database import: {}", e.getMessage(), e);</span>
|
||||
<span class="nc" id="L291"> } catch (ScriptException e) {</span>
|
||||
<span class="nc" id="L292"> log.error("Error: File {} not found", scriptPath.toString(), e);</span>
|
||||
<span class="nc" id="L293"> }</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L296"> log.info("Database import completed: {}", scriptPath);</span>
|
||||
<span class="nc" id="L297"> }</span>
|
||||
|
||||
/**
|
||||
* Checks for invalid characters or sequences
|
||||
*
|
||||
* @return true if it contains no invalid characters, false if it does
|
||||
*/
|
||||
private boolean isValidFileName(String fileName) {
|
||||
<span class="nc bnc" id="L305" title="All 2 branches missed."> return fileName != null</span>
|
||||
<span class="nc bnc" id="L306" title="All 2 branches missed."> && !fileName.contains("..")</span>
|
||||
<span class="nc bnc" id="L307" title="All 2 branches missed."> && !fileName.contains("/")</span>
|
||||
<span class="nc bnc" id="L308" title="All 2 branches missed."> && !fileName.contains("\\")</span>
|
||||
<span class="nc bnc" id="L309" title="All 2 branches missed."> && !fileName.contains(":")</span>
|
||||
<span class="nc bnc" id="L310" title="All 2 branches missed."> && !fileName.contains("*")</span>
|
||||
<span class="nc bnc" id="L311" title="All 2 branches missed."> && !fileName.contains("?")</span>
|
||||
<span class="nc bnc" id="L312" title="All 2 branches missed."> && !fileName.contains("\"")</span>
|
||||
<span class="nc bnc" id="L313" title="All 2 branches missed."> && !fileName.contains("<")</span>
|
||||
<span class="nc bnc" id="L314" title="All 2 branches missed."> && !fileName.contains(">")</span>
|
||||
<span class="nc bnc" id="L315" title="All 2 branches missed."> && !fileName.contains("|");</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ScheduledTasks</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.config.security.database</a> > <span class="el_class">ScheduledTasks</span></div><h1>ScheduledTasks</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">10 of 10</td><td class="ctr2">0%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">2</td><td class="ctr2">2</td><td class="ctr1">5</td><td class="ctr2">5</td><td class="ctr1">2</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a1"><a href="ScheduledTasks.java.html#L19" class="el_method">ScheduledTasks(DatabaseInterface)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="6" alt="6"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">1</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h0">3</td><td class="ctr2" id="i0">3</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a0"><a href="ScheduledTasks.java.html#L25" class="el_method">performBackup()</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="80" height="10" title="4" alt="4"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">2</td><td class="ctr2" id="i1">2</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ScheduledTasks.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.database</a> > <span class="el_source">ScheduledTasks.java</span></div><h1>ScheduledTasks.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.database;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
|
||||
import stirling.software.SPDF.controller.api.H2SQLCondition;
|
||||
import stirling.software.SPDF.model.exception.UnsupportedProviderException;
|
||||
|
||||
@Component
|
||||
@Conditional(H2SQLCondition.class)
|
||||
public class ScheduledTasks {
|
||||
|
||||
private final DatabaseInterface databaseService;
|
||||
|
||||
<span class="nc" id="L19"> public ScheduledTasks(DatabaseInterface databaseService) {</span>
|
||||
<span class="nc" id="L20"> this.databaseService = databaseService;</span>
|
||||
<span class="nc" id="L21"> }</span>
|
||||
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
public void performBackup() throws SQLException, UnsupportedProviderException {
|
||||
<span class="nc" id="L25"> databaseService.exportDatabase();</span>
|
||||
<span class="nc" id="L26"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>stirling.software.SPDF.config.security.database</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb', 'coveragetable'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="index.source.html" class="el_source">Source Files</a><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <span class="el_package">stirling.software.SPDF.config.security.database</span></div><h1>stirling.software.SPDF.config.security.database</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td><td class="sortable ctr1" id="l" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="m" onclick="toggleSort(this)">Classes</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">737 of 737</td><td class="ctr2">0%</td><td class="bar">71 of 71</td><td class="ctr2">0%</td><td class="ctr1">63</td><td class="ctr2">63</td><td class="ctr1">206</td><td class="ctr2">206</td><td class="ctr1">27</td><td class="ctr2">27</td><td class="ctr1">3</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a1"><a href="DatabaseService.html" class="el_class">DatabaseService</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="551" alt="551"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="60" alt="60"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">49</td><td class="ctr2" id="g0">49</td><td class="ctr1" id="h0">154</td><td class="ctr2" id="i0">154</td><td class="ctr1" id="j0">19</td><td class="ctr2" id="k0">19</td><td class="ctr1" id="l0">1</td><td class="ctr2" id="m0">1</td></tr><tr><td id="a0"><a href="DatabaseConfig.html" class="el_class">DatabaseConfig</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="38" height="10" title="176" alt="176"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"><img src="../jacoco-resources/redbar.gif" width="22" height="10" title="11" alt="11"/></td><td class="ctr2" id="e1">0%</td><td class="ctr1" id="f1">12</td><td class="ctr2" id="g1">12</td><td class="ctr1" id="h1">47</td><td class="ctr2" id="i1">47</td><td class="ctr1" id="j1">6</td><td class="ctr2" id="k1">6</td><td class="ctr1" id="l1">1</td><td class="ctr2" id="m1">1</td></tr><tr><td id="a2"><a href="ScheduledTasks.html" class="el_class">ScheduledTasks</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="2" height="10" title="10" alt="10"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">2</td><td class="ctr2" id="g2">2</td><td class="ctr1" id="h2">5</td><td class="ctr2" id="i2">5</td><td class="ctr1" id="j2">2</td><td class="ctr2" id="k2">2</td><td class="ctr1" id="l2">1</td><td class="ctr2" id="m2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>stirling.software.SPDF.config.security.database</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb', 'coveragetable'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="index.html" class="el_class">Classes</a><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <span class="el_package">stirling.software.SPDF.config.security.database</span></div><h1>stirling.software.SPDF.config.security.database</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td><td class="sortable ctr1" id="l" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="m" onclick="toggleSort(this)">Classes</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">737 of 737</td><td class="ctr2">0%</td><td class="bar">71 of 71</td><td class="ctr2">0%</td><td class="ctr1">63</td><td class="ctr2">63</td><td class="ctr1">206</td><td class="ctr2">206</td><td class="ctr1">27</td><td class="ctr2">27</td><td class="ctr1">3</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a1"><a href="DatabaseService.java.html" class="el_source">DatabaseService.java</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="551" alt="551"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="60" alt="60"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">49</td><td class="ctr2" id="g0">49</td><td class="ctr1" id="h0">154</td><td class="ctr2" id="i0">154</td><td class="ctr1" id="j0">19</td><td class="ctr2" id="k0">19</td><td class="ctr1" id="l0">1</td><td class="ctr2" id="m0">1</td></tr><tr><td id="a0"><a href="DatabaseConfig.java.html" class="el_source">DatabaseConfig.java</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="38" height="10" title="176" alt="176"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"><img src="../jacoco-resources/redbar.gif" width="22" height="10" title="11" alt="11"/></td><td class="ctr2" id="e1">0%</td><td class="ctr1" id="f1">12</td><td class="ctr2" id="g1">12</td><td class="ctr1" id="h1">47</td><td class="ctr2" id="i1">47</td><td class="ctr1" id="j1">6</td><td class="ctr2" id="k1">6</td><td class="ctr1" id="l1">1</td><td class="ctr2" id="m1">1</td></tr><tr><td id="a2"><a href="ScheduledTasks.java.html" class="el_source">ScheduledTasks.java</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="2" height="10" title="10" alt="10"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">2</td><td class="ctr2" id="g2">2</td><td class="ctr1" id="h2">5</td><td class="ctr2" id="i2">5</td><td class="ctr1" id="j2">2</td><td class="ctr2" id="k2">2</td><td class="ctr1" id="l2">1</td><td class="ctr2" id="m2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomOAuth2AuthenticationFailureHandler</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.config.security.oauth2</a> > <span class="el_class">CustomOAuth2AuthenticationFailureHandler</span></div><h1>CustomOAuth2AuthenticationFailureHandler</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">95 of 95</td><td class="ctr2">0%</td><td class="bar">12 of 12</td><td class="ctr2">0%</td><td class="ctr1">9</td><td class="ctr2">9</td><td class="ctr1">25</td><td class="ctr2">25</td><td class="ctr1">3</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a1"><a href="CustomOAuth2AuthenticationFailureHandler.java.html#L30" class="el_method">onAuthenticationFailure(HttpServletRequest, HttpServletResponse, AuthenticationException)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="88" alt="88"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="12" alt="12"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">7</td><td class="ctr2" id="g0">7</td><td class="ctr1" id="h0">23</td><td class="ctr2" id="i0">23</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a2"><a href="CustomOAuth2AuthenticationFailureHandler.java.html#L19" class="el_method">static {...}</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="5" height="10" title="4" alt="4"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">1</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a0"><a href="CustomOAuth2AuthenticationFailureHandler.java.html#L20" class="el_method">CustomOAuth2AuthenticationFailureHandler()</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="4" height="10" title="3" alt="3"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">1</td><td class="ctr2" id="i2">1</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomOAuth2AuthenticationFailureHandler.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.oauth2</a> > <span class="el_source">CustomOAuth2AuthenticationFailureHandler.java</span></div><h1>CustomOAuth2AuthenticationFailureHandler.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.oauth2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.DisabledException;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
<span class="nc" id="L19">@Slf4j</span>
|
||||
<span class="nc" id="L20">public class CustomOAuth2AuthenticationFailureHandler</span>
|
||||
extends SimpleUrlAuthenticationFailureHandler {
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AuthenticationException exception)
|
||||
throws IOException, ServletException {
|
||||
|
||||
<span class="nc bnc" id="L30" title="All 2 branches missed."> if (exception instanceof BadCredentialsException) {</span>
|
||||
<span class="nc" id="L31"> log.error("BadCredentialsException", exception);</span>
|
||||
<span class="nc" id="L32"> getRedirectStrategy().sendRedirect(request, response, "/login?error=badCredentials");</span>
|
||||
<span class="nc" id="L33"> return;</span>
|
||||
}
|
||||
<span class="nc bnc" id="L35" title="All 2 branches missed."> if (exception instanceof DisabledException) {</span>
|
||||
<span class="nc" id="L36"> log.error("User is deactivated: ", exception);</span>
|
||||
<span class="nc" id="L37"> getRedirectStrategy().sendRedirect(request, response, "/logout?userIsDisabled=true");</span>
|
||||
<span class="nc" id="L38"> return;</span>
|
||||
}
|
||||
<span class="nc bnc" id="L40" title="All 2 branches missed."> if (exception instanceof LockedException) {</span>
|
||||
<span class="nc" id="L41"> log.error("Account locked: ", exception);</span>
|
||||
<span class="nc" id="L42"> getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");</span>
|
||||
<span class="nc" id="L43"> return;</span>
|
||||
}
|
||||
<span class="nc bnc" id="L45" title="All 2 branches missed."> if (exception instanceof OAuth2AuthenticationException oAuth2Exception) {</span>
|
||||
<span class="nc" id="L46"> OAuth2Error error = oAuth2Exception.getError();</span>
|
||||
|
||||
<span class="nc" id="L48"> String errorCode = error.getErrorCode();</span>
|
||||
|
||||
<span class="nc bnc" id="L50" title="All 2 branches missed."> if ("Password must not be null".equals(error.getErrorCode())) {</span>
|
||||
<span class="nc" id="L51"> errorCode = "userAlreadyExistsWeb";</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L54"> log.error(</span>
|
||||
"OAuth2 Authentication error: {}",
|
||||
<span class="nc bnc" id="L56" title="All 2 branches missed."> errorCode != null ? errorCode : exception.getMessage(),</span>
|
||||
exception);
|
||||
<span class="nc" id="L58"> getRedirectStrategy().sendRedirect(request, response, "/login?errorOAuth=" + errorCode);</span>
|
||||
}
|
||||
<span class="nc" id="L60"> log.error("Unhandled authentication exception", exception);</span>
|
||||
<span class="nc" id="L61"> super.onAuthenticationFailure(request, response, exception);</span>
|
||||
<span class="nc" id="L62"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomOAuth2AuthenticationSuccessHandler</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.config.security.oauth2</a> > <span class="el_class">CustomOAuth2AuthenticationSuccessHandler</span></div><h1>CustomOAuth2AuthenticationSuccessHandler</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">157 of 157</td><td class="ctr2">0%</td><td class="bar">30 of 30</td><td class="ctr2">0%</td><td class="ctr1">17</td><td class="ctr2">17</td><td class="ctr1">45</td><td class="ctr2">45</td><td class="ctr1">2</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a1"><a href="CustomOAuth2AuthenticationSuccessHandler.java.html#L47" class="el_method">onAuthenticationSuccess(HttpServletRequest, HttpServletResponse, Authentication)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="145" alt="145"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="30" alt="30"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">16</td><td class="ctr2" id="g0">16</td><td class="ctr1" id="h0">40</td><td class="ctr2" id="i0">40</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a0"><a href="CustomOAuth2AuthenticationSuccessHandler.java.html#L36" class="el_method">CustomOAuth2AuthenticationSuccessHandler(LoginAttemptService, ApplicationProperties, UserService)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="9" height="10" title="12" alt="12"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">5</td><td class="ctr2" id="i1">5</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomOAuth2AuthenticationSuccessHandler.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.oauth2</a> > <span class="el_source">CustomOAuth2AuthenticationSuccessHandler.java</span></div><h1>CustomOAuth2AuthenticationSuccessHandler.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.oauth2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import stirling.software.SPDF.config.security.LoginAttemptService;
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||
import stirling.software.SPDF.model.AuthenticationType;
|
||||
import stirling.software.SPDF.model.exception.UnsupportedProviderException;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
public class CustomOAuth2AuthenticationSuccessHandler
|
||||
extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||
|
||||
private final LoginAttemptService loginAttemptService;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final UserService userService;
|
||||
|
||||
public CustomOAuth2AuthenticationSuccessHandler(
|
||||
LoginAttemptService loginAttemptService,
|
||||
ApplicationProperties applicationProperties,
|
||||
<span class="nc" id="L36"> UserService userService) {</span>
|
||||
<span class="nc" id="L37"> this.applicationProperties = applicationProperties;</span>
|
||||
<span class="nc" id="L38"> this.userService = userService;</span>
|
||||
<span class="nc" id="L39"> this.loginAttemptService = loginAttemptService;</span>
|
||||
<span class="nc" id="L40"> }</span>
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(
|
||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||
throws ServletException, IOException {
|
||||
|
||||
<span class="nc" id="L47"> Object principal = authentication.getPrincipal();</span>
|
||||
<span class="nc" id="L48"> String username = "";</span>
|
||||
|
||||
<span class="nc bnc" id="L50" title="All 2 branches missed."> if (principal instanceof OAuth2User oAuth2User) {</span>
|
||||
<span class="nc" id="L51"> username = oAuth2User.getName();</span>
|
||||
<span class="nc bnc" id="L52" title="All 2 branches missed."> } else if (principal instanceof UserDetails detailsUser) {</span>
|
||||
<span class="nc" id="L53"> username = detailsUser.getUsername();</span>
|
||||
}
|
||||
|
||||
// Get the saved request
|
||||
<span class="nc" id="L57"> HttpSession session = request.getSession(false);</span>
|
||||
<span class="nc" id="L58"> String contextPath = request.getContextPath();</span>
|
||||
SavedRequest savedRequest =
|
||||
<span class="nc bnc" id="L60" title="All 2 branches missed."> (session != null)</span>
|
||||
<span class="nc" id="L61"> ? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")</span>
|
||||
<span class="nc" id="L62"> : null;</span>
|
||||
|
||||
<span class="nc bnc" id="L64" title="All 2 branches missed."> if (savedRequest != null</span>
|
||||
<span class="nc bnc" id="L65" title="All 2 branches missed."> && !RequestUriUtils.isStaticResource(contextPath, savedRequest.getRedirectUrl())) {</span>
|
||||
// Redirect to the original destination
|
||||
<span class="nc" id="L67"> super.onAuthenticationSuccess(request, response, authentication);</span>
|
||||
} else {
|
||||
<span class="nc" id="L69"> OAUTH2 oAuth = applicationProperties.getSecurity().getOauth2();</span>
|
||||
|
||||
<span class="nc bnc" id="L71" title="All 2 branches missed."> if (loginAttemptService.isBlocked(username)) {</span>
|
||||
<span class="nc bnc" id="L72" title="All 2 branches missed."> if (session != null) {</span>
|
||||
<span class="nc" id="L73"> session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");</span>
|
||||
}
|
||||
<span class="nc" id="L75"> throw new LockedException(</span>
|
||||
"Your account has been locked due to too many failed login attempts.");
|
||||
}
|
||||
|
||||
<span class="nc bnc" id="L79" title="All 2 branches missed."> if (userService.isUserDisabled(username)) {</span>
|
||||
<span class="nc" id="L80"> getRedirectStrategy()</span>
|
||||
<span class="nc" id="L81"> .sendRedirect(request, response, "/logout?userIsDisabled=true");</span>
|
||||
<span class="nc" id="L82"> return;</span>
|
||||
}
|
||||
<span class="nc bnc" id="L84" title="All 2 branches missed."> if (userService.usernameExistsIgnoreCase(username)</span>
|
||||
<span class="nc bnc" id="L85" title="All 2 branches missed."> && userService.hasPassword(username)</span>
|
||||
<span class="nc bnc" id="L86" title="All 2 branches missed."> && !userService.isAuthenticationTypeByUsername(username, AuthenticationType.SSO)</span>
|
||||
<span class="nc bnc" id="L87" title="All 2 branches missed."> && oAuth.getAutoCreateUser()) {</span>
|
||||
<span class="nc" id="L88"> response.sendRedirect(contextPath + "/logout?oAuth2AuthenticationErrorWeb=true");</span>
|
||||
<span class="nc" id="L89"> return;</span>
|
||||
}
|
||||
|
||||
try {
|
||||
<span class="nc bnc" id="L93" title="All 2 branches missed."> if (oAuth.getBlockRegistration()</span>
|
||||
<span class="nc bnc" id="L94" title="All 2 branches missed."> && !userService.usernameExistsIgnoreCase(username)) {</span>
|
||||
<span class="nc" id="L95"> response.sendRedirect(contextPath + "/logout?oAuth2AdminBlockedUser=true");</span>
|
||||
<span class="nc" id="L96"> return;</span>
|
||||
}
|
||||
<span class="nc bnc" id="L98" title="All 2 branches missed."> if (principal instanceof OAuth2User) {</span>
|
||||
<span class="nc" id="L99"> userService.processSSOPostLogin(username, oAuth.getAutoCreateUser());</span>
|
||||
}
|
||||
<span class="nc" id="L101"> response.sendRedirect(contextPath + "/");</span>
|
||||
<span class="nc" id="L102"> } catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {</span>
|
||||
<span class="nc" id="L103"> response.sendRedirect(contextPath + "/logout?invalidUsername=true");</span>
|
||||
<span class="nc" id="L104"> }</span>
|
||||
}
|
||||
<span class="nc" id="L106"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomOAuth2UserService</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.config.security.oauth2</a> > <span class="el_class">CustomOAuth2UserService</span></div><h1>CustomOAuth2UserService</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">113 of 113</td><td class="ctr2">0%</td><td class="bar">6 of 6</td><td class="ctr2">0%</td><td class="ctr1">6</td><td class="ctr2">6</td><td class="ctr1">30</td><td class="ctr2">30</td><td class="ctr1">3</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a1"><a href="CustomOAuth2UserService.java.html#L46" class="el_method">loadUser(OidcUserRequest)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="92" alt="92"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="6" alt="6"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">4</td><td class="ctr2" id="g0">4</td><td class="ctr1" id="h0">23</td><td class="ctr2" id="i0">23</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a0"><a href="CustomOAuth2UserService.java.html#L26" class="el_method">CustomOAuth2UserService(ApplicationProperties, UserService, LoginAttemptService)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="22" height="10" title="17" alt="17"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">6</td><td class="ctr2" id="i1">6</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a2"><a href="CustomOAuth2UserService.java.html#L23" class="el_method">static {...}</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="5" height="10" title="4" alt="4"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">1</td><td class="ctr2" id="i2">1</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomOAuth2UserService.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.oauth2</a> > <span class="el_source">CustomOAuth2UserService.java</span></div><h1>CustomOAuth2UserService.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.oauth2;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.config.security.LoginAttemptService;
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.model.UsernameAttribute;
|
||||
|
||||
<span class="nc" id="L23">@Slf4j</span>
|
||||
public class CustomOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
|
||||
|
||||
<span class="nc" id="L26"> private final OidcUserService delegate = new OidcUserService();</span>
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final LoginAttemptService loginAttemptService;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
public CustomOAuth2UserService(
|
||||
ApplicationProperties applicationProperties,
|
||||
UserService userService,
|
||||
<span class="nc" id="L37"> LoginAttemptService loginAttemptService) {</span>
|
||||
<span class="nc" id="L38"> this.applicationProperties = applicationProperties;</span>
|
||||
<span class="nc" id="L39"> this.userService = userService;</span>
|
||||
<span class="nc" id="L40"> this.loginAttemptService = loginAttemptService;</span>
|
||||
<span class="nc" id="L41"> }</span>
|
||||
|
||||
@Override
|
||||
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
|
||||
try {
|
||||
<span class="nc" id="L46"> OidcUser user = delegate.loadUser(userRequest);</span>
|
||||
<span class="nc" id="L47"> OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2();</span>
|
||||
<span class="nc" id="L48"> UsernameAttribute usernameAttribute =</span>
|
||||
<span class="nc" id="L49"> UsernameAttribute.valueOf(oauth2.getUseAsUsername().toUpperCase());</span>
|
||||
<span class="nc" id="L50"> String usernameAttributeKey = usernameAttribute.getName();</span>
|
||||
|
||||
// todo: save user by OIDC ID instead of username
|
||||
<span class="nc" id="L53"> Optional<User> internalUser =</span>
|
||||
<span class="nc" id="L54"> userService.findByUsernameIgnoreCase(user.getAttribute(usernameAttributeKey));</span>
|
||||
|
||||
<span class="nc bnc" id="L56" title="All 2 branches missed."> if (internalUser.isPresent()) {</span>
|
||||
<span class="nc" id="L57"> String internalUsername = internalUser.get().getUsername();</span>
|
||||
<span class="nc bnc" id="L58" title="All 2 branches missed."> if (loginAttemptService.isBlocked(internalUsername)) {</span>
|
||||
<span class="nc" id="L59"> throw new LockedException(</span>
|
||||
"The account "
|
||||
+ internalUsername
|
||||
+ " has been locked due to too many failed login attempts.");
|
||||
}
|
||||
<span class="nc bnc" id="L64" title="All 2 branches missed."> if (userService.hasPassword(usernameAttributeKey)) {</span>
|
||||
<span class="nc" id="L65"> throw new IllegalArgumentException("Password must not be null");</span>
|
||||
}
|
||||
}
|
||||
|
||||
// Return a new OidcUser with adjusted attributes
|
||||
<span class="nc" id="L70"> return new DefaultOidcUser(</span>
|
||||
<span class="nc" id="L71"> user.getAuthorities(),</span>
|
||||
<span class="nc" id="L72"> userRequest.getIdToken(),</span>
|
||||
<span class="nc" id="L73"> user.getUserInfo(),</span>
|
||||
usernameAttributeKey);
|
||||
<span class="nc" id="L75"> } catch (IllegalArgumentException e) {</span>
|
||||
<span class="nc" id="L76"> log.error("Error loading OIDC user: {}", e.getMessage());</span>
|
||||
<span class="nc" id="L77"> throw new OAuth2AuthenticationException(new OAuth2Error(e.getMessage()), e);</span>
|
||||
<span class="nc" id="L78"> } catch (Exception e) {</span>
|
||||
<span class="nc" id="L79"> log.error("Unexpected error loading OIDC user", e);</span>
|
||||
<span class="nc" id="L80"> throw new OAuth2AuthenticationException("Unexpected error during authentication");</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,253 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>OAuth2Configuration.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.oauth2</a> > <span class="el_source">OAuth2Configuration.java</span></div><h1>OAuth2Configuration.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.oauth2;
|
||||
|
||||
import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE;
|
||||
import static stirling.software.SPDF.utils.validation.Validator.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.model.UsernameAttribute;
|
||||
import stirling.software.SPDF.model.exception.NoProviderFoundException;
|
||||
import stirling.software.SPDF.model.provider.GitHubProvider;
|
||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
||||
import stirling.software.SPDF.model.provider.Provider;
|
||||
|
||||
<span class="nc" id="L39">@Slf4j</span>
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "security.oauth2.enabled", havingValue = "true")
|
||||
public class OAuth2Configuration {
|
||||
|
||||
public static final String REDIRECT_URI_PATH = "{baseUrl}/login/oauth2/code/";
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
@Lazy private final UserService userService;
|
||||
|
||||
public OAuth2Configuration(
|
||||
<span class="nc" id="L50"> ApplicationProperties applicationProperties, @Lazy UserService userService) {</span>
|
||||
<span class="nc" id="L51"> this.userService = userService;</span>
|
||||
<span class="nc" id="L52"> this.applicationProperties = applicationProperties;</span>
|
||||
<span class="nc" id="L53"> }</span>
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "security.oauth2.enabled", havingValue = "true")
|
||||
public ClientRegistrationRepository clientRegistrationRepository()
|
||||
throws NoProviderFoundException {
|
||||
<span class="nc" id="L59"> List<ClientRegistration> registrations = new ArrayList<>();</span>
|
||||
<span class="nc" id="L60"> githubClientRegistration().ifPresent(registrations::add);</span>
|
||||
<span class="nc" id="L61"> oidcClientRegistration().ifPresent(registrations::add);</span>
|
||||
<span class="nc" id="L62"> googleClientRegistration().ifPresent(registrations::add);</span>
|
||||
<span class="nc" id="L63"> keycloakClientRegistration().ifPresent(registrations::add);</span>
|
||||
|
||||
<span class="nc bnc" id="L65" title="All 2 branches missed."> if (registrations.isEmpty()) {</span>
|
||||
<span class="nc" id="L66"> log.error("No OAuth2 provider registered");</span>
|
||||
<span class="nc" id="L67"> throw new NoProviderFoundException("At least one OAuth2 provider must be configured.");</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L70"> return new InMemoryClientRegistrationRepository(registrations);</span>
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> keycloakClientRegistration() {
|
||||
<span class="nc" id="L74"> OAUTH2 oauth2 = applicationProperties.getSecurity().getOauth2();</span>
|
||||
|
||||
<span class="nc bnc" id="L76" title="All 4 branches missed."> if (isOAuth2Enabled(oauth2) || isClientInitialised(oauth2)) {</span>
|
||||
<span class="nc" id="L77"> return Optional.empty();</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L80"> Client client = oauth2.getClient();</span>
|
||||
<span class="nc" id="L81"> KeycloakProvider keycloakClient = client.getKeycloak();</span>
|
||||
<span class="nc" id="L82"> Provider keycloak =</span>
|
||||
new KeycloakProvider(
|
||||
<span class="nc" id="L84"> keycloakClient.getIssuer(),</span>
|
||||
<span class="nc" id="L85"> keycloakClient.getClientId(),</span>
|
||||
<span class="nc" id="L86"> keycloakClient.getClientSecret(),</span>
|
||||
<span class="nc" id="L87"> keycloakClient.getScopes(),</span>
|
||||
<span class="nc" id="L88"> keycloakClient.getUseAsUsername());</span>
|
||||
|
||||
<span class="nc bnc" id="L90" title="All 2 branches missed."> return validateProvider(keycloak)</span>
|
||||
<span class="nc" id="L91"> ? Optional.of(</span>
|
||||
<span class="nc" id="L92"> ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())</span>
|
||||
<span class="nc" id="L93"> .registrationId(keycloak.getName())</span>
|
||||
<span class="nc" id="L94"> .clientId(keycloak.getClientId())</span>
|
||||
<span class="nc" id="L95"> .clientSecret(keycloak.getClientSecret())</span>
|
||||
<span class="nc" id="L96"> .scope(keycloak.getScopes())</span>
|
||||
<span class="nc" id="L97"> .userNameAttributeName(keycloak.getUseAsUsername().getName())</span>
|
||||
<span class="nc" id="L98"> .clientName(keycloak.getClientName())</span>
|
||||
<span class="nc" id="L99"> .build())</span>
|
||||
<span class="nc" id="L100"> : Optional.empty();</span>
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> googleClientRegistration() {
|
||||
<span class="nc" id="L104"> OAUTH2 oAuth2 = applicationProperties.getSecurity().getOauth2();</span>
|
||||
|
||||
<span class="nc bnc" id="L106" title="All 4 branches missed."> if (isOAuth2Enabled(oAuth2) || isClientInitialised(oAuth2)) {</span>
|
||||
<span class="nc" id="L107"> return Optional.empty();</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L110"> Client client = oAuth2.getClient();</span>
|
||||
<span class="nc" id="L111"> GoogleProvider googleClient = client.getGoogle();</span>
|
||||
<span class="nc" id="L112"> Provider google =</span>
|
||||
new GoogleProvider(
|
||||
<span class="nc" id="L114"> googleClient.getClientId(),</span>
|
||||
<span class="nc" id="L115"> googleClient.getClientSecret(),</span>
|
||||
<span class="nc" id="L116"> googleClient.getScopes(),</span>
|
||||
<span class="nc" id="L117"> googleClient.getUseAsUsername());</span>
|
||||
|
||||
<span class="nc bnc" id="L119" title="All 2 branches missed."> return validateProvider(google)</span>
|
||||
<span class="nc" id="L120"> ? Optional.of(</span>
|
||||
<span class="nc" id="L121"> ClientRegistration.withRegistrationId(google.getName())</span>
|
||||
<span class="nc" id="L122"> .clientId(google.getClientId())</span>
|
||||
<span class="nc" id="L123"> .clientSecret(google.getClientSecret())</span>
|
||||
<span class="nc" id="L124"> .scope(google.getScopes())</span>
|
||||
<span class="nc" id="L125"> .authorizationUri(google.getAuthorizationUri())</span>
|
||||
<span class="nc" id="L126"> .tokenUri(google.getTokenUri())</span>
|
||||
<span class="nc" id="L127"> .userInfoUri(google.getUserInfoUri())</span>
|
||||
<span class="nc" id="L128"> .userNameAttributeName(google.getUseAsUsername().getName())</span>
|
||||
<span class="nc" id="L129"> .clientName(google.getClientName())</span>
|
||||
<span class="nc" id="L130"> .redirectUri(REDIRECT_URI_PATH + google.getName())</span>
|
||||
<span class="nc" id="L131"> .authorizationGrantType(AUTHORIZATION_CODE)</span>
|
||||
<span class="nc" id="L132"> .build())</span>
|
||||
<span class="nc" id="L133"> : Optional.empty();</span>
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> githubClientRegistration() {
|
||||
<span class="nc" id="L137"> OAUTH2 oAuth2 = applicationProperties.getSecurity().getOauth2();</span>
|
||||
|
||||
<span class="nc bnc" id="L139" title="All 2 branches missed."> if (isOAuth2Enabled(oAuth2)) {</span>
|
||||
<span class="nc" id="L140"> return Optional.empty();</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L143"> Client client = oAuth2.getClient();</span>
|
||||
<span class="nc" id="L144"> GitHubProvider githubClient = client.getGithub();</span>
|
||||
<span class="nc" id="L145"> Provider github =</span>
|
||||
new GitHubProvider(
|
||||
<span class="nc" id="L147"> githubClient.getClientId(),</span>
|
||||
<span class="nc" id="L148"> githubClient.getClientSecret(),</span>
|
||||
<span class="nc" id="L149"> githubClient.getScopes(),</span>
|
||||
<span class="nc" id="L150"> githubClient.getUseAsUsername());</span>
|
||||
|
||||
<span class="nc bnc" id="L152" title="All 2 branches missed."> return validateProvider(github)</span>
|
||||
<span class="nc" id="L153"> ? Optional.of(</span>
|
||||
<span class="nc" id="L154"> ClientRegistration.withRegistrationId(github.getName())</span>
|
||||
<span class="nc" id="L155"> .clientId(github.getClientId())</span>
|
||||
<span class="nc" id="L156"> .clientSecret(github.getClientSecret())</span>
|
||||
<span class="nc" id="L157"> .scope(github.getScopes())</span>
|
||||
<span class="nc" id="L158"> .authorizationUri(github.getAuthorizationUri())</span>
|
||||
<span class="nc" id="L159"> .tokenUri(github.getTokenUri())</span>
|
||||
<span class="nc" id="L160"> .userInfoUri(github.getUserInfoUri())</span>
|
||||
<span class="nc" id="L161"> .userNameAttributeName(github.getUseAsUsername().getName())</span>
|
||||
<span class="nc" id="L162"> .clientName(github.getClientName())</span>
|
||||
<span class="nc" id="L163"> .redirectUri(REDIRECT_URI_PATH + github.getName())</span>
|
||||
<span class="nc" id="L164"> .authorizationGrantType(AUTHORIZATION_CODE)</span>
|
||||
<span class="nc" id="L165"> .build())</span>
|
||||
<span class="nc" id="L166"> : Optional.empty();</span>
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> oidcClientRegistration() {
|
||||
<span class="nc" id="L170"> OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();</span>
|
||||
|
||||
<span class="nc bnc" id="L172" title="All 4 branches missed."> if (isOAuth2Enabled(oauth) || isClientInitialised(oauth)) {</span>
|
||||
<span class="nc" id="L173"> return Optional.empty();</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L176"> String name = oauth.getProvider();</span>
|
||||
<span class="nc" id="L177"> String firstChar = String.valueOf(name.charAt(0));</span>
|
||||
<span class="nc" id="L178"> String clientName = name.replaceFirst(firstChar, firstChar.toUpperCase());</span>
|
||||
|
||||
<span class="nc" id="L180"> Provider oidcProvider =</span>
|
||||
new Provider(
|
||||
<span class="nc" id="L182"> oauth.getIssuer(),</span>
|
||||
name,
|
||||
clientName,
|
||||
<span class="nc" id="L185"> oauth.getClientId(),</span>
|
||||
<span class="nc" id="L186"> oauth.getClientSecret(),</span>
|
||||
<span class="nc" id="L187"> oauth.getScopes(),</span>
|
||||
<span class="nc" id="L188"> UsernameAttribute.valueOf(oauth.getUseAsUsername().toUpperCase()),</span>
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
<span class="nc bnc" id="L193" title="All 4 branches missed."> return !isStringEmpty(oidcProvider.getIssuer()) || validateProvider(oidcProvider)</span>
|
||||
<span class="nc" id="L194"> ? Optional.of(</span>
|
||||
<span class="nc" id="L195"> ClientRegistrations.fromIssuerLocation(oauth.getIssuer())</span>
|
||||
<span class="nc" id="L196"> .registrationId(name)</span>
|
||||
<span class="nc" id="L197"> .clientId(oidcProvider.getClientId())</span>
|
||||
<span class="nc" id="L198"> .clientSecret(oidcProvider.getClientSecret())</span>
|
||||
<span class="nc" id="L199"> .scope(oidcProvider.getScopes())</span>
|
||||
<span class="nc" id="L200"> .userNameAttributeName(oidcProvider.getUseAsUsername().getName())</span>
|
||||
<span class="nc" id="L201"> .clientName(clientName)</span>
|
||||
<span class="nc" id="L202"> .redirectUri(REDIRECT_URI_PATH + "oidc")</span>
|
||||
<span class="nc" id="L203"> .authorizationGrantType(AUTHORIZATION_CODE)</span>
|
||||
<span class="nc" id="L204"> .build())</span>
|
||||
<span class="nc" id="L205"> : Optional.empty();</span>
|
||||
}
|
||||
|
||||
private boolean isOAuth2Enabled(OAUTH2 oAuth2) {
|
||||
<span class="nc bnc" id="L209" title="All 4 branches missed."> return oAuth2 == null || !oAuth2.getEnabled();</span>
|
||||
}
|
||||
|
||||
private boolean isClientInitialised(OAUTH2 oauth2) {
|
||||
<span class="nc" id="L213"> Client client = oauth2.getClient();</span>
|
||||
<span class="nc bnc" id="L214" title="All 2 branches missed."> return client == null;</span>
|
||||
}
|
||||
|
||||
/*
|
||||
This following function is to grant Authorities to the OAUTH2 user from the values stored in the database.
|
||||
This is required for the internal; 'hasRole()' function to give out the correct role.
|
||||
*/
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "security.oauth2.enabled", havingValue = "true")
|
||||
GrantedAuthoritiesMapper userAuthoritiesMapper() {
|
||||
<span class="nc" id="L225"> return (authorities) -> {</span>
|
||||
<span class="nc" id="L226"> Set<GrantedAuthority> mappedAuthorities = new HashSet<>();</span>
|
||||
<span class="nc" id="L227"> authorities.forEach(</span>
|
||||
authority -> {
|
||||
// Add existing OAUTH2 Authorities
|
||||
<span class="nc" id="L230"> mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));</span>
|
||||
// Add Authorities from database for existing user, if user is present.
|
||||
<span class="nc bnc" id="L232" title="All 2 branches missed."> if (authority instanceof OAuth2UserAuthority oAuth2Auth) {</span>
|
||||
<span class="nc" id="L233"> String useAsUsername =</span>
|
||||
applicationProperties
|
||||
<span class="nc" id="L235"> .getSecurity()</span>
|
||||
<span class="nc" id="L236"> .getOauth2()</span>
|
||||
<span class="nc" id="L237"> .getUseAsUsername();</span>
|
||||
<span class="nc" id="L238"> Optional<User> userOpt =</span>
|
||||
<span class="nc" id="L239"> userService.findByUsernameIgnoreCase(</span>
|
||||
<span class="nc" id="L240"> (String) oAuth2Auth.getAttributes().get(useAsUsername));</span>
|
||||
<span class="nc bnc" id="L241" title="All 2 branches missed."> if (userOpt.isPresent()) {</span>
|
||||
<span class="nc" id="L242"> User user = userOpt.get();</span>
|
||||
<span class="nc" id="L243"> mappedAuthorities.add(</span>
|
||||
new SimpleGrantedAuthority(
|
||||
<span class="nc" id="L245"> userService.findRole(user).getAuthority()));</span>
|
||||
}
|
||||
}
|
||||
<span class="nc" id="L248"> });</span>
|
||||
<span class="nc" id="L249"> return mappedAuthorities;</span>
|
||||
};
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CertificateUtils</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.config.security.saml2</a> > <span class="el_class">CertificateUtils</span></div><h1>CertificateUtils</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">90 of 90</td><td class="ctr2">0%</td><td class="bar">6 of 6</td><td class="ctr2">0%</td><td class="ctr1">6</td><td class="ctr2">6</td><td class="ctr1">18</td><td class="ctr2">18</td><td class="ctr1">3</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a2"><a href="CertificateUtils.java.html#L35" class="el_method">readPrivateKey(Resource)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="58" alt="58"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="6" alt="6"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">4</td><td class="ctr2" id="g0">4</td><td class="ctr1" id="h0">11</td><td class="ctr2" id="i0">11</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="CertificateUtils.java.html#L23" class="el_method">readCertificate(Resource)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="60" height="10" title="29" alt="29"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">6</td><td class="ctr2" id="i1">6</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a0"><a href="CertificateUtils.java.html#L20" class="el_method">CertificateUtils()</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="6" height="10" title="3" alt="3"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">1</td><td class="ctr2" id="i2">1</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CertificateUtils.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.saml2</a> > <span class="el_source">CertificateUtils.java</span></div><h1>CertificateUtils.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.saml2;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
|
||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import org.bouncycastle.openssl.PEMKeyPair;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||
import org.bouncycastle.util.io.pem.PemObject;
|
||||
import org.bouncycastle.util.io.pem.PemReader;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
|
||||
<span class="nc" id="L20">public class CertificateUtils {</span>
|
||||
|
||||
public static X509Certificate readCertificate(Resource certificateResource) throws Exception {
|
||||
<span class="nc" id="L23"> try (PemReader pemReader =</span>
|
||||
new PemReader(
|
||||
new InputStreamReader(
|
||||
<span class="nc" id="L26"> certificateResource.getInputStream(), StandardCharsets.UTF_8))) {</span>
|
||||
<span class="nc" id="L27"> PemObject pemObject = pemReader.readPemObject();</span>
|
||||
<span class="nc" id="L28"> byte[] decodedCert = pemObject.getContent();</span>
|
||||
<span class="nc" id="L29"> CertificateFactory cf = CertificateFactory.getInstance("X.509");</span>
|
||||
<span class="nc" id="L30"> return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(decodedCert));</span>
|
||||
}
|
||||
}
|
||||
|
||||
public static RSAPrivateKey readPrivateKey(Resource privateKeyResource) throws Exception {
|
||||
<span class="nc" id="L35"> try (PEMParser pemParser =</span>
|
||||
new PEMParser(
|
||||
new InputStreamReader(
|
||||
<span class="nc" id="L38"> privateKeyResource.getInputStream(), StandardCharsets.UTF_8))) {</span>
|
||||
|
||||
<span class="nc" id="L40"> Object object = pemParser.readObject();</span>
|
||||
<span class="nc" id="L41"> JcaPEMKeyConverter converter = new JcaPEMKeyConverter();</span>
|
||||
|
||||
<span class="nc bnc" id="L43" title="All 2 branches missed."> if (object instanceof PEMKeyPair keypair) {</span>
|
||||
// Handle traditional RSA private key format
|
||||
<span class="nc" id="L45"> return (RSAPrivateKey) converter.getPrivateKey(keypair.getPrivateKeyInfo());</span>
|
||||
<span class="nc bnc" id="L46" title="All 2 branches missed."> } else if (object instanceof PrivateKeyInfo keyInfo) {</span>
|
||||
// Handle PKCS#8 format
|
||||
<span class="nc" id="L48"> return (RSAPrivateKey) converter.getPrivateKey(keyInfo);</span>
|
||||
} else {
|
||||
<span class="nc" id="L50"> throw new IllegalArgumentException(</span>
|
||||
"Unsupported key format: "
|
||||
<span class="nc bnc" id="L52" title="All 2 branches missed."> + (object != null ? object.getClass().getName() : "null"));</span>
|
||||
}
|
||||
<span class="nc" id="L54"> }</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomSaml2AuthenticatedPrincipal</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.config.security.saml2</a> > <span class="el_class">CustomSaml2AuthenticatedPrincipal</span></div><h1>CustomSaml2AuthenticatedPrincipal</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">21 of 21</td><td class="ctr2">0%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">3</td><td class="ctr2">3</td><td class="ctr1">3</td><td class="ctr2">3</td><td class="ctr1">3</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a0"><a href="CustomSaml2AuthenticatedPrincipal.java.html#L11" class="el_method">CustomSaml2AuthenticatedPrincipal(String, Map, String, List)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="15" alt="15"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">1</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h0">1</td><td class="ctr2" id="i0">1</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a2"><a href="CustomSaml2AuthenticatedPrincipal.java.html#L20" class="el_method">getName()</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="24" height="10" title="3" alt="3"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">1</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a1"><a href="CustomSaml2AuthenticatedPrincipal.java.html#L25" class="el_method">getAttributes()</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="24" height="10" title="3" alt="3"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">1</td><td class="ctr2" id="i2">1</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomSaml2AuthenticatedPrincipal.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.saml2</a> > <span class="el_source">CustomSaml2AuthenticatedPrincipal.java</span></div><h1>CustomSaml2AuthenticatedPrincipal.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.saml2;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||
|
||||
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
|
||||
<span class="nc" id="L11">public record CustomSaml2AuthenticatedPrincipal(</span>
|
||||
String name,
|
||||
Map<String, List<Object>> attributes,
|
||||
String nameId,
|
||||
List<String> sessionIndexes)
|
||||
implements Saml2AuthenticatedPrincipal, Serializable {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
<span class="nc" id="L20"> return this.name;</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Object>> getAttributes() {
|
||||
<span class="nc" id="L25"> return this.attributes;</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomSaml2AuthenticationFailureHandler</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.config.security.saml2</a> > <span class="el_class">CustomSaml2AuthenticationFailureHandler</span></div><h1>CustomSaml2AuthenticationFailureHandler</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">37 of 37</td><td class="ctr2">0%</td><td class="bar">4 of 4</td><td class="ctr2">0%</td><td class="ctr1">5</td><td class="ctr2">5</td><td class="ctr1">11</td><td class="ctr2">11</td><td class="ctr1">3</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a1"><a href="CustomSaml2AuthenticationFailureHandler.java.html#L27" class="el_method">onAuthenticationFailure(HttpServletRequest, HttpServletResponse, AuthenticationException)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="30" alt="30"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="4" alt="4"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">3</td><td class="ctr2" id="g0">3</td><td class="ctr1" id="h0">9</td><td class="ctr2" id="i0">9</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a2"><a href="CustomSaml2AuthenticationFailureHandler.java.html#L17" class="el_method">static {...}</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="16" height="10" title="4" alt="4"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">1</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a0"><a href="CustomSaml2AuthenticationFailureHandler.java.html#L19" class="el_method">CustomSaml2AuthenticationFailureHandler()</a></td><td class="bar" id="b2"><img src="../jacoco-resources/redbar.gif" width="12" height="10" title="3" alt="3"/></td><td class="ctr2" id="c2">0%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">1</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">1</td><td class="ctr2" id="i2">1</td><td class="ctr1" id="j2">1</td><td class="ctr2" id="k2">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomSaml2AuthenticationFailureHandler.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.saml2</a> > <span class="el_source">CustomSaml2AuthenticationFailureHandler.java</span></div><h1>CustomSaml2AuthenticationFailureHandler.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.saml2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.security.authentication.ProviderNotFoundException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.saml2.core.Saml2Error;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
<span class="nc" id="L17">@Slf4j</span>
|
||||
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
|
||||
<span class="nc" id="L19">public class CustomSaml2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {</span>
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AuthenticationException exception)
|
||||
throws IOException {
|
||||
<span class="nc" id="L27"> log.error("Authentication error", exception);</span>
|
||||
|
||||
<span class="nc bnc" id="L29" title="All 2 branches missed."> if (exception instanceof Saml2AuthenticationException) {</span>
|
||||
<span class="nc" id="L30"> Saml2Error error = ((Saml2AuthenticationException) exception).getSaml2Error();</span>
|
||||
<span class="nc" id="L31"> getRedirectStrategy()</span>
|
||||
<span class="nc" id="L32"> .sendRedirect(request, response, "/login?errorOAuth=" + error.getErrorCode());</span>
|
||||
<span class="nc bnc" id="L33" title="All 2 branches missed."> } else if (exception instanceof ProviderNotFoundException) {</span>
|
||||
<span class="nc" id="L34"> getRedirectStrategy()</span>
|
||||
<span class="nc" id="L35"> .sendRedirect(</span>
|
||||
request,
|
||||
response,
|
||||
"/login?errorOAuth=not_authentication_provider_found");
|
||||
}
|
||||
<span class="nc" id="L40"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomSaml2AuthenticationSuccessHandler</title><script type="text/javascript" src="../jacoco-resources/sort.js"></script></head><body onload="initialSort(['breadcrumb'])"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.html" class="el_package">stirling.software.SPDF.config.security.saml2</a> > <span class="el_class">CustomSaml2AuthenticationSuccessHandler</span></div><h1>CustomSaml2AuthenticationSuccessHandler</h1><table class="coverage" cellspacing="0" id="coveragetable"><thead><tr><td class="sortable" id="a" onclick="toggleSort(this)">Element</td><td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed Instructions</td><td class="sortable ctr2" id="c" onclick="toggleSort(this)">Cov.</td><td class="sortable bar" id="d" onclick="toggleSort(this)">Missed Branches</td><td class="sortable ctr2" id="e" onclick="toggleSort(this)">Cov.</td><td class="sortable ctr1" id="f" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="g" onclick="toggleSort(this)">Cxty</td><td class="sortable ctr1" id="h" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="i" onclick="toggleSort(this)">Lines</td><td class="sortable ctr1" id="j" onclick="toggleSort(this)">Missed</td><td class="sortable ctr2" id="k" onclick="toggleSort(this)">Methods</td></tr></thead><tfoot><tr><td>Total</td><td class="bar">222 of 222</td><td class="ctr2">0%</td><td class="bar">36 of 36</td><td class="ctr2">0%</td><td class="ctr1">20</td><td class="ctr2">20</td><td class="ctr1">56</td><td class="ctr2">56</td><td class="ctr1">2</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a0"><a href="CustomSaml2AuthenticationSuccessHandler.java.html#L41" class="el_method">onAuthenticationSuccess(HttpServletRequest, HttpServletResponse, Authentication)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="218" alt="218"/></td><td class="ctr2" id="c0">0%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="120" height="10" title="36" alt="36"/></td><td class="ctr2" id="e0">0%</td><td class="ctr1" id="f0">19</td><td class="ctr2" id="g0">19</td><td class="ctr1" id="h0">55</td><td class="ctr2" id="i0">55</td><td class="ctr1" id="j0">1</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="CustomSaml2AuthenticationSuccessHandler.java.html#L28" class="el_method">static {...}</a></td><td class="bar" id="b1"><img src="../jacoco-resources/redbar.gif" width="2" height="10" title="4" alt="4"/></td><td class="ctr2" id="c1">0%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">1</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">1</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j1">1</td><td class="ctr2" id="k1">1</td></tr></tbody></table><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomSaml2AuthenticationSuccessHandler.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.saml2</a> > <span class="el_source">CustomSaml2AuthenticationSuccessHandler.java</span></div><h1>CustomSaml2AuthenticationSuccessHandler.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.saml2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.config.security.LoginAttemptService;
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
||||
import stirling.software.SPDF.model.AuthenticationType;
|
||||
import stirling.software.SPDF.model.exception.UnsupportedProviderException;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
@AllArgsConstructor
|
||||
<span class="nc" id="L28">@Slf4j</span>
|
||||
public class CustomSaml2AuthenticationSuccessHandler
|
||||
extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||
|
||||
private LoginAttemptService loginAttemptService;
|
||||
private ApplicationProperties applicationProperties;
|
||||
private UserService userService;
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(
|
||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||
throws ServletException, IOException {
|
||||
|
||||
<span class="nc" id="L41"> Object principal = authentication.getPrincipal();</span>
|
||||
<span class="nc" id="L42"> log.debug("Starting SAML2 authentication success handling");</span>
|
||||
|
||||
<span class="nc bnc" id="L44" title="All 2 branches missed."> if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2Principal) {</span>
|
||||
<span class="nc" id="L45"> String username = saml2Principal.name();</span>
|
||||
<span class="nc" id="L46"> log.debug("Authenticated principal found for user: {}", username);</span>
|
||||
|
||||
<span class="nc" id="L48"> HttpSession session = request.getSession(false);</span>
|
||||
<span class="nc" id="L49"> String contextPath = request.getContextPath();</span>
|
||||
SavedRequest savedRequest =
|
||||
<span class="nc bnc" id="L51" title="All 2 branches missed."> (session != null)</span>
|
||||
<span class="nc" id="L52"> ? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")</span>
|
||||
<span class="nc" id="L53"> : null;</span>
|
||||
|
||||
<span class="nc bnc" id="L55" title="All 2 branches missed."> log.debug(</span>
|
||||
"Session exists: {}, Saved request exists: {}",
|
||||
<span class="nc bnc" id="L57" title="All 2 branches missed."> session != null,</span>
|
||||
<span class="nc" id="L58"> savedRequest != null);</span>
|
||||
|
||||
<span class="nc bnc" id="L60" title="All 2 branches missed."> if (savedRequest != null</span>
|
||||
<span class="nc bnc" id="L61" title="All 2 branches missed."> && !RequestUriUtils.isStaticResource(</span>
|
||||
<span class="nc" id="L62"> contextPath, savedRequest.getRedirectUrl())) {</span>
|
||||
<span class="nc" id="L63"> log.debug(</span>
|
||||
"Valid saved request found, redirecting to original destination: {}",
|
||||
<span class="nc" id="L65"> savedRequest.getRedirectUrl());</span>
|
||||
<span class="nc" id="L66"> super.onAuthenticationSuccess(request, response, authentication);</span>
|
||||
} else {
|
||||
<span class="nc" id="L68"> SAML2 saml2 = applicationProperties.getSecurity().getSaml2();</span>
|
||||
<span class="nc" id="L69"> log.debug(</span>
|
||||
"Processing SAML2 authentication with autoCreateUser: {}",
|
||||
<span class="nc" id="L71"> saml2.getAutoCreateUser());</span>
|
||||
|
||||
<span class="nc bnc" id="L73" title="All 2 branches missed."> if (loginAttemptService.isBlocked(username)) {</span>
|
||||
<span class="nc" id="L74"> log.debug("User {} is blocked due to too many login attempts", username);</span>
|
||||
<span class="nc bnc" id="L75" title="All 2 branches missed."> if (session != null) {</span>
|
||||
<span class="nc" id="L76"> session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");</span>
|
||||
}
|
||||
<span class="nc" id="L78"> throw new LockedException(</span>
|
||||
"Your account has been locked due to too many failed login attempts.");
|
||||
}
|
||||
|
||||
<span class="nc" id="L82"> boolean userExists = userService.usernameExistsIgnoreCase(username);</span>
|
||||
<span class="nc bnc" id="L83" title="All 4 branches missed."> boolean hasPassword = userExists && userService.hasPassword(username);</span>
|
||||
<span class="nc bnc" id="L84" title="All 2 branches missed."> boolean isSSOUser =</span>
|
||||
userExists
|
||||
<span class="nc bnc" id="L86" title="All 2 branches missed."> && userService.isAuthenticationTypeByUsername(</span>
|
||||
username, AuthenticationType.SSO);
|
||||
|
||||
<span class="nc" id="L89"> log.debug(</span>
|
||||
"User status - Exists: {}, Has password: {}, Is SSO user: {}",
|
||||
<span class="nc" id="L91"> userExists,</span>
|
||||
<span class="nc" id="L92"> hasPassword,</span>
|
||||
<span class="nc" id="L93"> isSSOUser);</span>
|
||||
|
||||
<span class="nc bnc" id="L95" title="All 8 branches missed."> if (userExists && hasPassword && !isSSOUser && saml2.getAutoCreateUser()) {</span>
|
||||
<span class="nc" id="L96"> log.debug(</span>
|
||||
"User {} exists with password but is not SSO user, redirecting to logout",
|
||||
username);
|
||||
<span class="nc" id="L99"> response.sendRedirect(</span>
|
||||
contextPath + "/logout?oAuth2AuthenticationErrorWeb=true");
|
||||
<span class="nc" id="L101"> return;</span>
|
||||
}
|
||||
|
||||
try {
|
||||
<span class="nc bnc" id="L105" title="All 4 branches missed."> if (saml2.getBlockRegistration() && !userExists) {</span>
|
||||
<span class="nc" id="L106"> log.debug("Registration blocked for new user: {}", username);</span>
|
||||
<span class="nc" id="L107"> response.sendRedirect(</span>
|
||||
contextPath + "/login?errorOAuth=oAuth2AdminBlockedUser");
|
||||
<span class="nc" id="L109"> return;</span>
|
||||
}
|
||||
<span class="nc" id="L111"> log.debug("Processing SSO post-login for user: {}", username);</span>
|
||||
<span class="nc" id="L112"> userService.processSSOPostLogin(username, saml2.getAutoCreateUser());</span>
|
||||
<span class="nc" id="L113"> log.debug("Successfully processed authentication for user: {}", username);</span>
|
||||
<span class="nc" id="L114"> response.sendRedirect(contextPath + "/");</span>
|
||||
<span class="nc" id="L115"> } catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {</span>
|
||||
<span class="nc" id="L116"> log.debug(</span>
|
||||
"Invalid username detected for user: {}, redirecting to logout",
|
||||
username);
|
||||
<span class="nc" id="L119"> response.sendRedirect(contextPath + "/logout?invalidUsername=true");</span>
|
||||
<span class="nc" id="L120"> }</span>
|
||||
}
|
||||
<span class="nc" id="L122"> } else {</span>
|
||||
<span class="nc" id="L123"> log.debug("Non-SAML2 principal detected, delegating to parent handler");</span>
|
||||
<span class="nc" id="L124"> super.onAuthenticationSuccess(request, response, authentication);</span>
|
||||
}
|
||||
<span class="nc" id="L126"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>CustomSaml2ResponseAuthenticationConverter.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.saml2</a> > <span class="el_source">CustomSaml2ResponseAuthenticationConverter.java</span></div><h1>CustomSaml2ResponseAuthenticationConverter.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.saml2;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.saml.saml2.core.Assertion;
|
||||
import org.opensaml.saml.saml2.core.Attribute;
|
||||
import org.opensaml.saml.saml2.core.AttributeStatement;
|
||||
import org.opensaml.saml.saml2.core.AuthnStatement;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.ResponseToken;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.User;
|
||||
|
||||
<span class="nc" id="L21">@Slf4j</span>
|
||||
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
|
||||
public class CustomSaml2ResponseAuthenticationConverter
|
||||
implements Converter<ResponseToken, Saml2Authentication> {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
<span class="nc" id="L28"> public CustomSaml2ResponseAuthenticationConverter(UserService userService) {</span>
|
||||
<span class="nc" id="L29"> this.userService = userService;</span>
|
||||
<span class="nc" id="L30"> }</span>
|
||||
|
||||
private Map<String, List<Object>> extractAttributes(Assertion assertion) {
|
||||
<span class="nc" id="L33"> Map<String, List<Object>> attributes = new HashMap<>();</span>
|
||||
|
||||
<span class="nc bnc" id="L35" title="All 2 branches missed."> for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) {</span>
|
||||
<span class="nc bnc" id="L36" title="All 2 branches missed."> for (Attribute attribute : attributeStatement.getAttributes()) {</span>
|
||||
<span class="nc" id="L37"> String attributeName = attribute.getName();</span>
|
||||
<span class="nc" id="L38"> List<Object> values = new ArrayList<>();</span>
|
||||
|
||||
<span class="nc bnc" id="L40" title="All 2 branches missed."> for (XMLObject xmlObject : attribute.getAttributeValues()) {</span>
|
||||
// Get the text content directly
|
||||
<span class="nc" id="L42"> String value = xmlObject.getDOM().getTextContent();</span>
|
||||
<span class="nc bnc" id="L43" title="All 4 branches missed."> if (value != null && !value.trim().isEmpty()) {</span>
|
||||
<span class="nc" id="L44"> values.add(value);</span>
|
||||
}
|
||||
<span class="nc" id="L46"> }</span>
|
||||
|
||||
<span class="nc bnc" id="L48" title="All 2 branches missed."> if (!values.isEmpty()) {</span>
|
||||
// Store with both full URI and last part of the URI
|
||||
<span class="nc" id="L50"> attributes.put(attributeName, values);</span>
|
||||
<span class="nc" id="L51"> String shortName = attributeName.substring(attributeName.lastIndexOf('/') + 1);</span>
|
||||
<span class="nc" id="L52"> attributes.put(shortName, values);</span>
|
||||
}
|
||||
<span class="nc" id="L54"> }</span>
|
||||
<span class="nc" id="L55"> }</span>
|
||||
|
||||
<span class="nc" id="L57"> return attributes;</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public Saml2Authentication convert(ResponseToken responseToken) {
|
||||
<span class="nc" id="L62"> Assertion assertion = responseToken.getResponse().getAssertions().get(0);</span>
|
||||
<span class="nc" id="L63"> Map<String, List<Object>> attributes = extractAttributes(assertion);</span>
|
||||
|
||||
// Debug log with actual values
|
||||
<span class="nc" id="L66"> log.debug("Extracted SAML Attributes: {}", attributes);</span>
|
||||
|
||||
// Try to get username/identifier in order of preference
|
||||
String userIdentifier;
|
||||
<span class="nc bnc" id="L70" title="All 2 branches missed."> if (hasAttribute(attributes, "username")) {</span>
|
||||
<span class="nc" id="L71"> userIdentifier = getFirstAttributeValue(attributes, "username");</span>
|
||||
<span class="nc bnc" id="L72" title="All 2 branches missed."> } else if (hasAttribute(attributes, "emailaddress")) {</span>
|
||||
<span class="nc" id="L73"> userIdentifier = getFirstAttributeValue(attributes, "emailaddress");</span>
|
||||
<span class="nc bnc" id="L74" title="All 2 branches missed."> } else if (hasAttribute(attributes, "name")) {</span>
|
||||
<span class="nc" id="L75"> userIdentifier = getFirstAttributeValue(attributes, "name");</span>
|
||||
<span class="nc bnc" id="L76" title="All 2 branches missed."> } else if (hasAttribute(attributes, "upn")) {</span>
|
||||
<span class="nc" id="L77"> userIdentifier = getFirstAttributeValue(attributes, "upn");</span>
|
||||
<span class="nc bnc" id="L78" title="All 2 branches missed."> } else if (hasAttribute(attributes, "uid")) {</span>
|
||||
<span class="nc" id="L79"> userIdentifier = getFirstAttributeValue(attributes, "uid");</span>
|
||||
} else {
|
||||
<span class="nc" id="L81"> userIdentifier = assertion.getSubject().getNameID().getValue();</span>
|
||||
}
|
||||
|
||||
// Rest of your existing code...
|
||||
<span class="nc" id="L85"> Optional<User> userOpt = userService.findByUsernameIgnoreCase(userIdentifier);</span>
|
||||
<span class="nc" id="L86"> SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_USER");</span>
|
||||
<span class="nc bnc" id="L87" title="All 2 branches missed."> if (userOpt.isPresent()) {</span>
|
||||
<span class="nc" id="L88"> User user = userOpt.get();</span>
|
||||
<span class="nc" id="L89"> simpleGrantedAuthority =</span>
|
||||
<span class="nc" id="L90"> new SimpleGrantedAuthority(userService.findRole(user).getAuthority());</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L93"> List<String> sessionIndexes = new ArrayList<>();</span>
|
||||
<span class="nc bnc" id="L94" title="All 2 branches missed."> for (AuthnStatement authnStatement : assertion.getAuthnStatements()) {</span>
|
||||
<span class="nc" id="L95"> sessionIndexes.add(authnStatement.getSessionIndex());</span>
|
||||
<span class="nc" id="L96"> }</span>
|
||||
|
||||
<span class="nc" id="L98"> CustomSaml2AuthenticatedPrincipal principal =</span>
|
||||
new CustomSaml2AuthenticatedPrincipal(
|
||||
userIdentifier, attributes, userIdentifier, sessionIndexes);
|
||||
|
||||
<span class="nc" id="L102"> return new Saml2Authentication(</span>
|
||||
principal,
|
||||
<span class="nc" id="L104"> responseToken.getToken().getSaml2Response(),</span>
|
||||
<span class="nc" id="L105"> List.of(simpleGrantedAuthority));</span>
|
||||
}
|
||||
|
||||
private boolean hasAttribute(Map<String, List<Object>> attributes, String name) {
|
||||
<span class="nc bnc" id="L109" title="All 4 branches missed."> return attributes.containsKey(name) && !attributes.get(name).isEmpty();</span>
|
||||
}
|
||||
|
||||
private String getFirstAttributeValue(Map<String, List<Object>> attributes, String name) {
|
||||
<span class="nc" id="L113"> List<Object> values = attributes.get(name);</span>
|
||||
<span class="nc bnc" id="L114" title="All 4 branches missed."> return values != null && !values.isEmpty() ? values.get(0).toString() : null;</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|
@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="zh"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>SAML2Configuration.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Stirling-PDF</a> > <a href="index.source.html" class="el_package">stirling.software.SPDF.config.security.saml2</a> > <span class="el_source">SAML2Configuration.java</span></div><h1>SAML2Configuration.java</h1><pre class="source lang-java linenums">package stirling.software.SPDF.config.security.saml2;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.opensaml.saml.saml2.core.AuthnRequest;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.security.saml2.core.Saml2X509Credential;
|
||||
import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;
|
||||
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
|
||||
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
|
||||
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
||||
|
||||
@Configuration
|
||||
<span class="nc" id="L30">@Slf4j</span>
|
||||
@ConditionalOnProperty(value = "security.saml2.enabled", havingValue = "true")
|
||||
public class SAML2Configuration {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
<span class="nc" id="L36"> public SAML2Configuration(ApplicationProperties applicationProperties) {</span>
|
||||
<span class="nc" id="L37"> this.applicationProperties = applicationProperties;</span>
|
||||
<span class="nc" id="L38"> }</span>
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
|
||||
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
|
||||
<span class="nc" id="L43"> SAML2 samlConf = applicationProperties.getSecurity().getSaml2();</span>
|
||||
<span class="nc" id="L44"> X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getIdpCert());</span>
|
||||
<span class="nc" id="L45"> Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);</span>
|
||||
<span class="nc" id="L46"> Resource privateKeyResource = samlConf.getPrivateKey();</span>
|
||||
<span class="nc" id="L47"> Resource certificateResource = samlConf.getSpCert();</span>
|
||||
<span class="nc" id="L48"> Saml2X509Credential signingCredential =</span>
|
||||
new Saml2X509Credential(
|
||||
<span class="nc" id="L50"> CertificateUtils.readPrivateKey(privateKeyResource),</span>
|
||||
<span class="nc" id="L51"> CertificateUtils.readCertificate(certificateResource),</span>
|
||||
Saml2X509CredentialType.SIGNING);
|
||||
<span class="nc" id="L53"> RelyingPartyRegistration rp =</span>
|
||||
<span class="nc" id="L54"> RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())</span>
|
||||
<span class="nc" id="L55"> .signingX509Credentials(c -> c.add(signingCredential))</span>
|
||||
<span class="nc" id="L56"> .entityId(samlConf.getIdpIssuer())</span>
|
||||
<span class="nc" id="L57"> .singleLogoutServiceBinding(Saml2MessageBinding.POST)</span>
|
||||
<span class="nc" id="L58"> .singleLogoutServiceLocation(samlConf.getIdpSingleLogoutUrl())</span>
|
||||
<span class="nc" id="L59"> .singleLogoutServiceResponseLocation("http://localhost:8080/login")</span>
|
||||
<span class="nc" id="L60"> .assertionConsumerServiceBinding(Saml2MessageBinding.POST)</span>
|
||||
<span class="nc" id="L61"> .assertionConsumerServiceLocation(</span>
|
||||
"{baseUrl}/login/saml2/sso/{registrationId}")
|
||||
<span class="nc" id="L63"> .assertingPartyMetadata(</span>
|
||||
metadata ->
|
||||
<span class="nc" id="L65"> metadata.entityId(samlConf.getIdpIssuer())</span>
|
||||
<span class="nc" id="L66"> .verificationX509Credentials(</span>
|
||||
<span class="nc" id="L67"> c -> c.add(verificationCredential))</span>
|
||||
<span class="nc" id="L68"> .singleSignOnServiceBinding(</span>
|
||||
Saml2MessageBinding.POST)
|
||||
<span class="nc" id="L70"> .singleSignOnServiceLocation(</span>
|
||||
<span class="nc" id="L71"> samlConf.getIdpSingleLoginUrl())</span>
|
||||
<span class="nc" id="L72"> .singleLogoutServiceBinding(</span>
|
||||
Saml2MessageBinding.POST)
|
||||
<span class="nc" id="L74"> .singleLogoutServiceLocation(</span>
|
||||
<span class="nc" id="L75"> samlConf.getIdpSingleLogoutUrl())</span>
|
||||
<span class="nc" id="L76"> .wantAuthnRequestsSigned(true))</span>
|
||||
<span class="nc" id="L77"> .build();</span>
|
||||
<span class="nc" id="L78"> return new InMemoryRelyingPartyRegistrationRepository(rp);</span>
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "security.saml2.enabled", havingValue = "true")
|
||||
public OpenSaml4AuthenticationRequestResolver authenticationRequestResolver(
|
||||
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
|
||||
<span class="nc" id="L85"> OpenSaml4AuthenticationRequestResolver resolver =</span>
|
||||
new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationRepository);
|
||||
|
||||
<span class="nc" id="L88"> resolver.setAuthnRequestCustomizer(</span>
|
||||
customizer -> {
|
||||
<span class="nc" id="L90"> HttpServletRequest request = customizer.getRequest();</span>
|
||||
<span class="nc" id="L91"> AuthnRequest authnRequest = customizer.getAuthnRequest();</span>
|
||||
<span class="nc" id="L92"> HttpSessionSaml2AuthenticationRequestRepository requestRepository =</span>
|
||||
new HttpSessionSaml2AuthenticationRequestRepository();
|
||||
<span class="nc" id="L94"> AbstractSaml2AuthenticationRequest saml2AuthenticationRequest =</span>
|
||||
<span class="nc" id="L95"> requestRepository.loadAuthenticationRequest(request);</span>
|
||||
|
||||
<span class="nc bnc" id="L97" title="All 2 branches missed."> if (saml2AuthenticationRequest != null) {</span>
|
||||
<span class="nc" id="L98"> String sessionId = request.getSession(false).getId();</span>
|
||||
|
||||
<span class="nc" id="L100"> log.debug(</span>
|
||||
"Retrieving SAML 2 authentication request ID from the current HTTP session {}",
|
||||
sessionId);
|
||||
|
||||
<span class="nc" id="L104"> String authenticationRequestId = saml2AuthenticationRequest.getId();</span>
|
||||
|
||||
<span class="nc bnc" id="L106" title="All 2 branches missed."> if (!authenticationRequestId.isBlank()) {</span>
|
||||
<span class="nc" id="L107"> authnRequest.setID(authenticationRequestId);</span>
|
||||
} else {
|
||||
<span class="nc" id="L109"> log.warn(</span>
|
||||
"No authentication request found for HTTP session {}. Generating new ID",
|
||||
sessionId);
|
||||
<span class="nc" id="L112"> authnRequest.setID("ARQ" + UUID.randomUUID().toString().substring(1));</span>
|
||||
}
|
||||
<span class="nc" id="L114"> } else {</span>
|
||||
<span class="nc" id="L115"> log.debug("Generating new authentication request ID");</span>
|
||||
<span class="nc" id="L116"> authnRequest.setID("ARQ" + UUID.randomUUID().toString().substring(1));</span>
|
||||
}
|
||||
|
||||
<span class="nc" id="L119"> logAuthnRequestDetails(authnRequest);</span>
|
||||
<span class="nc" id="L120"> logHttpRequestDetails(request);</span>
|
||||
<span class="nc" id="L121"> });</span>
|
||||
<span class="nc" id="L122"> return resolver;</span>
|
||||
}
|
||||
|
||||
private static void logAuthnRequestDetails(AuthnRequest authnRequest) {
|
||||
<span class="nc" id="L126"> String message =</span>
|
||||
"""
|
||||
AuthnRequest:
|
||||
|
||||
ID: {}
|
||||
Issuer: {}
|
||||
IssueInstant: {}
|
||||
AssertionConsumerService (ACS) URL: {}
|
||||
""";
|
||||
<span class="nc" id="L135"> log.debug(</span>
|
||||
message,
|
||||
<span class="nc" id="L137"> authnRequest.getID(),</span>
|
||||
<span class="nc bnc" id="L138" title="All 2 branches missed."> authnRequest.getIssuer() != null ? authnRequest.getIssuer().getValue() : null,</span>
|
||||
<span class="nc" id="L139"> authnRequest.getIssueInstant(),</span>
|
||||
<span class="nc" id="L140"> authnRequest.getAssertionConsumerServiceURL());</span>
|
||||
|
||||
<span class="nc bnc" id="L142" title="All 2 branches missed."> if (authnRequest.getNameIDPolicy() != null) {</span>
|
||||
<span class="nc" id="L143"> log.debug("NameIDPolicy Format: {}", authnRequest.getNameIDPolicy().getFormat());</span>
|
||||
}
|
||||
<span class="nc" id="L145"> }</span>
|
||||
|
||||
private static void logHttpRequestDetails(HttpServletRequest request) {
|
||||
<span class="nc" id="L148"> log.debug("HTTP Headers: ");</span>
|
||||
<span class="nc" id="L149"> Collections.list(request.getHeaderNames())</span>
|
||||
<span class="nc" id="L150"> .forEach(</span>
|
||||
headerName ->
|
||||
<span class="nc" id="L152"> log.debug("{}: {}", headerName, request.getHeader(headerName)));</span>
|
||||
<span class="nc" id="L153"> String message =</span>
|
||||
"""
|
||||
HTTP Request Method: {}
|
||||
Session ID: {}
|
||||
Request Path: {}
|
||||
Query String: {}
|
||||
Remote Address: {}
|
||||
|
||||
SAML Request Parameters:
|
||||
|
||||
SAMLRequest: {}
|
||||
RelayState: {}
|
||||
""";
|
||||
<span class="nc" id="L166"> log.debug(</span>
|
||||
message,
|
||||
<span class="nc" id="L168"> request.getMethod(),</span>
|
||||
<span class="nc" id="L169"> request.getSession().getId(),</span>
|
||||
<span class="nc" id="L170"> request.getRequestURI(),</span>
|
||||
<span class="nc" id="L171"> request.getQueryString(),</span>
|
||||
<span class="nc" id="L172"> request.getRemoteAddr(),</span>
|
||||
<span class="nc" id="L173"> request.getParameter("SAMLRequest"),</span>
|
||||
<span class="nc" id="L174"> request.getParameter("RelayState"));</span>
|
||||
<span class="nc" id="L175"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.12.202403310830</span></div></body></html>
|