init: 클린 기반 auth 서버 설계
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "app.docs",
|
||||
"type": "com.project.auth.config.openapi.AppDocsProperties",
|
||||
"sourceType": "com.project.auth.config.openapi.AppDocsProperties"
|
||||
},
|
||||
{
|
||||
"name": "app.logging.access",
|
||||
"type": "com.project.auth.config.web.AccessLogProperties",
|
||||
"sourceType": "com.project.auth.config.web.AccessLogProperties"
|
||||
},
|
||||
{
|
||||
"name": "app.persistence.migration",
|
||||
"type": "com.project.auth.config.persistence.MigrationProperties",
|
||||
"sourceType": "com.project.auth.config.persistence.MigrationProperties"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "app.docs.description",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "com.project.auth.config.openapi.AppDocsProperties"
|
||||
},
|
||||
{
|
||||
"name": "app.docs.title",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "com.project.auth.config.openapi.AppDocsProperties"
|
||||
},
|
||||
{
|
||||
"name": "app.docs.version",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "com.project.auth.config.openapi.AppDocsProperties"
|
||||
},
|
||||
{
|
||||
"name": "app.logging.access.excluded-path-prefixes",
|
||||
"type": "java.util.List<java.lang.String>",
|
||||
"sourceType": "com.project.auth.config.web.AccessLogProperties"
|
||||
},
|
||||
{
|
||||
"name": "app.persistence.migration.location",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "com.project.auth.config.persistence.MigrationProperties"
|
||||
},
|
||||
{
|
||||
"name": "app.persistence.migration.schema",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "com.project.auth.config.persistence.MigrationProperties"
|
||||
}
|
||||
],
|
||||
"hints": [],
|
||||
"ignored": {
|
||||
"properties": []
|
||||
}
|
||||
}
|
||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
@@ -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="en"><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>JwtTypValidator</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">bootstrap</a> > <a href="index.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_class">JwtTypValidator</span></div><h1>JwtTypValidator</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 38</td><td class="ctr2">23%</td><td class="bar">2 of 2</td><td class="ctr2">0%</td><td class="ctr1">2</td><td class="ctr2">3</td><td class="ctr1">5</td><td class="ctr2">8</td><td class="ctr1">1</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a1"><a href="JwtTypValidator.java.html#L23" class="el_method">validate(Jwt)</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="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="f0">2</td><td class="ctr2" id="g0">2</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="a0"><a href="JwtTypValidator.java.html#L17" class="el_method">JwtTypValidator(String)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/greenbar.gif" width="37" height="10" title="9" alt="9"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">0</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">0</td><td class="ctr2" id="i1">3</td><td class="ctr1" id="j1">0</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.13.202504020838</span></div></body></html>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?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="en"><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>JwtTypValidator.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_source">JwtTypValidator.java</span></div><h1>JwtTypValidator.java</h1><pre class="source lang-java linenums">package com.project.auth.config.auth.security;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class JwtTypValidator implements OAuth2TokenValidator<Jwt> {
|
||||
|
||||
private static final String TYP_CLAIM = "typ";
|
||||
private static final String ERROR_CODE = "invalid_token";
|
||||
|
||||
private final String expectedTyp;
|
||||
|
||||
<span class="fc" id="L17"> public JwtTypValidator(String expectedTyp) {</span>
|
||||
<span class="fc" id="L18"> this.expectedTyp = Objects.requireNonNull(expectedTyp, "expectedTyp must not be null");</span>
|
||||
<span class="fc" id="L19"> }</span>
|
||||
|
||||
@Override
|
||||
public OAuth2TokenValidatorResult validate(Jwt jwt) {
|
||||
<span class="nc" id="L23"> String actual = jwt.getClaimAsString(TYP_CLAIM);</span>
|
||||
<span class="nc bnc" id="L24" title="All 2 branches missed."> if (expectedTyp.equals(actual)) {</span>
|
||||
<span class="nc" id="L25"> return OAuth2TokenValidatorResult.success();</span>
|
||||
}
|
||||
<span class="nc" id="L27"> OAuth2Error error = new OAuth2Error(</span>
|
||||
ERROR_CODE,
|
||||
"Expected JWT typ=" + expectedTyp + " but got " + actual,
|
||||
null
|
||||
);
|
||||
<span class="nc" id="L32"> return OAuth2TokenValidatorResult.failure(error);</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
@@ -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="en"><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>KeycloakAuthenticatedUserFactory</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">bootstrap</a> > <a href="index.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_class">KeycloakAuthenticatedUserFactory</span></div><h1>KeycloakAuthenticatedUserFactory</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">4 of 39</td><td class="ctr2">89%</td><td class="bar">2 of 4</td><td class="ctr2">50%</td><td class="ctr1">2</td><td class="ctr2">6</td><td class="ctr1">1</td><td class="ctr2">13</td><td class="ctr1">0</td><td class="ctr2">4</td></tr></tfoot><tbody><tr><td id="a2"><a href="KeycloakAuthenticatedUserFactory.java.html#L27" class="el_method">displayName(Jwt)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="32" height="10" title="4" alt="4"/><img src="../jacoco-resources/greenbar.gif" width="88" height="10" title="11" alt="11"/></td><td class="ctr2" id="c3">73%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="60" height="10" title="2" alt="2"/><img src="../jacoco-resources/greenbar.gif" width="60" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">50%</td><td class="ctr1" id="f0">2</td><td class="ctr2" id="g0">3</td><td class="ctr1" id="h0">1</td><td class="ctr2" id="i1">4</td><td class="ctr1" id="j0">0</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="KeycloakAuthenticatedUserFactory.java.html#L18" class="el_method">create(Jwt, Collection)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/greenbar.gif" width="104" height="10" title="13" alt="13"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">0</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">0</td><td class="ctr2" id="i0">5</td><td class="ctr1" id="j1">0</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a0"><a href="KeycloakAuthenticatedUserFactory.java.html#L35" class="el_method">authorityNames(Collection)</a></td><td class="bar" id="b2"><img src="../jacoco-resources/greenbar.gif" width="64" height="10" title="8" alt="8"/></td><td class="ctr2" id="c1">100%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">0</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">0</td><td class="ctr2" id="i2">3</td><td class="ctr1" id="j2">0</td><td class="ctr2" id="k2">1</td></tr><tr><td id="a3"><a href="KeycloakAuthenticatedUserFactory.java.html#L11" class="el_method">KeycloakAuthenticatedUserFactory()</a></td><td class="bar" id="b3"><img src="../jacoco-resources/greenbar.gif" width="24" height="10" title="3" alt="3"/></td><td class="ctr2" id="c2">100%</td><td class="bar" id="d3"/><td class="ctr2" id="e3">n/a</td><td class="ctr1" id="f3">0</td><td class="ctr2" id="g3">1</td><td class="ctr1" id="h3">0</td><td class="ctr2" id="i3">1</td><td class="ctr1" id="j3">0</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.13.202504020838</span></div></body></html>
|
||||
+40
@@ -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="en"><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>KeycloakAuthenticatedUserFactory.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_source">KeycloakAuthenticatedUserFactory.java</span></div><h1>KeycloakAuthenticatedUserFactory.java</h1><pre class="source lang-java linenums">package com.project.auth.config.auth.security;
|
||||
|
||||
import com.project.auth.presentation.auth.current.AuthenticatedUser;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
<span class="fc" id="L11">public class KeycloakAuthenticatedUserFactory {</span>
|
||||
|
||||
private static final String CLAIM_EMAIL = "email";
|
||||
private static final String CLAIM_NAME = "name";
|
||||
private static final String CLAIM_PREFERRED_USERNAME = "preferred_username";
|
||||
|
||||
public AuthenticatedUser create(Jwt jwt, Collection<? extends GrantedAuthority> authorities) {
|
||||
<span class="fc" id="L18"> return new AuthenticatedUser(</span>
|
||||
<span class="fc" id="L19"> jwt.getSubject(),</span>
|
||||
<span class="fc" id="L20"> jwt.getClaimAsString(CLAIM_EMAIL),</span>
|
||||
<span class="fc" id="L21"> displayName(jwt),</span>
|
||||
<span class="fc" id="L22"> authorityNames(authorities)</span>
|
||||
);
|
||||
}
|
||||
|
||||
private static String displayName(Jwt jwt) {
|
||||
<span class="fc" id="L27"> String name = jwt.getClaimAsString(CLAIM_NAME);</span>
|
||||
<span class="pc bpc" id="L28" title="2 of 4 branches missed."> if (name != null && !name.isBlank()) {</span>
|
||||
<span class="fc" id="L29"> return name;</span>
|
||||
}
|
||||
<span class="nc" id="L31"> return jwt.getClaimAsString(CLAIM_PREFERRED_USERNAME);</span>
|
||||
}
|
||||
|
||||
private static Set<String> authorityNames(Collection<? extends GrantedAuthority> authorities) {
|
||||
<span class="fc" id="L35"> return authorities.stream()</span>
|
||||
<span class="fc" id="L36"> .map(GrantedAuthority::getAuthority)</span>
|
||||
<span class="fc" id="L37"> .collect(Collectors.toUnmodifiableSet());</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
File diff suppressed because one or more lines are too long
+50
@@ -0,0 +1,50 @@
|
||||
<?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="en"><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>KeycloakGrantedAuthoritiesConverter.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_source">KeycloakGrantedAuthoritiesConverter.java</span></div><h1>KeycloakGrantedAuthoritiesConverter.java</h1><pre class="source lang-java linenums">package com.project.auth.config.auth.security;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
<span class="fc" id="L15">public class KeycloakGrantedAuthoritiesConverter implements Converter<Jwt, Collection<GrantedAuthority>> {</span>
|
||||
|
||||
private static final String CLAIM_REALM_ACCESS = "realm_access";
|
||||
private static final String CLAIM_ROLES = "roles";
|
||||
private static final String ROLE_PREFIX = "ROLE_";
|
||||
|
||||
<span class="fc" id="L21"> private final JwtGrantedAuthoritiesConverter scopeConverter = new JwtGrantedAuthoritiesConverter();</span>
|
||||
|
||||
@Override
|
||||
public Collection<GrantedAuthority> convert(Jwt jwt) {
|
||||
<span class="fc" id="L25"> Set<GrantedAuthority> authorities = new LinkedHashSet<>(scopeConverter.convert(jwt));</span>
|
||||
<span class="fc bfc" id="L26" title="All 2 branches covered."> for (String role : realmRoles(jwt)) {</span>
|
||||
<span class="fc" id="L27"> authorities.add(new SimpleGrantedAuthority(ROLE_PREFIX + role));</span>
|
||||
<span class="fc" id="L28"> }</span>
|
||||
<span class="fc" id="L29"> return Set.copyOf(authorities);</span>
|
||||
}
|
||||
|
||||
private static List<String> realmRoles(Jwt jwt) {
|
||||
<span class="fc" id="L33"> Map<String, Object> realmAccess = jwt.getClaim(CLAIM_REALM_ACCESS);</span>
|
||||
<span class="pc bpc" id="L34" title="1 of 2 branches missed."> if (realmAccess == null) {</span>
|
||||
<span class="nc" id="L35"> return List.of();</span>
|
||||
}
|
||||
|
||||
<span class="fc" id="L38"> Object roles = realmAccess.get(CLAIM_ROLES);</span>
|
||||
<span class="pc bpc" id="L39" title="1 of 2 branches missed."> if (!(roles instanceof Collection<?> roleValues)) {</span>
|
||||
<span class="nc" id="L40"> return List.of();</span>
|
||||
}
|
||||
|
||||
<span class="fc" id="L43"> return roleValues.stream()</span>
|
||||
<span class="fc" id="L44"> .filter(String.class::isInstance)</span>
|
||||
<span class="fc" id="L45"> .map(String.class::cast)</span>
|
||||
<span class="pc bpc" id="L46" title="1 of 2 branches missed."> .filter(role -> !role.isBlank())</span>
|
||||
<span class="fc" id="L47"> .toList();</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
@@ -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="en"><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>KeycloakJwtAuthenticationConverter</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">bootstrap</a> > <a href="index.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_class">KeycloakJwtAuthenticationConverter</span></div><h1>KeycloakJwtAuthenticationConverter</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">0 of 36</td><td class="ctr2">100%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">0</td><td class="ctr2">2</td><td class="ctr1">0</td><td class="ctr2">7</td><td class="ctr1">0</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a0"><a href="KeycloakJwtAuthenticationConverter.java.html#L30" class="el_method">convert(Jwt)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="21" alt="21"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">0</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h0">0</td><td class="ctr2" id="i1">3</td><td class="ctr1" id="j0">0</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="KeycloakJwtAuthenticationConverter.java.html#L20" class="el_method">KeycloakJwtAuthenticationConverter(Converter, KeycloakAuthenticatedUserFactory)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/greenbar.gif" width="85" height="10" title="15" alt="15"/></td><td class="ctr2" id="c1">100%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">0</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">0</td><td class="ctr2" id="i0">4</td><td class="ctr1" id="j1">0</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.13.202504020838</span></div></body></html>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?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="en"><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>KeycloakJwtAuthenticationConverter.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_source">KeycloakJwtAuthenticationConverter.java</span></div><h1>KeycloakJwtAuthenticationConverter.java</h1><pre class="source lang-java linenums">package com.project.auth.config.auth.security;
|
||||
|
||||
import com.project.auth.presentation.auth.current.AuthenticatedUser;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
public class KeycloakJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
|
||||
|
||||
private final Converter<Jwt, Collection<GrantedAuthority>> authoritiesConverter;
|
||||
private final KeycloakAuthenticatedUserFactory authenticatedUserFactory;
|
||||
|
||||
public KeycloakJwtAuthenticationConverter(
|
||||
Converter<Jwt, Collection<GrantedAuthority>> authoritiesConverter,
|
||||
KeycloakAuthenticatedUserFactory authenticatedUserFactory
|
||||
<span class="fc" id="L20"> ) {</span>
|
||||
<span class="fc" id="L21"> this.authoritiesConverter = Objects.requireNonNull(authoritiesConverter, "authoritiesConverter must not be null");</span>
|
||||
<span class="fc" id="L22"> this.authenticatedUserFactory = Objects.requireNonNull(</span>
|
||||
authenticatedUserFactory,
|
||||
"authenticatedUserFactory must not be null"
|
||||
);
|
||||
<span class="fc" id="L26"> }</span>
|
||||
|
||||
@Override
|
||||
public AbstractAuthenticationToken convert(Jwt jwt) {
|
||||
<span class="fc" id="L30"> Collection<GrantedAuthority> authorities = authoritiesConverter.convert(jwt);</span>
|
||||
<span class="fc" id="L31"> AuthenticatedUser principal = authenticatedUserFactory.create(jwt, authorities);</span>
|
||||
<span class="fc" id="L32"> return new KeycloakJwtAuthenticationToken(jwt, authorities, principal, jwt.getSubject());</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
@@ -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="en"><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>KeycloakJwtAuthenticationToken</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">bootstrap</a> > <a href="index.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_class">KeycloakJwtAuthenticationToken</span></div><h1>KeycloakJwtAuthenticationToken</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">0 of 12</td><td class="ctr2">100%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">0</td><td class="ctr2">2</td><td class="ctr1">0</td><td class="ctr2">4</td><td class="ctr1">0</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a1"><a href="KeycloakJwtAuthenticationToken.java.html#L20" class="el_method">KeycloakJwtAuthenticationToken(Jwt, Collection, AuthenticatedUser, String)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="9" alt="9"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">0</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h0">0</td><td class="ctr2" id="i0">3</td><td class="ctr1" id="j0">0</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a0"><a href="KeycloakJwtAuthenticationToken.java.html#L26" class="el_method">getPrincipal()</a></td><td class="bar" id="b1"><img src="../jacoco-resources/greenbar.gif" width="40" height="10" title="3" alt="3"/></td><td class="ctr2" id="c1">100%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">0</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">0</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j1">0</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.13.202504020838</span></div></body></html>
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?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="en"><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>KeycloakJwtAuthenticationToken.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_source">KeycloakJwtAuthenticationToken.java</span></div><h1>KeycloakJwtAuthenticationToken.java</h1><pre class="source lang-java linenums">package com.project.auth.config.auth.security;
|
||||
|
||||
import com.project.auth.presentation.auth.current.AuthenticatedUser;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class KeycloakJwtAuthenticationToken extends JwtAuthenticationToken {
|
||||
|
||||
private final AuthenticatedUser principal;
|
||||
|
||||
public KeycloakJwtAuthenticationToken(
|
||||
Jwt jwt,
|
||||
Collection<? extends GrantedAuthority> authorities,
|
||||
AuthenticatedUser principal,
|
||||
String name
|
||||
) {
|
||||
<span class="fc" id="L20"> super(jwt, authorities, name);</span>
|
||||
<span class="fc" id="L21"> this.principal = principal;</span>
|
||||
<span class="fc" id="L22"> }</span>
|
||||
|
||||
@Override
|
||||
public AuthenticatedUser getPrincipal() {
|
||||
<span class="fc" id="L26"> return principal;</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
@@ -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="en"><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>SecurityActorIdResolver</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">bootstrap</a> > <a href="index.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_class">SecurityActorIdResolver</span></div><h1>SecurityActorIdResolver</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 30</td><td class="ctr2">66%</td><td class="bar">5 of 8</td><td class="ctr2">37%</td><td class="ctr1">3</td><td class="ctr2">6</td><td class="ctr1">2</td><td class="ctr2">9</td><td class="ctr1">0</td><td class="ctr2">2</td></tr></tfoot><tbody><tr><td id="a0"><a href="SecurityActorIdResolver.java.html#L16" class="el_method">resolve(HttpServletRequest)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="44" height="10" title="10" alt="10"/><img src="../jacoco-resources/greenbar.gif" width="75" height="10" title="17" alt="17"/></td><td class="ctr2" id="c1">62%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="75" height="10" title="5" alt="5"/><img src="../jacoco-resources/greenbar.gif" width="45" height="10" title="3" alt="3"/></td><td class="ctr2" id="e0">37%</td><td class="ctr1" id="f0">3</td><td class="ctr2" id="g0">5</td><td class="ctr1" id="h0">2</td><td class="ctr2" id="i0">8</td><td class="ctr1" id="j0">0</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="SecurityActorIdResolver.java.html#L11" class="el_method">SecurityActorIdResolver()</a></td><td class="bar" id="b1"><img src="../jacoco-resources/greenbar.gif" width="13" height="10" title="3" alt="3"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">0</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">0</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j1">0</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.13.202504020838</span></div></body></html>
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?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="en"><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>SecurityActorIdResolver.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_source">SecurityActorIdResolver.java</span></div><h1>SecurityActorIdResolver.java</h1><pre class="source lang-java linenums">package com.project.auth.config.auth.security;
|
||||
|
||||
import com.project.auth.application.support.logging.LogSanitizer;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
<span class="fc" id="L11">public class SecurityActorIdResolver {</span>
|
||||
|
||||
private static final String ANONYMOUS_PRINCIPAL = "anonymous";
|
||||
|
||||
public String resolve(HttpServletRequest request) {
|
||||
<span class="fc" id="L16"> Principal principal = request.getUserPrincipal();</span>
|
||||
<span class="fc bfc" id="L17" title="All 2 branches covered."> if (principal != null) {</span>
|
||||
<span class="fc" id="L18"> return LogSanitizer.actorId(principal.getName());</span>
|
||||
}
|
||||
|
||||
<span class="fc" id="L21"> Authentication authentication = SecurityContextHolder.getContext().getAuthentication();</span>
|
||||
<span class="pc bpc" id="L22" title="1 of 2 branches missed."> if (authentication != null</span>
|
||||
<span class="nc bnc" id="L23" title="All 4 branches missed."> && authentication.isAuthenticated()</span>
|
||||
&& !(authentication instanceof AnonymousAuthenticationToken)) {
|
||||
<span class="nc" id="L25"> return LogSanitizer.actorId(authentication.getName());</span>
|
||||
}
|
||||
|
||||
<span class="fc" id="L28"> return LogSanitizer.actorId(ANONYMOUS_PRINCIPAL);</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
@@ -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="en"><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>SecurityAuditTrailWriter</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">bootstrap</a> > <a href="index.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_class">SecurityAuditTrailWriter</span></div><h1>SecurityAuditTrailWriter</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">0 of 64</td><td class="ctr2">100%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">0</td><td class="ctr2">3</td><td class="ctr1">0</td><td class="ctr2">16</td><td class="ctr1">0</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a0"><a href="SecurityAuditTrailWriter.java.html#L22" class="el_method">record(HttpServletRequest, AuthAuditEventType, String)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="51" alt="51"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">0</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h0">0</td><td class="ctr2" id="i0">11</td><td class="ctr1" id="j0">0</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a2"><a href="SecurityAuditTrailWriter.java.html#L12" class="el_method">static {...}</a></td><td class="bar" id="b1"><img src="../jacoco-resources/greenbar.gif" width="16" height="10" title="7" alt="7"/></td><td class="ctr2" id="c1">100%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">0</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">0</td><td class="ctr2" id="i2">2</td><td class="ctr1" id="j1">0</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a1"><a href="SecurityAuditTrailWriter.java.html#L17" class="el_method">SecurityAuditTrailWriter(SecurityActorIdResolver)</a></td><td class="bar" id="b2"><img src="../jacoco-resources/greenbar.gif" width="14" height="10" title="6" alt="6"/></td><td class="ctr2" id="c2">100%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">0</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">0</td><td class="ctr2" id="i1">3</td><td class="ctr1" id="j2">0</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.13.202504020838</span></div></body></html>
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?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="en"><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>SecurityAuditTrailWriter.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_source">SecurityAuditTrailWriter.java</span></div><h1>SecurityAuditTrailWriter.java</h1><pre class="source lang-java linenums">package com.project.auth.config.auth.security;
|
||||
|
||||
import com.project.auth.application.support.audit.AuthAuditEventType;
|
||||
import com.project.auth.application.support.audit.AuthAuditFields;
|
||||
import com.project.auth.application.support.logging.LogSanitizer;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SecurityAuditTrailWriter {
|
||||
|
||||
<span class="fc" id="L12"> private static final Logger log = LoggerFactory.getLogger(SecurityAuditTrailWriter.class);</span>
|
||||
<span class="fc" id="L13"> private static final Logger audit = LoggerFactory.getLogger("audit.auth");</span>
|
||||
|
||||
private final SecurityActorIdResolver actorIdResolver;
|
||||
|
||||
<span class="fc" id="L17"> public SecurityAuditTrailWriter(SecurityActorIdResolver actorIdResolver) {</span>
|
||||
<span class="fc" id="L18"> this.actorIdResolver = actorIdResolver;</span>
|
||||
<span class="fc" id="L19"> }</span>
|
||||
|
||||
public void record(HttpServletRequest request, AuthAuditEventType eventType, String message) {
|
||||
<span class="fc" id="L22"> String actorId = actorIdResolver.resolve(request);</span>
|
||||
<span class="fc" id="L23"> String requestPath = LogSanitizer.requestPath(request.getRequestURI());</span>
|
||||
<span class="fc" id="L24"> log.warn(</span>
|
||||
"{} actorId={} method={} requestPath={}",
|
||||
message,
|
||||
actorId,
|
||||
<span class="fc" id="L28"> request.getMethod(),</span>
|
||||
requestPath
|
||||
);
|
||||
<span class="fc" id="L31"> audit.atWarn()</span>
|
||||
<span class="fc" id="L32"> .addKeyValue(AuthAuditFields.EVENT_TYPE, eventType.code())</span>
|
||||
<span class="fc" id="L33"> .addKeyValue(AuthAuditFields.ACTOR_ID, actorId)</span>
|
||||
<span class="fc" id="L34"> .addKeyValue(AuthAuditFields.METHOD, request.getMethod())</span>
|
||||
<span class="fc" id="L35"> .addKeyValue(AuthAuditFields.REQUEST_PATH, requestPath)</span>
|
||||
<span class="fc" id="L36"> .log(eventType.code());</span>
|
||||
<span class="fc" id="L37"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
File diff suppressed because one or more lines are too long
+77
@@ -0,0 +1,77 @@
|
||||
<?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="en"><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>SecurityExceptionHandler.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.auth.security</a> > <span class="el_source">SecurityExceptionHandler.java</span></div><h1>SecurityExceptionHandler.java</h1><pre class="source lang-java linenums">package com.project.auth.config.auth.security;
|
||||
|
||||
import com.project.auth.application.support.audit.AuthAuditEventType;
|
||||
import com.project.auth.application.support.logging.LogSanitizer;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class SecurityExceptionHandler implements AuthenticationEntryPoint, AccessDeniedHandler {
|
||||
|
||||
<span class="fc" id="L19"> private static final Logger log = LoggerFactory.getLogger(SecurityExceptionHandler.class);</span>
|
||||
|
||||
private final HandlerExceptionResolver handlerExceptionResolver;
|
||||
private final SecurityAuditTrailWriter securityAuditTrailWriter;
|
||||
|
||||
public SecurityExceptionHandler(
|
||||
HandlerExceptionResolver handlerExceptionResolver,
|
||||
SecurityAuditTrailWriter securityAuditTrailWriter
|
||||
<span class="fc" id="L27"> ) {</span>
|
||||
<span class="fc" id="L28"> this.handlerExceptionResolver = Objects.requireNonNull(</span>
|
||||
handlerExceptionResolver,
|
||||
"handlerExceptionResolver must not be null"
|
||||
);
|
||||
<span class="fc" id="L32"> this.securityAuditTrailWriter = Objects.requireNonNull(</span>
|
||||
securityAuditTrailWriter,
|
||||
"securityAuditTrailWriter must not be null"
|
||||
);
|
||||
<span class="fc" id="L36"> }</span>
|
||||
|
||||
@Override
|
||||
public void commence(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AuthenticationException authException
|
||||
) {
|
||||
<span class="fc" id="L44"> recordAudit(request, AuthAuditEventType.AUTHENTICATION_REQUIRED, "Authentication required.");</span>
|
||||
<span class="pc bpc" id="L45" title="1 of 2 branches missed."> if (response.isCommitted()) {</span>
|
||||
<span class="nc" id="L46"> log.warn("Response already committed; cannot render authentication error body. requestPath={}",</span>
|
||||
<span class="nc" id="L47"> LogSanitizer.requestPath(request.getRequestURI()));</span>
|
||||
<span class="nc" id="L48"> return;</span>
|
||||
}
|
||||
<span class="fc" id="L50"> handlerExceptionResolver.resolveException(request, response, null, authException);</span>
|
||||
<span class="fc" id="L51"> }</span>
|
||||
|
||||
@Override
|
||||
public void handle(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AccessDeniedException accessDeniedException
|
||||
) {
|
||||
<span class="fc" id="L59"> recordAudit(request, AuthAuditEventType.ACCESS_DENIED, "Access denied.");</span>
|
||||
<span class="pc bpc" id="L60" title="1 of 2 branches missed."> if (response.isCommitted()) {</span>
|
||||
<span class="nc" id="L61"> log.warn("Response already committed; cannot render access-denied error body. requestPath={}",</span>
|
||||
<span class="nc" id="L62"> LogSanitizer.requestPath(request.getRequestURI()));</span>
|
||||
<span class="nc" id="L63"> return;</span>
|
||||
}
|
||||
<span class="fc" id="L65"> handlerExceptionResolver.resolveException(request, response, null, accessDeniedException);</span>
|
||||
<span class="fc" id="L66"> }</span>
|
||||
|
||||
private void recordAudit(HttpServletRequest request, AuthAuditEventType type, String description) {
|
||||
try {
|
||||
<span class="fc" id="L70"> securityAuditTrailWriter.record(request, type, description);</span>
|
||||
<span class="nc" id="L71"> } catch (RuntimeException auditFailure) {</span>
|
||||
<span class="nc" id="L72"> log.warn("Security audit write failed; continuing with response rendering. type={}",</span>
|
||||
type, auditFailure);
|
||||
<span class="fc" id="L74"> }</span>
|
||||
<span class="fc" id="L75"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
@@ -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="en"><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>AuthAuditLoggingConfiguration.AuthAuditEventLogListener</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">bootstrap</a> > <a href="index.html" class="el_package">com.project.auth.config.logging</a> > <span class="el_class">AuthAuditLoggingConfiguration.AuthAuditEventLogListener</span></div><h1>AuthAuditLoggingConfiguration.AuthAuditEventLogListener</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">0 of 36</td><td class="ctr2">100%</td><td class="bar">0 of 2</td><td class="ctr2">100%</td><td class="ctr1">0</td><td class="ctr2">4</td><td class="ctr1">0</td><td class="ctr2">6</td><td class="ctr1">0</td><td class="ctr2">3</td></tr></tfoot><tbody><tr><td id="a2"><a href="AuthAuditLoggingConfiguration.java.html#L35" class="el_method">onAuthAuditEvent(AuthAuditEvent)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="26" alt="26"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d0"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">100%</td><td class="ctr1" id="f0">0</td><td class="ctr2" id="g0">2</td><td class="ctr1" id="h0">0</td><td class="ctr2" id="i0">5</td><td class="ctr1" id="j0">0</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="AuthAuditLoggingConfiguration.java.html#L37" class="el_method">lambda$onAuthAuditEvent$0(LoggingEventBuilder, String, String)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/greenbar.gif" width="32" height="10" title="7" alt="7"/></td><td class="ctr2" id="c1">100%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">0</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">0</td><td class="ctr2" id="i1">1</td><td class="ctr1" id="j1">0</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a0"><a href="AuthAuditLoggingConfiguration.java.html#L31" class="el_method">AuthAuditLoggingConfiguration.AuthAuditEventLogListener()</a></td><td class="bar" id="b2"><img src="../jacoco-resources/greenbar.gif" width="13" height="10" title="3" alt="3"/></td><td class="ctr2" id="c2">100%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">0</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">0</td><td class="ctr2" id="i2">1</td><td class="ctr1" id="j2">0</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.13.202504020838</span></div></body></html>
|
||||
+42
@@ -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="en"><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>AuthAuditLoggingConfiguration.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.logging</a> > <span class="el_source">AuthAuditLoggingConfiguration.java</span></div><h1>AuthAuditLoggingConfiguration.java</h1><pre class="source lang-java linenums">package com.project.auth.config.logging;
|
||||
|
||||
import com.project.auth.application.support.audit.AuditLevel;
|
||||
import com.project.auth.application.support.audit.AuthAuditEvent;
|
||||
import com.project.auth.application.support.audit.AuthAuditEventPublisher;
|
||||
import com.project.auth.application.support.audit.AuthAuditFields;
|
||||
import com.project.auth.application.support.logging.LogSanitizer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.spi.LoggingEventBuilder;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
@Configuration
|
||||
public class AuthAuditLoggingConfiguration {
|
||||
|
||||
private static final Logger audit = LoggerFactory.getLogger("audit.auth");
|
||||
|
||||
@Bean
|
||||
public AuthAuditEventPublisher authAuditEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
return applicationEventPublisher::publishEvent;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthAuditEventLogListener authAuditEventListener() {
|
||||
return new AuthAuditEventLogListener();
|
||||
}
|
||||
|
||||
<span class="fc" id="L31"> static final class AuthAuditEventLogListener {</span>
|
||||
|
||||
@EventListener
|
||||
public void onAuthAuditEvent(AuthAuditEvent event) {
|
||||
<span class="fc bfc" id="L35" title="All 2 branches covered."> LoggingEventBuilder builder = event.level() == AuditLevel.WARN ? audit.atWarn() : audit.atInfo();</span>
|
||||
<span class="fc" id="L36"> builder.addKeyValue(AuthAuditFields.EVENT_TYPE, event.eventType());</span>
|
||||
<span class="fc" id="L37"> event.fields().forEach((key, value) -> builder.addKeyValue(key, LogSanitizer.normalize(value)));</span>
|
||||
<span class="fc" id="L38"> builder.log(event.eventType());</span>
|
||||
<span class="fc" id="L39"> }</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</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="en"><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>com.project.auth.config.logging</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">bootstrap</a> > <span class="el_package">com.project.auth.config.logging</span></div><h1>com.project.auth.config.logging</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">0 of 36</td><td class="ctr2">100%</td><td class="bar">0 of 2</td><td class="ctr2">100%</td><td class="ctr1">0</td><td class="ctr2">4</td><td class="ctr1">0</td><td class="ctr2">6</td><td class="ctr1">0</td><td class="ctr2">3</td><td class="ctr1">0</td><td class="ctr2">1</td></tr></tfoot><tbody><tr><td id="a0"><a href="AuthAuditLoggingConfiguration$AuthAuditEventLogListener.html" class="el_class">AuthAuditLoggingConfiguration.AuthAuditEventLogListener</a></td><td class="bar" id="b0"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="36" alt="36"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d0"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">100%</td><td class="ctr1" id="f0">0</td><td class="ctr2" id="g0">4</td><td class="ctr1" id="h0">0</td><td class="ctr2" id="i0">6</td><td class="ctr1" id="j0">0</td><td class="ctr2" id="k0">3</td><td class="ctr1" id="l0">0</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.13.202504020838</span></div></body></html>
|
||||
+1
@@ -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="en"><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>com.project.auth.config.logging</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">bootstrap</a> > <span class="el_package">com.project.auth.config.logging</span></div><h1>com.project.auth.config.logging</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">0 of 36</td><td class="ctr2">100%</td><td class="bar">0 of 2</td><td class="ctr2">100%</td><td class="ctr1">0</td><td class="ctr2">4</td><td class="ctr1">0</td><td class="ctr2">6</td><td class="ctr1">0</td><td class="ctr2">3</td><td class="ctr1">0</td><td class="ctr2">1</td></tr></tfoot><tbody><tr><td id="a0"><a href="AuthAuditLoggingConfiguration.java.html" class="el_source">AuthAuditLoggingConfiguration.java</a></td><td class="bar" id="b0"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="36" alt="36"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d0"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">100%</td><td class="ctr1" id="f0">0</td><td class="ctr2" id="g0">4</td><td class="ctr1" id="h0">0</td><td class="ctr2" id="i0">6</td><td class="ctr1" id="j0">0</td><td class="ctr2" id="k0">3</td><td class="ctr1" id="l0">0</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.13.202504020838</span></div></body></html>
|
||||
+1
File diff suppressed because one or more lines are too long
+115
@@ -0,0 +1,115 @@
|
||||
<?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="en"><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>ApiErrorController.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.web</a> > <span class="el_source">ApiErrorController.java</span></div><h1>ApiErrorController.java</h1><pre class="source lang-java linenums">package com.project.auth.config.web;
|
||||
|
||||
import com.project.auth.application.support.exception.ClientFacingErrorCode;
|
||||
import com.project.auth.application.support.exception.CommonErrorCode;
|
||||
import com.project.auth.application.support.logging.LogSanitizer;
|
||||
import com.project.auth.presentation.support.exception.PresentationErrorCode;
|
||||
import com.project.auth.presentation.support.response.ApiResult;
|
||||
import com.project.auth.presentation.support.response.ApiResultFactory;
|
||||
import jakarta.servlet.RequestDispatcher;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.webmvc.error.ErrorController;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* {@code @RestControllerAdvice}를 우회하는 요청(필터에서 던져진 예외,
|
||||
* {@code response.sendError(...)} 호출, 컨테이너 레벨 라우팅 실패, 다른 핸들러 내부의
|
||||
* 이중 폴트 등)에 대한 최후의 에러 렌더러.
|
||||
*
|
||||
* 컨테이너가 결정한 상태 코드는 그대로 보존하고, 본문만 {@link ApiResult} 형태로 정규화한다.
|
||||
* 5xx는 내부 분류 노출을 막기 위해 {@code COMMON-999}로 정규화하고, 4xx는
|
||||
* {@link PresentationErrorCode} 기반 코드로 매핑하여 클라이언트 SDK가
|
||||
* "요청이 잘못됐다"와 "서버가 깨졌다"를 구분할 수 있게 한다.
|
||||
*
|
||||
* 의존성 정책: 이 컨트롤러는 Spring Boot의 ErrorAttributes를 사용하지 않는다.
|
||||
* 필요한 정보(상태 코드, 원인 예외, 원래 요청 경로)는 모두 서블릿 표준
|
||||
* RequestDispatcher.ERROR_* attribute로부터 직접 추출한다. 이렇게 두면
|
||||
* ErrorAttributeOptions의 변경(예: 스택트레이스 포함)이 이 컨트롤러가 노출하는 정보를
|
||||
* 본의 아니게 확장하지 못하며, 빈 의존 그래프도 작아진다.
|
||||
*/
|
||||
@RestController
|
||||
class ApiErrorController implements ErrorController {
|
||||
|
||||
<span class="fc" id="L40"> private static final Logger log = LoggerFactory.getLogger(ApiErrorController.class);</span>
|
||||
|
||||
private final ApiResultFactory apiResultFactory;
|
||||
|
||||
<span class="fc" id="L44"> ApiErrorController(ApiResultFactory apiResultFactory) {</span>
|
||||
<span class="fc" id="L45"> this.apiResultFactory = Objects.requireNonNull(apiResultFactory, "apiResultFactory must not be null");</span>
|
||||
<span class="fc" id="L46"> }</span>
|
||||
|
||||
@RequestMapping("/error")
|
||||
ResponseEntity<ApiResult<Void>> error(HttpServletRequest request) {
|
||||
<span class="fc" id="L50"> int rawStatus = resolveStatusCode(request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE));</span>
|
||||
<span class="fc" id="L51"> HttpStatus httpStatus = safeStatus(rawStatus);</span>
|
||||
<span class="fc" id="L52"> Throwable error = resolveError(request);</span>
|
||||
<span class="fc" id="L53"> String requestPath = LogSanitizer.requestPath(originalRequestPath(request));</span>
|
||||
<span class="fc" id="L54"> String method = request.getMethod();</span>
|
||||
|
||||
<span class="pc bpc" id="L56" title="1 of 4 branches missed."> if (httpStatus.is5xxServerError() && error != null) {</span>
|
||||
<span class="nc" id="L57"> log.error("Unhandled exception. method={} requestPath={} status={}",</span>
|
||||
<span class="nc" id="L58"> method, requestPath, httpStatus.value(), error);</span>
|
||||
<span class="fc bfc" id="L59" title="All 2 branches covered."> } else if (httpStatus.is5xxServerError()) {</span>
|
||||
<span class="fc" id="L60"> log.error("Unhandled error response. method={} requestPath={} status={}",</span>
|
||||
<span class="fc" id="L61"> method, requestPath, httpStatus.value());</span>
|
||||
} else {
|
||||
<span class="fc" id="L63"> log.warn("Unhandled error response. method={} requestPath={} status={}",</span>
|
||||
<span class="fc" id="L64"> method, requestPath, httpStatus.value());</span>
|
||||
}
|
||||
|
||||
<span class="fc" id="L67"> ClientFacingErrorCode errorCode = classify(httpStatus);</span>
|
||||
|
||||
<span class="fc" id="L69"> return ResponseEntity.status(httpStatus)</span>
|
||||
<span class="fc" id="L70"> .body(apiResultFactory.failure(errorCode.code(), errorCode.message()));</span>
|
||||
}
|
||||
|
||||
private static ClientFacingErrorCode classify(HttpStatus status) {
|
||||
<span class="fc bfc" id="L74" title="All 2 branches covered."> if (status.is5xxServerError()) {</span>
|
||||
<span class="fc" id="L75"> return CommonErrorCode.INTERNAL_SERVER_ERROR;</span>
|
||||
}
|
||||
<span class="pc bpc" id="L77" title="3 of 6 branches missed."> return switch (status) {</span>
|
||||
<span class="fc" id="L78"> case NOT_FOUND -> PresentationErrorCode.RESOURCE_NOT_FOUND;</span>
|
||||
<span class="fc" id="L79"> case METHOD_NOT_ALLOWED -> PresentationErrorCode.METHOD_NOT_ALLOWED;</span>
|
||||
<span class="nc" id="L80"> case UNSUPPORTED_MEDIA_TYPE -> PresentationErrorCode.UNSUPPORTED_MEDIA_TYPE;</span>
|
||||
<span class="nc" id="L81"> case NOT_ACCEPTABLE -> PresentationErrorCode.NOT_ACCEPTABLE;</span>
|
||||
<span class="nc" id="L82"> case CONTENT_TOO_LARGE -> PresentationErrorCode.PAYLOAD_TOO_LARGE;</span>
|
||||
<span class="fc" id="L83"> default -> PresentationErrorCode.UNHANDLED_CLIENT_ERROR;</span>
|
||||
};
|
||||
}
|
||||
|
||||
private static HttpStatus safeStatus(int rawStatus) {
|
||||
try {
|
||||
<span class="fc" id="L89"> return HttpStatus.valueOf(rawStatus);</span>
|
||||
<span class="fc" id="L90"> } catch (IllegalArgumentException ignored) {</span>
|
||||
<span class="fc" id="L91"> return HttpStatus.INTERNAL_SERVER_ERROR;</span>
|
||||
}
|
||||
}
|
||||
|
||||
private static int resolveStatusCode(Object status) {
|
||||
<span class="fc bfc" id="L96" title="All 2 branches covered."> if (status instanceof Integer value) {</span>
|
||||
<span class="fc" id="L97"> return value;</span>
|
||||
}
|
||||
<span class="fc" id="L99"> return HttpStatus.INTERNAL_SERVER_ERROR.value();</span>
|
||||
}
|
||||
|
||||
private static String originalRequestPath(HttpServletRequest request) {
|
||||
<span class="fc" id="L103"> Object path = request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);</span>
|
||||
<span class="pc bpc" id="L104" title="1 of 4 branches missed."> if (path instanceof String requestUri && !requestUri.isBlank()) {</span>
|
||||
<span class="fc" id="L105"> return requestUri;</span>
|
||||
}
|
||||
<span class="fc" id="L107"> return request.getRequestURI();</span>
|
||||
}
|
||||
|
||||
private static Throwable resolveError(HttpServletRequest request) {
|
||||
<span class="fc" id="L111"> Object exception = request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);</span>
|
||||
<span class="pc bpc" id="L112" title="1 of 2 branches missed."> return exception instanceof Throwable throwable ? throwable : null;</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
@@ -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="en"><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>InfrastructureExceptionHandler</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">bootstrap</a> > <a href="index.html" class="el_package">com.project.auth.config.web</a> > <span class="el_class">InfrastructureExceptionHandler</span></div><h1>InfrastructureExceptionHandler</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">0 of 80</td><td class="ctr2">100%</td><td class="bar">0 of 0</td><td class="ctr2">n/a</td><td class="ctr1">0</td><td class="ctr2">4</td><td class="ctr1">0</td><td class="ctr2">19</td><td class="ctr1">0</td><td class="ctr2">4</td></tr></tfoot><tbody><tr><td id="a0"><a href="InfrastructureExceptionHandler.java.html#L42" class="el_method">handleInfrastructureException(InfrastructureException, HttpServletRequest)</a></td><td class="bar" id="b0"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="38" alt="38"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d0"/><td class="ctr2" id="e0">n/a</td><td class="ctr1" id="f0">0</td><td class="ctr2" id="g0">1</td><td class="ctr1" id="h0">0</td><td class="ctr2" id="i0">8</td><td class="ctr1" id="j0">0</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a1"><a href="InfrastructureExceptionHandler.java.html#L62" class="el_method">handleLeakedDomainException(DomainException, HttpServletRequest)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/greenbar.gif" width="101" height="10" title="32" alt="32"/></td><td class="ctr2" id="c1">100%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">0</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">0</td><td class="ctr2" id="i1">7</td><td class="ctr1" id="j1">0</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a2"><a href="InfrastructureExceptionHandler.java.html#L33" class="el_method">InfrastructureExceptionHandler(ApiResultFactory)</a></td><td class="bar" id="b2"><img src="../jacoco-resources/greenbar.gif" width="18" height="10" title="6" alt="6"/></td><td class="ctr2" id="c2">100%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">0</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">0</td><td class="ctr2" id="i2">3</td><td class="ctr1" id="j2">0</td><td class="ctr2" id="k2">1</td></tr><tr><td id="a3"><a href="InfrastructureExceptionHandler.java.html#L29" class="el_method">static {...}</a></td><td class="bar" id="b3"><img src="../jacoco-resources/greenbar.gif" width="12" height="10" title="4" alt="4"/></td><td class="ctr2" id="c3">100%</td><td class="bar" id="d3"/><td class="ctr2" id="e3">n/a</td><td class="ctr1" id="f3">0</td><td class="ctr2" id="g3">1</td><td class="ctr1" id="h3">0</td><td class="ctr2" id="i3">1</td><td class="ctr1" id="j3">0</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.13.202504020838</span></div></body></html>
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?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="en"><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>InfrastructureExceptionHandler.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.web</a> > <span class="el_source">InfrastructureExceptionHandler.java</span></div><h1>InfrastructureExceptionHandler.java</h1><pre class="source lang-java linenums">package com.project.auth.config.web;
|
||||
|
||||
import com.project.auth.application.support.exception.CommonErrorCode;
|
||||
import com.project.auth.application.support.logging.LogSanitizer;
|
||||
import com.project.auth.domain.user.exception.DomainException;
|
||||
import com.project.auth.infrastructure.support.exception.InfrastructureException;
|
||||
import com.project.auth.presentation.support.exception.ApiErrorHttpStatusMapper;
|
||||
import com.project.auth.presentation.support.response.ApiResult;
|
||||
import com.project.auth.presentation.support.response.ApiResultFactory;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* InfrastructureErrorCode is kept as an internal classification for logs and alerting.
|
||||
* Client responses are intentionally normalized to COMMON-999 to avoid exposing internal dependency details.
|
||||
* This advice stays in bootstrap because moving it to presentation would create a
|
||||
* presentation -> infrastructure dependency and break the layer rule.
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class InfrastructureExceptionHandler {
|
||||
|
||||
<span class="fc" id="L29"> private static final Logger log = LoggerFactory.getLogger(InfrastructureExceptionHandler.class);</span>
|
||||
|
||||
private final ApiResultFactory apiResultFactory;
|
||||
|
||||
<span class="fc" id="L33"> public InfrastructureExceptionHandler(ApiResultFactory apiResultFactory) {</span>
|
||||
<span class="fc" id="L34"> this.apiResultFactory = apiResultFactory;</span>
|
||||
<span class="fc" id="L35"> }</span>
|
||||
|
||||
@ExceptionHandler(InfrastructureException.class)
|
||||
public ResponseEntity<ApiResult<Void>> handleInfrastructureException(
|
||||
InfrastructureException exception,
|
||||
HttpServletRequest request
|
||||
) {
|
||||
<span class="fc" id="L42"> log.error(</span>
|
||||
"Infrastructure failure. errorCode={} method={} requestPath={}",
|
||||
<span class="fc" id="L44"> exception.getErrorCode().code(),</span>
|
||||
<span class="fc" id="L45"> request.getMethod(),</span>
|
||||
<span class="fc" id="L46"> LogSanitizer.requestPath(request.getRequestURI()),</span>
|
||||
exception
|
||||
);
|
||||
|
||||
<span class="fc" id="L50"> return ResponseEntity.status(ApiErrorHttpStatusMapper.map(CommonErrorCode.INTERNAL_SERVER_ERROR))</span>
|
||||
<span class="fc" id="L51"> .body(apiResultFactory.failure(</span>
|
||||
<span class="fc" id="L52"> CommonErrorCode.INTERNAL_SERVER_ERROR.code(),</span>
|
||||
<span class="fc" id="L53"> CommonErrorCode.INTERNAL_SERVER_ERROR.message()</span>
|
||||
));
|
||||
}
|
||||
|
||||
@ExceptionHandler(DomainException.class)
|
||||
public ResponseEntity<ApiResult<Void>> handleLeakedDomainException(
|
||||
DomainException exception,
|
||||
HttpServletRequest request
|
||||
) {
|
||||
<span class="fc" id="L62"> log.error(</span>
|
||||
"Leaked domain exception. method={} requestPath={}",
|
||||
<span class="fc" id="L64"> request.getMethod(),</span>
|
||||
<span class="fc" id="L65"> LogSanitizer.requestPath(request.getRequestURI()),</span>
|
||||
exception
|
||||
);
|
||||
|
||||
<span class="fc" id="L69"> return ResponseEntity.status(ApiErrorHttpStatusMapper.map(CommonErrorCode.INTERNAL_SERVER_ERROR))</span>
|
||||
<span class="fc" id="L70"> .body(apiResultFactory.failure(</span>
|
||||
<span class="fc" id="L71"> CommonErrorCode.INTERNAL_SERVER_ERROR.code(),</span>
|
||||
<span class="fc" id="L72"> CommonErrorCode.INTERNAL_SERVER_ERROR.message()</span>
|
||||
));
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
File diff suppressed because one or more lines are too long
+90
@@ -0,0 +1,90 @@
|
||||
<?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="en"><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>RequestAccessLogFilter.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.web</a> > <span class="el_source">RequestAccessLogFilter.java</span></div><h1>RequestAccessLogFilter.java</h1><pre class="source lang-java linenums">package com.project.auth.config.web;
|
||||
|
||||
import com.project.auth.application.support.logging.LogSanitizer;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class RequestAccessLogFilter extends OncePerRequestFilter {
|
||||
|
||||
<span class="fc" id="L19"> private static final Logger log = LoggerFactory.getLogger("http.access");</span>
|
||||
private static final String ACCESS_EVENT_TYPE = "HTTP_ACCESS";
|
||||
|
||||
private final List<String> excludedPathPrefixes;
|
||||
|
||||
<span class="fc" id="L24"> public RequestAccessLogFilter(AccessLogProperties accessLogProperties) {</span>
|
||||
<span class="fc bfc" id="L25" title="All 2 branches covered."> this.excludedPathPrefixes = accessLogProperties.excludedPathPrefixes() == null</span>
|
||||
<span class="fc" id="L26"> ? List.of()</span>
|
||||
<span class="fc" id="L27"> : List.copyOf(accessLogProperties.excludedPathPrefixes());</span>
|
||||
<span class="fc" id="L28"> }</span>
|
||||
|
||||
@Override
|
||||
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||
<span class="fc" id="L32"> String path = request.getRequestURI();</span>
|
||||
<span class="fc" id="L33"> return excludedPathPrefixes.stream().anyMatch(path::startsWith);</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldNotFilterAsyncDispatch() {
|
||||
<span class="nc" id="L38"> return true;</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldNotFilterErrorDispatch() {
|
||||
<span class="nc" id="L43"> return true;</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
<span class="fc" id="L51"> long startTime = System.nanoTime();</span>
|
||||
|
||||
try {
|
||||
<span class="fc" id="L54"> filterChain.doFilter(request, response);</span>
|
||||
} finally {
|
||||
<span class="fc" id="L56"> long durationMs = (System.nanoTime() - startTime) / 1_000_000;</span>
|
||||
<span class="fc" id="L57"> logRequestSummary(request, response, durationMs);</span>
|
||||
}
|
||||
<span class="fc" id="L59"> }</span>
|
||||
|
||||
private void logRequestSummary(HttpServletRequest request, HttpServletResponse response, long durationMs) {
|
||||
<span class="fc" id="L62"> String method = request.getMethod();</span>
|
||||
<span class="fc" id="L63"> String path = LogSanitizer.requestPath(request.getRequestURI());</span>
|
||||
<span class="fc" id="L64"> int status = response.getStatus();</span>
|
||||
<span class="fc" id="L65"> String remoteIp = LogSanitizer.clientIp(request.getRemoteAddr());</span>
|
||||
<span class="fc" id="L66"> String actorId = resolveActorId();</span>
|
||||
<span class="pc bpc" id="L67" title="1 of 2 branches missed."> String result = status < 400 ? "success" : "failure";</span>
|
||||
|
||||
<span class="fc" id="L69"> log.atInfo()</span>
|
||||
<span class="fc" id="L70"> .addKeyValue("eventType", ACCESS_EVENT_TYPE)</span>
|
||||
<span class="fc" id="L71"> .addKeyValue("method", method)</span>
|
||||
<span class="fc" id="L72"> .addKeyValue("requestPath", path)</span>
|
||||
<span class="fc" id="L73"> .addKeyValue("status", status)</span>
|
||||
<span class="fc" id="L74"> .addKeyValue("durationMs", durationMs)</span>
|
||||
<span class="fc" id="L75"> .addKeyValue("remoteIp", remoteIp)</span>
|
||||
<span class="fc" id="L76"> .addKeyValue("actorId", actorId)</span>
|
||||
<span class="fc" id="L77"> .addKeyValue("result", result)</span>
|
||||
<span class="fc" id="L78"> .log("ACCESS");</span>
|
||||
<span class="fc" id="L79"> }</span>
|
||||
|
||||
private String resolveActorId() {
|
||||
<span class="fc" id="L82"> Authentication authentication = SecurityContextHolder.getContext().getAuthentication();</span>
|
||||
<span class="pc bpc" id="L83" title="1 of 4 branches missed."> if (authentication != null && authentication.isAuthenticated()</span>
|
||||
<span class="pc bpc" id="L84" title="1 of 2 branches missed."> && !"anonymousUser".equals(authentication.getPrincipal())) {</span>
|
||||
<span class="fc" id="L85"> return LogSanitizer.actorId(authentication.getName());</span>
|
||||
}
|
||||
<span class="fc" id="L87"> return LogSanitizer.actorId("anonymous");</span>
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
File diff suppressed because one or more lines are too long
+69
@@ -0,0 +1,69 @@
|
||||
<?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="en"><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>RequestBoundApiResultFactory.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.web</a> > <span class="el_source">RequestBoundApiResultFactory.java</span></div><h1>RequestBoundApiResultFactory.java</h1><pre class="source lang-java linenums">package com.project.auth.config.web;
|
||||
|
||||
import com.project.auth.presentation.support.response.ApiResult;
|
||||
import com.project.auth.presentation.support.response.ApiResultFactory;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class RequestBoundApiResultFactory implements ApiResultFactory {
|
||||
|
||||
private static final String TRACE_ID_KEY = "traceId";
|
||||
/**
|
||||
* MDC에 traceId가 없을 때 사용하는 sentinel. JSON {@code null}로 그대로 두면
|
||||
* TraceIdFilter가 누락/오설정된 사실이 가려지므로, 명시적인 placeholder 문자열로
|
||||
* 로그·대시보드에서 즉시 식별 가능하게 한다.
|
||||
*/
|
||||
static final String MISSING_TRACE_ID = "-";
|
||||
<span class="fc" id="L22"> private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ISO_INSTANT;</span>
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
<span class="fc" id="L26"> public RequestBoundApiResultFactory(Clock clock) {</span>
|
||||
<span class="fc" id="L27"> this.clock = Objects.requireNonNull(clock, "clock must not be null");</span>
|
||||
<span class="fc" id="L28"> }</span>
|
||||
|
||||
@Override
|
||||
public <T> ApiResult<T> success(String code, String message, T data) {
|
||||
<span class="fc" id="L32"> return result(true, code, message, data, null);</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResult<Void> success(String code, String message) {
|
||||
<span class="nc" id="L37"> return result(true, code, message, null, null);</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResult<Void> failure(String code, String message) {
|
||||
<span class="fc" id="L42"> return result(false, code, message, null, null);</span>
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResult<Void> failure(String code, String message, Map<String, List<String>> errors) {
|
||||
<span class="fc" id="L47"> return result(false, code, message, null, errors);</span>
|
||||
}
|
||||
|
||||
private <T> ApiResult<T> result(
|
||||
boolean success,
|
||||
String code,
|
||||
String message,
|
||||
T data,
|
||||
Map<String, List<String>> errors
|
||||
) {
|
||||
<span class="fc" id="L57"> String traceId = MDC.get(TRACE_ID_KEY);</span>
|
||||
<span class="fc" id="L58"> return new ApiResult<>(</span>
|
||||
success,
|
||||
code,
|
||||
message,
|
||||
data,
|
||||
errors,
|
||||
<span class="fc bfc" id="L64" title="All 4 branches covered."> traceId == null || traceId.isBlank() ? MISSING_TRACE_ID : traceId,</span>
|
||||
<span class="fc" id="L65"> TIMESTAMP_FORMATTER.format(clock.instant())</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
File diff suppressed because one or more lines are too long
+144
@@ -0,0 +1,144 @@
|
||||
<?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="en"><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>SecurityResponseExceptionHandler.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.web</a> > <span class="el_source">SecurityResponseExceptionHandler.java</span></div><h1>SecurityResponseExceptionHandler.java</h1><pre class="source lang-java linenums">package com.project.auth.config.web;
|
||||
|
||||
import com.project.auth.application.support.audit.AuthAuditEventType;
|
||||
import com.project.auth.application.support.exception.AuthErrorCode;
|
||||
import com.project.auth.application.support.logging.LogSanitizer;
|
||||
import com.project.auth.config.auth.security.SecurityAuditTrailWriter;
|
||||
import com.project.auth.presentation.support.exception.ApiErrorHttpStatusMapper;
|
||||
import com.project.auth.presentation.support.response.ApiResult;
|
||||
import com.project.auth.presentation.support.response.ApiResultFactory;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Spring Security 예외를 처리하는 bootstrap 계층 advice.
|
||||
*
|
||||
* presentation 모듈은 ArchUnit 규칙에 의해 org.springframework.security.core / context에
|
||||
* 직접 의존할 수 없으므로(LayerDependencyArchitectureTest 참고), 이 advice는 bootstrap에 둔다.
|
||||
*
|
||||
* AccessDeniedException은 현재 인증 상태에 따라 401/403으로 분기한다. 익명 principal이
|
||||
* 보호된 리소스에 접근한 경우는 인증이 필요하다는 의미로 401을 반환하고, 인증된 principal이
|
||||
* 권한이 부족한 경우에만 403을 반환한다. Spring Security의 ExceptionTranslationFilter가
|
||||
* 필터 단계에서 던지는 예외에 대해 적용하는 분기 로직을 컨트롤러 단(@PreAuthorize 등 메서드
|
||||
* 보안)에서 던져진 동일 예외에도 일관되게 적용한 것이다.
|
||||
*
|
||||
* Audit 정책: 이 advice의 모든 분기(인증 필요, 익명 401, 인증된 사용자 403)는
|
||||
* SecurityAuditTrailWriter로 audit를 기록한다. 필터 단의 SecurityExceptionHandler가
|
||||
* 기록하는 audit 채널과 동일 채널을 사용하므로, "필터 단/컨트롤러 단" 어느 경로로 들어와도
|
||||
* 보안 이벤트가 누락 없이 동일 형태로 적재된다.
|
||||
*
|
||||
* 스레드/익명 판정: SecurityContextHolder의 ThreadLocal 컨텍스트를 우선 보지만,
|
||||
* 비동기 컨트롤러로 컨텍스트가 워커 스레드로 전파되지 않은 케이스를 대비해
|
||||
* HttpServletRequest.getUserPrincipal()을 보조 신호로 사용한다. principal이 명시적으로
|
||||
* 존재하면 익명으로 분류하지 않는다 — 컨텍스트가 비어 있더라도 인증된 사용자가 잘못
|
||||
* 401로 분류되지 않도록 보호하는 방어적 폴백이다.
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 5)
|
||||
public class SecurityResponseExceptionHandler {
|
||||
|
||||
<span class="fc" id="L53"> private static final Logger log = LoggerFactory.getLogger(SecurityResponseExceptionHandler.class);</span>
|
||||
|
||||
private final ApiResultFactory apiResultFactory;
|
||||
private final SecurityAuditTrailWriter securityAuditTrailWriter;
|
||||
|
||||
public SecurityResponseExceptionHandler(
|
||||
ApiResultFactory apiResultFactory,
|
||||
SecurityAuditTrailWriter securityAuditTrailWriter
|
||||
<span class="fc" id="L61"> ) {</span>
|
||||
<span class="fc" id="L62"> this.apiResultFactory = Objects.requireNonNull(apiResultFactory, "apiResultFactory must not be null");</span>
|
||||
<span class="fc" id="L63"> this.securityAuditTrailWriter = Objects.requireNonNull(</span>
|
||||
securityAuditTrailWriter, "securityAuditTrailWriter must not be null");
|
||||
<span class="fc" id="L65"> }</span>
|
||||
|
||||
@ExceptionHandler(AuthenticationException.class)
|
||||
public ResponseEntity<ApiResult<Void>> handleAuthenticationException(
|
||||
AuthenticationException exception,
|
||||
HttpServletRequest request
|
||||
) {
|
||||
<span class="fc" id="L72"> log.warn("Authentication required. exceptionType={} method={} requestPath={} errorCode={}",</span>
|
||||
<span class="fc" id="L73"> exception.getClass().getSimpleName(),</span>
|
||||
<span class="fc" id="L74"> request.getMethod(),</span>
|
||||
<span class="fc" id="L75"> LogSanitizer.requestPath(request.getRequestURI()),</span>
|
||||
<span class="fc" id="L76"> AuthErrorCode.AUTHENTICATION_REQUIRED.code());</span>
|
||||
<span class="fc" id="L77"> recordAudit(request, AuthAuditEventType.AUTHENTICATION_REQUIRED, "Authentication required.");</span>
|
||||
|
||||
<span class="fc" id="L79"> return ResponseEntity.status(ApiErrorHttpStatusMapper.map(AuthErrorCode.AUTHENTICATION_REQUIRED))</span>
|
||||
<span class="fc" id="L80"> .body(apiResultFactory.failure(</span>
|
||||
<span class="fc" id="L81"> AuthErrorCode.AUTHENTICATION_REQUIRED.code(),</span>
|
||||
<span class="fc" id="L82"> AuthErrorCode.AUTHENTICATION_REQUIRED.message()</span>
|
||||
));
|
||||
}
|
||||
|
||||
@ExceptionHandler(AccessDeniedException.class)
|
||||
public ResponseEntity<ApiResult<Void>> handleAccessDeniedException(
|
||||
AccessDeniedException exception,
|
||||
HttpServletRequest request
|
||||
) {
|
||||
<span class="pc bpc" id="L91" title="1 of 2 branches missed."> if (isAnonymous(SecurityContextHolder.getContext().getAuthentication(), request)) {</span>
|
||||
<span class="nc" id="L92"> log.warn("AccessDenied for anonymous principal -> 401. exceptionType={} method={} requestPath={} errorCode={}",</span>
|
||||
<span class="nc" id="L93"> exception.getClass().getSimpleName(),</span>
|
||||
<span class="nc" id="L94"> request.getMethod(),</span>
|
||||
<span class="nc" id="L95"> LogSanitizer.requestPath(request.getRequestURI()),</span>
|
||||
<span class="nc" id="L96"> AuthErrorCode.AUTHENTICATION_REQUIRED.code());</span>
|
||||
<span class="nc" id="L97"> recordAudit(request, AuthAuditEventType.AUTHENTICATION_REQUIRED,</span>
|
||||
"Authentication required (AccessDenied for anonymous).");
|
||||
|
||||
<span class="nc" id="L100"> return ResponseEntity.status(ApiErrorHttpStatusMapper.map(AuthErrorCode.AUTHENTICATION_REQUIRED))</span>
|
||||
<span class="nc" id="L101"> .body(apiResultFactory.failure(</span>
|
||||
<span class="nc" id="L102"> AuthErrorCode.AUTHENTICATION_REQUIRED.code(),</span>
|
||||
<span class="nc" id="L103"> AuthErrorCode.AUTHENTICATION_REQUIRED.message()</span>
|
||||
));
|
||||
}
|
||||
|
||||
<span class="fc" id="L107"> log.warn("Access denied for authenticated principal. exceptionType={} method={} requestPath={} errorCode={}",</span>
|
||||
<span class="fc" id="L108"> exception.getClass().getSimpleName(),</span>
|
||||
<span class="fc" id="L109"> request.getMethod(),</span>
|
||||
<span class="fc" id="L110"> LogSanitizer.requestPath(request.getRequestURI()),</span>
|
||||
<span class="fc" id="L111"> AuthErrorCode.ACCESS_DENIED.code());</span>
|
||||
<span class="fc" id="L112"> recordAudit(request, AuthAuditEventType.ACCESS_DENIED, "Access denied.");</span>
|
||||
|
||||
<span class="fc" id="L114"> return ResponseEntity.status(ApiErrorHttpStatusMapper.map(AuthErrorCode.ACCESS_DENIED))</span>
|
||||
<span class="fc" id="L115"> .body(apiResultFactory.failure(</span>
|
||||
<span class="fc" id="L116"> AuthErrorCode.ACCESS_DENIED.code(),</span>
|
||||
<span class="fc" id="L117"> AuthErrorCode.ACCESS_DENIED.message()</span>
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* SecurityContext가 비어 있어도 서블릿 principal이 명시적으로 존재하면 익명이 아니다.
|
||||
* ThreadLocal 컨텍스트가 비동기 워커 스레드로 전파되지 않은 케이스에서 인증된 사용자가
|
||||
* 잘못 401로 분류되는 사고를 막기 위한 방어적 폴백.
|
||||
*/
|
||||
private static boolean isAnonymous(Authentication authentication, HttpServletRequest request) {
|
||||
<span class="pc bpc" id="L127" title="1 of 2 branches missed."> if (request.getUserPrincipal() != null) {</span>
|
||||
<span class="fc" id="L128"> return false;</span>
|
||||
}
|
||||
<span class="nc bnc" id="L130" title="All 2 branches missed."> return authentication == null</span>
|
||||
<span class="nc bnc" id="L131" title="All 4 branches missed."> || !authentication.isAuthenticated()</span>
|
||||
|| authentication instanceof AnonymousAuthenticationToken;
|
||||
}
|
||||
|
||||
private void recordAudit(HttpServletRequest request, AuthAuditEventType type, String description) {
|
||||
try {
|
||||
<span class="fc" id="L137"> securityAuditTrailWriter.record(request, type, description);</span>
|
||||
<span class="nc" id="L138"> } catch (RuntimeException auditFailure) {</span>
|
||||
<span class="nc" id="L139"> log.warn("Security audit write failed; continuing with response rendering. type={}",</span>
|
||||
type, auditFailure);
|
||||
<span class="fc" id="L141"> }</span>
|
||||
<span class="fc" id="L142"> }</span>
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
+1
@@ -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="en"><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>TraceIdFilter</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">bootstrap</a> > <a href="index.html" class="el_package">com.project.auth.config.web</a> > <span class="el_class">TraceIdFilter</span></div><h1>TraceIdFilter</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">4 of 61</td><td class="ctr2">93%</td><td class="bar">3 of 4</td><td class="ctr2">25%</td><td class="ctr1">2</td><td class="ctr2">6</td><td class="ctr1">0</td><td class="ctr2">15</td><td class="ctr1">0</td><td class="ctr2">4</td></tr></tfoot><tbody><tr><td id="a1"><a href="TraceIdFilter.java.html#L47" class="el_method">nextTraceId()</a></td><td class="bar" id="b0"><img src="../jacoco-resources/redbar.gif" width="14" height="10" title="4" alt="4"/><img src="../jacoco-resources/greenbar.gif" width="65" height="10" title="18" alt="18"/></td><td class="ctr2" id="c3">81%</td><td class="bar" id="d0"><img src="../jacoco-resources/redbar.gif" width="90" height="10" title="3" alt="3"/><img src="../jacoco-resources/greenbar.gif" width="30" height="10" title="1" alt="1"/></td><td class="ctr2" id="e0">25%</td><td class="ctr1" id="f0">2</td><td class="ctr2" id="g0">3</td><td class="ctr1" id="h0">0</td><td class="ctr2" id="i1">4</td><td class="ctr1" id="j0">0</td><td class="ctr2" id="k0">1</td></tr><tr><td id="a0"><a href="TraceIdFilter.java.html#L30" class="el_method">doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain)</a></td><td class="bar" id="b1"><img src="../jacoco-resources/greenbar.gif" width="120" height="10" title="33" alt="33"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d1"/><td class="ctr2" id="e1">n/a</td><td class="ctr1" id="f1">0</td><td class="ctr2" id="g1">1</td><td class="ctr1" id="h1">0</td><td class="ctr2" id="i0">9</td><td class="ctr1" id="j1">0</td><td class="ctr2" id="k1">1</td></tr><tr><td id="a3"><a href="TraceIdFilter.java.html#L16" class="el_method">TraceIdFilter()</a></td><td class="bar" id="b2"><img src="../jacoco-resources/greenbar.gif" width="10" height="10" title="3" alt="3"/></td><td class="ctr2" id="c1">100%</td><td class="bar" id="d2"/><td class="ctr2" id="e2">n/a</td><td class="ctr1" id="f2">0</td><td class="ctr2" id="g2">1</td><td class="ctr1" id="h2">0</td><td class="ctr2" id="i2">1</td><td class="ctr1" id="j2">0</td><td class="ctr2" id="k2">1</td></tr><tr><td id="a2"><a href="TraceIdFilter.java.html#L23" class="el_method">static {...}</a></td><td class="bar" id="b3"><img src="../jacoco-resources/greenbar.gif" width="10" height="10" title="3" alt="3"/></td><td class="ctr2" id="c2">100%</td><td class="bar" id="d3"/><td class="ctr2" id="e3">n/a</td><td class="ctr1" id="f3">0</td><td class="ctr2" id="g3">1</td><td class="ctr1" id="h3">0</td><td class="ctr2" id="i3">1</td><td class="ctr1" id="j3">0</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.13.202504020838</span></div></body></html>
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?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="en"><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>TraceIdFilter.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">bootstrap</a> > <a href="index.source.html" class="el_package">com.project.auth.config.web</a> > <span class="el_source">TraceIdFilter.java</span></div><h1>TraceIdFilter.java</h1><pre class="source lang-java linenums">package com.project.auth.config.web;
|
||||
|
||||
import com.project.auth.application.support.logging.LogSanitizer;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.MDC;
|
||||
import org.slf4j.MDC.MDCCloseable;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HexFormat;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
<span class="fc" id="L16">public class TraceIdFilter extends OncePerRequestFilter {</span>
|
||||
|
||||
private static final String TRACE_ID_KEY = "traceId";
|
||||
private static final String CLIENT_IP_KEY = "clientIp";
|
||||
private static final String USER_AGENT_KEY = "userAgent";
|
||||
private static final String TRACE_ID_HEADER = "X-Trace-Id";
|
||||
private static final String USER_AGENT_HEADER = "User-Agent";
|
||||
<span class="fc" id="L23"> private static final HexFormat HEX_FORMAT = HexFormat.of();</span>
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
<span class="fc" id="L30"> String traceId = nextTraceId();</span>
|
||||
<span class="fc" id="L31"> String clientIp = LogSanitizer.clientIp(request.getRemoteAddr());</span>
|
||||
<span class="fc" id="L32"> String userAgent = LogSanitizer.userAgent(request.getHeader(USER_AGENT_HEADER));</span>
|
||||
<span class="fc" id="L33"> response.setHeader(TRACE_ID_HEADER, traceId);</span>
|
||||
|
||||
try (
|
||||
<span class="fc" id="L36"> MDCCloseable ignoredTraceId = MDC.putCloseable(TRACE_ID_KEY, traceId);</span>
|
||||
<span class="fc" id="L37"> MDCCloseable ignoredClientIp = MDC.putCloseable(CLIENT_IP_KEY, clientIp);</span>
|
||||
<span class="fc" id="L38"> MDCCloseable ignoredUserAgent = MDC.putCloseable(USER_AGENT_KEY, userAgent)) {</span>
|
||||
<span class="fc" id="L39"> filterChain.doFilter(request, response);</span>
|
||||
}
|
||||
<span class="fc" id="L41"> }</span>
|
||||
|
||||
private String nextTraceId() {
|
||||
long highBits;
|
||||
long lowBits;
|
||||
do {
|
||||
<span class="fc" id="L47"> highBits = ThreadLocalRandom.current().nextLong();</span>
|
||||
<span class="fc" id="L48"> lowBits = ThreadLocalRandom.current().nextLong();</span>
|
||||
<span class="pc bpc" id="L49" title="3 of 4 branches missed."> } while (highBits == 0L && lowBits == 0L);</span>
|
||||
|
||||
<span class="fc" id="L51"> return HEX_FORMAT.toHexDigits(highBits) + HEX_FORMAT.toHexDigits(lowBits);</span>
|
||||
}
|
||||
|
||||
}
|
||||
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.13.202504020838</span></div></body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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="en"><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>bootstrap</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="jacoco-sessions.html" class="el_session">Sessions</a></span><span class="el_report">bootstrap</span></div><h1>bootstrap</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">153 of 1,096</td><td class="ctr2">86%</td><td class="bar">32 of 76</td><td class="ctr2">57%</td><td class="ctr1">29</td><td class="ctr2">102</td><td class="ctr1">35</td><td class="ctr2">266</td><td class="ctr1">3</td><td class="ctr2">62</td><td class="ctr1">0</td><td class="ctr2">14</td></tr></tfoot><tbody><tr><td id="a2"><a href="com.project.auth.config.web/index.html" class="el_package">com.project.auth.config.web</a></td><td class="bar" id="b0"><img src="jacoco-resources/redbar.gif" width="19" height="10" title="114" alt="114"/><img src="jacoco-resources/greenbar.gif" width="100" height="10" title="598" alt="598"/></td><td class="ctr2" id="c2">83%</td><td class="bar" id="d0"><img src="jacoco-resources/redbar.gif" width="48" height="10" title="20" alt="20"/><img src="jacoco-resources/greenbar.gif" width="72" height="10" title="30" alt="30"/></td><td class="ctr2" id="e1">60%</td><td class="ctr1" id="f0">19</td><td class="ctr2" id="g0">64</td><td class="ctr1" id="h0">22</td><td class="ctr2" id="i0">169</td><td class="ctr1" id="j0">3</td><td class="ctr2" id="k0">37</td><td class="ctr1" id="l0">0</td><td class="ctr2" id="m1">6</td></tr><tr><td id="a0"><a href="com.project.auth.config.auth.security/index.html" class="el_package">com.project.auth.config.auth.security</a></td><td class="bar" id="b1"><img src="jacoco-resources/redbar.gif" width="6" height="10" title="39" alt="39"/><img src="jacoco-resources/greenbar.gif" width="52" height="10" title="309" alt="309"/></td><td class="ctr2" id="c1">88%</td><td class="bar" id="d1"><img src="jacoco-resources/redbar.gif" width="28" height="10" title="12" alt="12"/><img src="jacoco-resources/greenbar.gif" width="28" height="10" title="12" alt="12"/></td><td class="ctr2" id="e2">50%</td><td class="ctr1" id="f1">10</td><td class="ctr2" id="g1">34</td><td class="ctr1" id="h1">13</td><td class="ctr2" id="i1">91</td><td class="ctr1" id="j1">0</td><td class="ctr2" id="k1">22</td><td class="ctr1" id="l1">0</td><td class="ctr2" id="m0">7</td></tr><tr><td id="a1"><a href="com.project.auth.config.logging/index.html" class="el_package">com.project.auth.config.logging</a></td><td class="bar" id="b2"><img src="jacoco-resources/greenbar.gif" width="6" height="10" title="36" alt="36"/></td><td class="ctr2" id="c0">100%</td><td class="bar" id="d2"><img src="jacoco-resources/greenbar.gif" width="4" height="10" title="2" alt="2"/></td><td class="ctr2" id="e0">100%</td><td class="ctr1" id="f2">0</td><td class="ctr2" id="g2">4</td><td class="ctr1" id="h2">0</td><td class="ctr2" id="i2">6</td><td class="ctr1" id="j2">0</td><td class="ctr2" id="k2">3</td><td class="ctr1" id="l2">0</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.13.202504020838</span></div></body></html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 91 B |
Binary file not shown.
|
After Width: | Height: | Size: 91 B |
Binary file not shown.
|
After Width: | Height: | Size: 91 B |
Binary file not shown.
|
After Width: | Height: | Size: 709 B |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user