mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
[MNT-24859] Authentication flow for Web script home
This commit is contained in:
@@ -32,6 +32,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
|
||||||
import net.sf.acegisecurity.DisabledException;
|
import net.sf.acegisecurity.DisabledException;
|
||||||
|
import org.alfresco.repo.security.authentication.external.WebScriptHomeAuthenticator;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.extensions.webscripts.Authenticator;
|
import org.springframework.extensions.webscripts.Authenticator;
|
||||||
@@ -72,9 +73,12 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
protected RemoteUserMapper remoteUserMapper;
|
protected RemoteUserMapper remoteUserMapper;
|
||||||
protected AuthenticationComponent authenticationComponent;
|
protected AuthenticationComponent authenticationComponent;
|
||||||
protected AdminConsoleAuthenticator adminConsoleAuthenticator;
|
protected AdminConsoleAuthenticator adminConsoleAuthenticator;
|
||||||
|
protected WebScriptHomeAuthenticator webScriptHomeAuthenticator;
|
||||||
|
|
||||||
private boolean alwaysAllowBasicAuthForAdminConsole = true;
|
private boolean alwaysAllowBasicAuthForAdminConsole = true;
|
||||||
|
private boolean alwaysAllowBasicAuthForWebScriptHome = true;
|
||||||
List<String> adminConsoleScriptFamilies;
|
List<String> adminConsoleScriptFamilies;
|
||||||
|
List<String> webScriptHomeFamilies;
|
||||||
long getRemoteUserTimeoutMilliseconds = GET_REMOTE_USER_TIMEOUT_MILLISECONDS_DEFAULT;
|
long getRemoteUserTimeoutMilliseconds = GET_REMOTE_USER_TIMEOUT_MILLISECONDS_DEFAULT;
|
||||||
|
|
||||||
public void setRemoteUserMapper(RemoteUserMapper remoteUserMapper)
|
public void setRemoteUserMapper(RemoteUserMapper remoteUserMapper)
|
||||||
@@ -97,6 +101,16 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
this.alwaysAllowBasicAuthForAdminConsole = alwaysAllowBasicAuthForAdminConsole;
|
this.alwaysAllowBasicAuthForAdminConsole = alwaysAllowBasicAuthForAdminConsole;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAlwaysAllowBasicAuthForWebScriptHome()
|
||||||
|
{
|
||||||
|
return alwaysAllowBasicAuthForWebScriptHome;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlwaysAllowBasicAuthForWebScriptHome(boolean alwaysAllowBasicAuthForWebScriptHome)
|
||||||
|
{
|
||||||
|
this.alwaysAllowBasicAuthForWebScriptHome = alwaysAllowBasicAuthForWebScriptHome;
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getAdminConsoleScriptFamilies()
|
public List<String> getAdminConsoleScriptFamilies()
|
||||||
{
|
{
|
||||||
return adminConsoleScriptFamilies;
|
return adminConsoleScriptFamilies;
|
||||||
@@ -107,6 +121,16 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
this.adminConsoleScriptFamilies = adminConsoleScriptFamilies;
|
this.adminConsoleScriptFamilies = adminConsoleScriptFamilies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getWebScriptHomeFamilies()
|
||||||
|
{
|
||||||
|
return webScriptHomeFamilies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWebScriptHomeFamilies(List<String> webScriptHomeFamilies)
|
||||||
|
{
|
||||||
|
this.webScriptHomeFamilies = webScriptHomeFamilies;
|
||||||
|
}
|
||||||
|
|
||||||
public long getGetRemoteUserTimeoutMilliseconds()
|
public long getGetRemoteUserTimeoutMilliseconds()
|
||||||
{
|
{
|
||||||
return getRemoteUserTimeoutMilliseconds;
|
return getRemoteUserTimeoutMilliseconds;
|
||||||
@@ -123,6 +147,12 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
this.adminConsoleAuthenticator = adminConsoleAuthenticator;
|
this.adminConsoleAuthenticator = adminConsoleAuthenticator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setWebScriptHomeAuthenticator(
|
||||||
|
WebScriptHomeAuthenticator webScriptHomeAuthenticator)
|
||||||
|
{
|
||||||
|
this.webScriptHomeAuthenticator = webScriptHomeAuthenticator;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Authenticator create(WebScriptServletRequest req, WebScriptServletResponse res)
|
public Authenticator create(WebScriptServletRequest req, WebScriptServletResponse res)
|
||||||
{
|
{
|
||||||
@@ -160,6 +190,12 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
{
|
{
|
||||||
userId = getAdminConsoleUser();
|
userId = getAdminConsoleUser();
|
||||||
}
|
}
|
||||||
|
else if (servletReq.getServiceMatch() != null &&
|
||||||
|
isWebScriptHome(servletReq.getServiceMatch().getWebScript())
|
||||||
|
&& isWebScriptAuthenticatorActive())
|
||||||
|
{
|
||||||
|
userId = getWebScriptHomeUser();
|
||||||
|
}
|
||||||
|
|
||||||
if (userId == null)
|
if (userId == null)
|
||||||
{
|
{
|
||||||
@@ -181,6 +217,25 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isAlwaysAllowBasicAuthForWebScriptHome())
|
||||||
|
{
|
||||||
|
final boolean useTimeoutForAdminAccessingWebScript = shouldUseTimeoutForAdminAccessingWebScriptHome(required, isGuest);
|
||||||
|
|
||||||
|
if (useTimeoutForAdminAccessingWebScript && isBasicAuthHeaderPresentForAdmin())
|
||||||
|
{
|
||||||
|
return callBasicAuthForWebScriptHomeAccess(required, isGuest);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
userId = getRemoteUserWithTimeout(useTimeoutForAdminAccessingWebScript);
|
||||||
|
}
|
||||||
|
catch (AuthenticationTimeoutException e)
|
||||||
|
{
|
||||||
|
// return basic auth challenge
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// retrieve the remote user if configured and available - authenticate that user directly
|
// retrieve the remote user if configured and available - authenticate that user directly
|
||||||
@@ -252,10 +307,25 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
authenticated = super.authenticate(required, isGuest);
|
authenticated = super.authenticate(required, isGuest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!authenticated && servletReq.getServiceMatch() != null &&
|
if (!authenticated && servletReq.getServiceMatch() != null)
|
||||||
isAdminConsoleWebScript(servletReq.getServiceMatch().getWebScript()) && isAdminConsoleAuthenticatorActive())
|
|
||||||
{
|
{
|
||||||
adminConsoleAuthenticator.requestAuthentication(this.servletReq.getHttpServletRequest(), this.servletRes.getHttpServletResponse());
|
WebScript webScript = servletReq.getServiceMatch().getWebScript();
|
||||||
|
|
||||||
|
if (isAdminConsoleWebScript(webScript) && isAdminConsoleAuthenticatorActive())
|
||||||
|
{
|
||||||
|
adminConsoleAuthenticator.requestAuthentication(
|
||||||
|
this.servletReq.getHttpServletRequest(),
|
||||||
|
this.servletRes.getHttpServletResponse()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (isWebScriptHome(webScript)
|
||||||
|
&& isWebScriptAuthenticatorActive())
|
||||||
|
{
|
||||||
|
webScriptHomeAuthenticator.requestAuthentication(
|
||||||
|
this.servletReq.getHttpServletRequest(),
|
||||||
|
this.servletRes.getHttpServletResponse()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return authenticated;
|
return authenticated;
|
||||||
}
|
}
|
||||||
@@ -274,6 +344,16 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
return super.authenticate(required, isGuest);
|
return super.authenticate(required, isGuest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean callBasicAuthForWebScriptHomeAccess(RequiredAuthentication required, boolean isGuest)
|
||||||
|
{
|
||||||
|
// return REST call, after a timeout/basic auth challenge
|
||||||
|
if (LOGGER.isTraceEnabled())
|
||||||
|
{
|
||||||
|
LOGGER.trace("An Web script request has come in with Basic Auth headers present for an admin user.");
|
||||||
|
}
|
||||||
|
return super.authenticate(required, isGuest);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean shouldUseTimeoutForAdminAccessingAdminConsole(RequiredAuthentication required, boolean isGuest)
|
private boolean shouldUseTimeoutForAdminAccessingAdminConsole(RequiredAuthentication required, boolean isGuest)
|
||||||
{
|
{
|
||||||
boolean useTimeoutForAdminAccessingAdminConsole = RequiredAuthentication.admin.equals(required) && !isGuest &&
|
boolean useTimeoutForAdminAccessingAdminConsole = RequiredAuthentication.admin.equals(required) && !isGuest &&
|
||||||
@@ -286,6 +366,18 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
return useTimeoutForAdminAccessingAdminConsole;
|
return useTimeoutForAdminAccessingAdminConsole;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldUseTimeoutForAdminAccessingWebScriptHome(RequiredAuthentication required, boolean isGuest)
|
||||||
|
{
|
||||||
|
boolean useTimeoutForAdminAccessingAdminConsole = RequiredAuthentication.admin.equals(required) && !isGuest &&
|
||||||
|
servletReq.getServiceMatch() != null && isWebScriptHome(servletReq.getServiceMatch().getWebScript());
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled())
|
||||||
|
{
|
||||||
|
LOGGER.trace("Should ensure that the admins can login with basic auth: " + useTimeoutForAdminAccessingAdminConsole);
|
||||||
|
}
|
||||||
|
return useTimeoutForAdminAccessingAdminConsole;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isRemoteUserMapperActive()
|
private boolean isRemoteUserMapperActive()
|
||||||
{
|
{
|
||||||
return remoteUserMapper != null && (!(remoteUserMapper instanceof ActivateableBean) || ((ActivateableBean) remoteUserMapper).isActive());
|
return remoteUserMapper != null && (!(remoteUserMapper instanceof ActivateableBean) || ((ActivateableBean) remoteUserMapper).isActive());
|
||||||
@@ -296,6 +388,11 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
return adminConsoleAuthenticator != null && (!(adminConsoleAuthenticator instanceof ActivateableBean) || ((ActivateableBean) adminConsoleAuthenticator).isActive());
|
return adminConsoleAuthenticator != null && (!(adminConsoleAuthenticator instanceof ActivateableBean) || ((ActivateableBean) adminConsoleAuthenticator).isActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isWebScriptAuthenticatorActive()
|
||||||
|
{
|
||||||
|
return webScriptHomeAuthenticator != null && (!(webScriptHomeAuthenticator instanceof ActivateableBean) || ((ActivateableBean) webScriptHomeAuthenticator).isActive());
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isAdminConsoleWebScript(WebScript webScript)
|
protected boolean isAdminConsoleWebScript(WebScript webScript)
|
||||||
{
|
{
|
||||||
if (webScript == null || adminConsoleScriptFamilies == null || webScript.getDescription() == null
|
if (webScript == null || adminConsoleScriptFamilies == null || webScript.getDescription() == null
|
||||||
@@ -322,6 +419,34 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
return isAdminConsole;
|
return isAdminConsole;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isWebScriptHome(WebScript webScript)
|
||||||
|
{
|
||||||
|
if (webScript == null || webScriptHomeFamilies == null || webScript.getDescription() == null
|
||||||
|
|| webScript.getDescription().getFamilys() == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled())
|
||||||
|
{
|
||||||
|
LOGGER.trace("WebScript: " + webScript + " has these families: " + webScript.getDescription().getFamilys());
|
||||||
|
}
|
||||||
|
|
||||||
|
// intersect the "family" sets defined
|
||||||
|
Set<String> families = new HashSet<>(webScript.getDescription()
|
||||||
|
.getFamilys());
|
||||||
|
families.retainAll(webScriptHomeFamilies);
|
||||||
|
final boolean isWebScriptHome = !families.isEmpty();
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled() && isWebScriptHome)
|
||||||
|
{
|
||||||
|
LOGGER.trace("Detected a WebScript Home webscript: " + webScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isWebScriptHome;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String getRemoteUserWithTimeout(boolean useTimeout) throws AuthenticationTimeoutException
|
protected String getRemoteUserWithTimeout(boolean useTimeout) throws AuthenticationTimeoutException
|
||||||
{
|
{
|
||||||
if (!useTimeout)
|
if (!useTimeout)
|
||||||
@@ -425,6 +550,21 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
|
|||||||
return userId;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String getWebScriptHomeUser()
|
||||||
|
{
|
||||||
|
String userId = null;
|
||||||
|
|
||||||
|
if (isRemoteUserMapperActive())
|
||||||
|
{
|
||||||
|
userId = webScriptHomeAuthenticator.getWebScriptHomeUser(this.servletReq.getHttpServletRequest(), this.servletRes.getHttpServletResponse());
|
||||||
|
}
|
||||||
|
|
||||||
|
logRemoteUserID(userId);
|
||||||
|
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class GetRemoteUserRunnable implements Runnable
|
class GetRemoteUserRunnable implements Runnable
|
||||||
{
|
{
|
||||||
private volatile String returnedRemoteUser;
|
private volatile String returnedRemoteUser;
|
||||||
|
@@ -5,4 +5,5 @@
|
|||||||
<authentication>guest</authentication>
|
<authentication>guest</authentication>
|
||||||
<transaction allow="readonly">required</transaction>
|
<transaction allow="readonly">required</transaction>
|
||||||
<lifecycle>internal</lifecycle>
|
<lifecycle>internal</lifecycle>
|
||||||
|
<family>Index</family>
|
||||||
</webscript>
|
</webscript>
|
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
|
||||||
|
<shortname>Web Script Maintenance</shortname>
|
||||||
|
|
||||||
|
<description>Maintain index of Web Scripts</description>
|
||||||
|
|
||||||
|
<url>/index?reset={reset?}</url>
|
||||||
|
|
||||||
|
<url>/?reset={reset?}</url>
|
||||||
|
|
||||||
|
<format default="html">any</format>
|
||||||
|
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
|
||||||
|
<family>Index</family>
|
||||||
|
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
|
||||||
|
<transaction allow="readwrite">required</transaction>
|
||||||
|
|
||||||
|
<cache>
|
||||||
|
|
||||||
|
<never>true</never>
|
||||||
|
|
||||||
|
<public>false</public>
|
||||||
|
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
|
||||||
|
</cache>
|
||||||
|
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
|
||||||
|
</webscript>
|
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
<shortname>All Web Scripts Index</shortname>
|
||||||
|
<description>Retrieve an index of all Web Scripts</description>
|
||||||
|
<url>/index/all?package={package?}&url={url?}&family={family?}</url>
|
||||||
|
<url>/index/all.mediawiki?package={package?}&url={url?}&family={family?}</url>
|
||||||
|
<format default="html">any</format>
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
<family>Index</family>
|
||||||
|
<transaction allow="readonly">required</transaction>
|
||||||
|
<cache>
|
||||||
|
<never>true</never>
|
||||||
|
<public>false</public>
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
</cache>
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
</webscript>
|
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
|
||||||
|
<shortname>Failed Web Scripts Index</shortname>
|
||||||
|
|
||||||
|
<description>Retrieve an index of all failed Web Scripts</description>
|
||||||
|
|
||||||
|
<url>/index/failures</url>
|
||||||
|
|
||||||
|
<format default="html">any</format>
|
||||||
|
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
|
||||||
|
<family>Index</family>
|
||||||
|
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
|
||||||
|
<transaction allow="readonly">required</transaction>
|
||||||
|
|
||||||
|
<cache>
|
||||||
|
|
||||||
|
<never>true</never>
|
||||||
|
|
||||||
|
<public>false</public>
|
||||||
|
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
|
||||||
|
</cache>
|
||||||
|
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
|
||||||
|
</webscript>
|
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
|
||||||
|
<shortname>Web Script Family Index</shortname>
|
||||||
|
|
||||||
|
<description>Provide an index of Web Scripts for the specified family</description>
|
||||||
|
|
||||||
|
<url>/index/family/{family}</url>
|
||||||
|
|
||||||
|
<format default="html">any</format>
|
||||||
|
|
||||||
|
<family>Index</family>
|
||||||
|
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
|
||||||
|
<transaction allow="readonly">required</transaction>
|
||||||
|
|
||||||
|
<cache>
|
||||||
|
|
||||||
|
<never>true</never>
|
||||||
|
|
||||||
|
<public>false</public>
|
||||||
|
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
|
||||||
|
</cache>
|
||||||
|
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
|
||||||
|
</webscript>
|
@@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
|
||||||
|
<shortname>Web Script Lifecycle Index</shortname>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
Provide an index of Web Scripts in the various lifecycle states:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>none : This web script is not part of a lifecycle</li>
|
||||||
|
|
||||||
|
<li>sample : This web script is a sample and is not intended for production use</li>
|
||||||
|
|
||||||
|
<li>draft : This method may be incomplete, experimental or still subject to change</li>
|
||||||
|
|
||||||
|
<li>public_api : This method is part of the Alfresco public api and should be stable and well tested</li>
|
||||||
|
|
||||||
|
<li>draft_public_api : This method is intended to eventually become part of the public api but is
|
||||||
|
incomplete or still subject to change</li>
|
||||||
|
|
||||||
|
<li>deprecated : This method should be avoided. It may be removed in future versions of Alfresco</li>
|
||||||
|
|
||||||
|
<li>internal : This script is for Alfresco use only. This script should not be relied upon between
|
||||||
|
versions. It is likely to change</li>
|
||||||
|
|
||||||
|
<li>limited_support : This web script is no longer being actively developed</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<url>/index/lifecycle/{lifecycle}</url>
|
||||||
|
|
||||||
|
<format default="html">any</format>
|
||||||
|
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
|
||||||
|
<family>Index</family>
|
||||||
|
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
|
||||||
|
<transaction allow="readonly">required</transaction>
|
||||||
|
|
||||||
|
<cache>
|
||||||
|
|
||||||
|
<never>true</never>
|
||||||
|
|
||||||
|
<public>false</public>
|
||||||
|
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
|
||||||
|
</cache>
|
||||||
|
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
|
||||||
|
</webscript>
|
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
|
||||||
|
<shortname>Web Script Package Index</shortname>
|
||||||
|
|
||||||
|
<description>Provide an index of Web Scripts for the specified Web Script package</description>
|
||||||
|
|
||||||
|
<url>/index/package/{package}</url>
|
||||||
|
|
||||||
|
<format default="html">any</format>
|
||||||
|
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
|
||||||
|
<family>Index</family>
|
||||||
|
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
|
||||||
|
<transaction allow="readonly">required</transaction>
|
||||||
|
|
||||||
|
<cache>
|
||||||
|
|
||||||
|
<never>true</never>
|
||||||
|
|
||||||
|
<public>false</public>
|
||||||
|
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
|
||||||
|
</cache>
|
||||||
|
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
|
||||||
|
</webscript>
|
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
|
||||||
|
<shortname>Web Script URI Index</shortname>
|
||||||
|
|
||||||
|
<description>Provide an index of Web Scripts for the specified Web Script URI</description>
|
||||||
|
|
||||||
|
<url>/index/uri/{uri}</url>
|
||||||
|
|
||||||
|
<format default="html">argument</format>
|
||||||
|
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
|
||||||
|
<family>Index</family>
|
||||||
|
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
|
||||||
|
<transaction allow="readonly">required</transaction>
|
||||||
|
|
||||||
|
<cache>
|
||||||
|
|
||||||
|
<never>true</never>
|
||||||
|
|
||||||
|
<public>false</public>
|
||||||
|
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
|
||||||
|
</cache>
|
||||||
|
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
|
||||||
|
</webscript>
|
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
|
||||||
|
<shortname>Javascript Debugger</shortname>
|
||||||
|
|
||||||
|
<description>Javascript Debugger</description>
|
||||||
|
|
||||||
|
<url>/api/javascript/debugger</url>
|
||||||
|
|
||||||
|
<format default="html">any</format>
|
||||||
|
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
|
||||||
|
<transaction allow="readonly">required</transaction>
|
||||||
|
|
||||||
|
<family>Index</family>
|
||||||
|
|
||||||
|
<cache>
|
||||||
|
|
||||||
|
<never>true</never>
|
||||||
|
|
||||||
|
<public>false</public>
|
||||||
|
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
|
||||||
|
</cache>
|
||||||
|
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
|
||||||
|
</webscript>
|
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
|
||||||
|
<shortname>Javascript Debugger Maintenance</shortname>
|
||||||
|
|
||||||
|
<description>Javascript Debugger Maintenance</description>
|
||||||
|
|
||||||
|
<url>/api/javascript/debugger?active={active?}</url>
|
||||||
|
|
||||||
|
<format default="html">any</format>
|
||||||
|
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
|
||||||
|
<family>Index</family>
|
||||||
|
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
|
||||||
|
<transaction allow="readonly">required</transaction>
|
||||||
|
|
||||||
|
<cache>
|
||||||
|
|
||||||
|
<never>true</never>
|
||||||
|
|
||||||
|
<public>false</public>
|
||||||
|
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
|
||||||
|
</cache>
|
||||||
|
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
|
||||||
|
</webscript>
|
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
|
||||||
|
<shortname>Web Script Description</shortname>
|
||||||
|
|
||||||
|
<description>Retrieve description document for identified Web Script</description>
|
||||||
|
|
||||||
|
<url>/description/{serviceId}</url>
|
||||||
|
|
||||||
|
<format default="html">argument</format>
|
||||||
|
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
|
||||||
|
<family>Admin</family>
|
||||||
|
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
|
||||||
|
<transaction allow="readonly">required</transaction>
|
||||||
|
|
||||||
|
<cache>
|
||||||
|
|
||||||
|
<never>true</never>
|
||||||
|
|
||||||
|
<public>false</public>
|
||||||
|
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
|
||||||
|
</cache>
|
||||||
|
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
|
||||||
|
</webscript>
|
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.springsurf.org/schemas/DescriptionXMLSchema.xsd">
|
||||||
|
|
||||||
|
<shortname>Display Web Script</shortname>
|
||||||
|
|
||||||
|
<description>Full inspection of Web Script implementation - useful for diagnostics and download/upload</description>
|
||||||
|
|
||||||
|
<url>/script/{serviceId}</url>
|
||||||
|
|
||||||
|
<format default="html">argument</format>
|
||||||
|
|
||||||
|
<lifecycle>internal</lifecycle>
|
||||||
|
|
||||||
|
<authentication>admin</authentication>
|
||||||
|
|
||||||
|
<transaction allow="readonly">required</transaction>
|
||||||
|
|
||||||
|
<family>Admin</family>
|
||||||
|
|
||||||
|
<cache>
|
||||||
|
|
||||||
|
<never>true</never>
|
||||||
|
|
||||||
|
<public>false</public>
|
||||||
|
|
||||||
|
<mustrevalidate>true</mustrevalidate>
|
||||||
|
|
||||||
|
</cache>
|
||||||
|
|
||||||
|
<formdata multipart-processing="true"/>
|
||||||
|
|
||||||
|
</webscript>
|
@@ -214,9 +214,13 @@
|
|||||||
<property name="authenticationListener" ref="webScriptAuthenticationListener"/>
|
<property name="authenticationListener" ref="webScriptAuthenticationListener"/>
|
||||||
<property name="remoteUserMapper" ref="RemoteUserMapper" />
|
<property name="remoteUserMapper" ref="RemoteUserMapper" />
|
||||||
<property name="adminConsoleAuthenticator" ref="AdminConsoleAuthenticator" />
|
<property name="adminConsoleAuthenticator" ref="AdminConsoleAuthenticator" />
|
||||||
|
<property name="webScriptHomeAuthenticator" ref="WebScriptHomeAuthenticator" />
|
||||||
<property name="alwaysAllowBasicAuthForAdminConsole">
|
<property name="alwaysAllowBasicAuthForAdminConsole">
|
||||||
<value>${authentication.alwaysAllowBasicAuthForAdminConsole.enabled}</value>
|
<value>${authentication.alwaysAllowBasicAuthForAdminConsole.enabled}</value>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="alwaysAllowBasicAuthForWebScriptHome">
|
||||||
|
<value>${authentication.alwaysAllowBasicAuthForWebScriptHome.enabled}</value>
|
||||||
|
</property>
|
||||||
<property name="getRemoteUserTimeoutMilliseconds">
|
<property name="getRemoteUserTimeoutMilliseconds">
|
||||||
<value>${authentication.getRemoteUserTimeoutMilliseconds}</value>
|
<value>${authentication.getRemoteUserTimeoutMilliseconds}</value>
|
||||||
</property>
|
</property>
|
||||||
@@ -224,9 +228,25 @@
|
|||||||
<list>
|
<list>
|
||||||
<value>AdminConsole</value>
|
<value>AdminConsole</value>
|
||||||
<value>AdminConsoleHelper</value>
|
<value>AdminConsoleHelper</value>
|
||||||
<value>Index</value>
|
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="webScriptHomeFamilies">
|
||||||
|
<list>
|
||||||
|
<value>Index</value>
|
||||||
|
<value>Admin</value>
|
||||||
|
<value>Content Applications</value>
|
||||||
|
<value>Audit</value>
|
||||||
|
<value>Authentication</value>
|
||||||
|
<value>Bulk Filesystem Import</value>
|
||||||
|
<value>IMAP</value>
|
||||||
|
<value>MultiTenantAdmin</value>
|
||||||
|
<value>QuADDS</value>
|
||||||
|
<value>Remote Share</value>
|
||||||
|
<value>SOLR</value>
|
||||||
|
<value>Tagging</value>
|
||||||
|
<value>googledocs</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<!-- HTTP Basic Authenticator (Servlet based - stateless) for MT -->
|
<!-- HTTP Basic Authenticator (Servlet based - stateless) for MT -->
|
||||||
|
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.external;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.alfresco.repo.management.subsystems.ActivateableBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A default {@link WebScriptHomeAuthenticator} implementation. Returns null to request a basic auth challenge.
|
||||||
|
*/
|
||||||
|
public class DefaultWebScriptHomeAuthenticator implements WebScriptHomeAuthenticator, ActivateableBean
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String getWebScriptHomeUser(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestAuthentication(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
// No implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.external;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for objects capable of extracting an externally authenticated user ID from the HTTP WebScripts Home request.
|
||||||
|
*/
|
||||||
|
public interface WebScriptHomeAuthenticator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Gets an externally authenticated user ID from the HTTP WebScripts Home request.
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* the request
|
||||||
|
* @param response
|
||||||
|
* the response
|
||||||
|
* @return the user ID or <code>null</code> if the user is unauthenticated
|
||||||
|
*/
|
||||||
|
String getWebScriptHomeUser(HttpServletRequest request, HttpServletResponse response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests an authentication for accessing WebScripts Home.
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* the request
|
||||||
|
* @param response
|
||||||
|
* the response
|
||||||
|
*/
|
||||||
|
void requestAuthentication(HttpServletRequest request, HttpServletResponse response);
|
||||||
|
}
|
||||||
|
|
@@ -77,6 +77,7 @@ public class IdentityServiceConfig
|
|||||||
private String emailAttribute;
|
private String emailAttribute;
|
||||||
private long jwtClockSkewMs;
|
private long jwtClockSkewMs;
|
||||||
private String webScriptHomeRedirectPath;
|
private String webScriptHomeRedirectPath;
|
||||||
|
private String webScriptHomeScopes;
|
||||||
|
|
||||||
public String getWebScriptHomeRedirectPath()
|
public String getWebScriptHomeRedirectPath()
|
||||||
{
|
{
|
||||||
@@ -370,6 +371,18 @@ public class IdentityServiceConfig
|
|||||||
this.adminConsoleScopes = adminConsoleScopes;
|
this.adminConsoleScopes = adminConsoleScopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<String> getWebScriptHomeScopes()
|
||||||
|
{
|
||||||
|
return Stream.of(webScriptHomeScopes.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWebScriptHomeScopes(String webScriptHomeScopes)
|
||||||
|
{
|
||||||
|
this.webScriptHomeScopes = webScriptHomeScopes;
|
||||||
|
}
|
||||||
|
|
||||||
public Set<String> getPasswordGrantScopes()
|
public Set<String> getPasswordGrantScopes()
|
||||||
{
|
{
|
||||||
return Stream.of(passwordGrantScopes.split(","))
|
return Stream.of(passwordGrantScopes.split(","))
|
||||||
|
@@ -240,10 +240,6 @@ public class IdentityServiceAdminConsoleAuthenticator implements AdminConsoleAut
|
|||||||
{
|
{
|
||||||
URI originalUri = new URI(requestURL);
|
URI originalUri = new URI(requestURL);
|
||||||
String redirectPath = identityServiceConfig.getAdminConsoleRedirectPath();
|
String redirectPath = identityServiceConfig.getAdminConsoleRedirectPath();
|
||||||
if (originalUri.getPath().equals(identityServiceConfig.getWebScriptHomeRedirectPath()))
|
|
||||||
{
|
|
||||||
redirectPath = originalUri.getPath();
|
|
||||||
}
|
|
||||||
URI redirectUri = new URI(originalUri.getScheme(), originalUri.getAuthority(), redirectPath, originalUri.getQuery(), originalUri.getFragment());
|
URI redirectUri = new URI(originalUri.getScheme(), originalUri.getAuthority(), redirectPath, originalUri.getQuery(), originalUri.getFragment());
|
||||||
return redirectUri.toASCIIString();
|
return redirectUri.toASCIIString();
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,333 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.identityservice.webscript;
|
||||||
|
|
||||||
|
import static org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant.authorizationCode;
|
||||||
|
import static org.alfresco.repo.security.authentication.identityservice.IdentityServiceMetadataKey.SCOPES_SUPPORTED;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import com.nimbusds.oauth2.sdk.Scope;
|
||||||
|
import com.nimbusds.oauth2.sdk.id.Identifier;
|
||||||
|
import com.nimbusds.oauth2.sdk.id.State;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.external.WebScriptHomeAuthenticator;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import org.alfresco.repo.management.subsystems.ActivateableBean;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||||
|
import org.alfresco.repo.security.authentication.external.RemoteUserMapper;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceConfig;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessTokenAuthorization;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationException;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link WebScriptHomeAuthenticator} implementation to extract an externally authenticated user ID or to initiate the OIDC authorization code flow.
|
||||||
|
*/
|
||||||
|
public class IdentityServiceWebScriptHomeAuthenticator implements WebScriptHomeAuthenticator, ActivateableBean
|
||||||
|
{
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(IdentityServiceWebScriptHomeAuthenticator.class);
|
||||||
|
|
||||||
|
private static final String ALFRESCO_ACCESS_TOKEN = "ALFRESCO_ACCESS_TOKEN";
|
||||||
|
private static final String ALFRESCO_REFRESH_TOKEN = "ALFRESCO_REFRESH_TOKEN";
|
||||||
|
private static final String ALFRESCO_TOKEN_EXPIRATION = "ALFRESCO_TOKEN_EXPIRATION";
|
||||||
|
|
||||||
|
private IdentityServiceConfig identityServiceConfig;
|
||||||
|
private IdentityServiceFacade identityServiceFacade;
|
||||||
|
private WebScriptHomeAuthenticationCookiesService cookiesService;
|
||||||
|
private RemoteUserMapper remoteUserMapper;
|
||||||
|
private boolean isEnabled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWebScriptHomeUser(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
String username = remoteUserMapper.getRemoteUser(request);
|
||||||
|
if (username != null)
|
||||||
|
{
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
String bearerToken = cookiesService.getCookie(ALFRESCO_ACCESS_TOKEN, request);
|
||||||
|
|
||||||
|
if (bearerToken != null)
|
||||||
|
{
|
||||||
|
bearerToken = refreshTokenIfNeeded(request, response, bearerToken);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String code = request.getParameter("code");
|
||||||
|
if (code != null)
|
||||||
|
{
|
||||||
|
bearerToken = retrieveTokenUsingAuthCode(request, response, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bearerToken == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteUserMapper.getRemoteUser(decorateBearerHeader(bearerToken, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestAuthentication(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
respondWithAuthChallenge(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void respondWithAuthChallenge(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (LOGGER.isDebugEnabled())
|
||||||
|
{
|
||||||
|
LOGGER.debug("Responding with the authentication challenge");
|
||||||
|
}
|
||||||
|
response.sendRedirect(getAuthenticationRequest(request));
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
LOGGER.error("WebScript Home Auth challenge failed: {}", e.getMessage(), e);
|
||||||
|
throw new AuthenticationException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String retrieveTokenUsingAuthCode(HttpServletRequest request, HttpServletResponse response, String code)
|
||||||
|
{
|
||||||
|
String bearerToken = null;
|
||||||
|
if (LOGGER.isDebugEnabled())
|
||||||
|
{
|
||||||
|
LOGGER.debug("Retrieving a response using the Authorization Code at the Token Endpoint");
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AccessTokenAuthorization accessTokenAuthorization = identityServiceFacade.authorize(
|
||||||
|
authorizationCode(code, request.getRequestURL().toString()));
|
||||||
|
addCookies(response, accessTokenAuthorization);
|
||||||
|
bearerToken = accessTokenAuthorization.getAccessToken().getTokenValue();
|
||||||
|
}
|
||||||
|
catch (AuthorizationException exception)
|
||||||
|
{
|
||||||
|
if (LOGGER.isWarnEnabled())
|
||||||
|
{
|
||||||
|
LOGGER.warn(
|
||||||
|
"Error while trying to retrieve a response using the Authorization Code at the Token Endpoint: {}",
|
||||||
|
exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bearerToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String refreshTokenIfNeeded(HttpServletRequest request, HttpServletResponse response, String bearerToken)
|
||||||
|
{
|
||||||
|
String refreshToken = cookiesService.getCookie(ALFRESCO_REFRESH_TOKEN, request);
|
||||||
|
String authTokenExpiration = cookiesService.getCookie(ALFRESCO_TOKEN_EXPIRATION, request);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isAuthTokenExpired(authTokenExpiration))
|
||||||
|
{
|
||||||
|
bearerToken = refreshAuthToken(refreshToken, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.debug("WebScript token refresh failed: {}", e.getMessage());
|
||||||
|
bearerToken = null;
|
||||||
|
resetCookies(response);
|
||||||
|
}
|
||||||
|
return bearerToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCookies(HttpServletResponse response, AccessTokenAuthorization accessTokenAuthorization)
|
||||||
|
{
|
||||||
|
cookiesService.addCookie(ALFRESCO_ACCESS_TOKEN, accessTokenAuthorization.getAccessToken().getTokenValue(), response);
|
||||||
|
cookiesService.addCookie(ALFRESCO_TOKEN_EXPIRATION, String.valueOf(accessTokenAuthorization.getAccessToken().getExpiresAt().toEpochMilli()), response);
|
||||||
|
cookiesService.addCookie(ALFRESCO_REFRESH_TOKEN, accessTokenAuthorization.getRefreshTokenValue(), response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAuthenticationRequest(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
ClientRegistration clientRegistration = identityServiceFacade.getClientRegistration();
|
||||||
|
State state = new State();
|
||||||
|
|
||||||
|
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getAuthorizationUri())
|
||||||
|
.queryParam("client_id", clientRegistration.getClientId())
|
||||||
|
.queryParam("redirect_uri", getRedirectUri(request.getRequestURL().toString()))
|
||||||
|
.queryParam("response_type", "code")
|
||||||
|
.queryParam("scope", String.join("+", getScopes(clientRegistration)))
|
||||||
|
.queryParam("state", state.toString());
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(identityServiceConfig.getAudience()))
|
||||||
|
{
|
||||||
|
builder.queryParam("audience", identityServiceConfig.getAudience());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build().toUriString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> getScopes(ClientRegistration clientRegistration)
|
||||||
|
{
|
||||||
|
return Optional.ofNullable(clientRegistration.getProviderDetails())
|
||||||
|
.map(ProviderDetails::getConfigurationMetadata)
|
||||||
|
.map(metadata -> metadata.get(SCOPES_SUPPORTED.getValue()))
|
||||||
|
.filter(Scope.class::isInstance)
|
||||||
|
.map(Scope.class::cast)
|
||||||
|
.map(this::getSupportedScopes)
|
||||||
|
.orElse(clientRegistration.getScopes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> getSupportedScopes(Scope scopes)
|
||||||
|
{
|
||||||
|
return scopes.stream()
|
||||||
|
.filter(this::hasWebScriptHomeScope)
|
||||||
|
.map(Identifier::getValue)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasWebScriptHomeScope(Scope.Value scope)
|
||||||
|
{
|
||||||
|
return identityServiceConfig.getWebScriptHomeScopes().contains(scope.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRedirectUri(String requestURL)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
URI originalUri = new URI(requestURL);
|
||||||
|
|
||||||
|
// Keep full original path so we return to the correct page after login
|
||||||
|
String fullOriginalPath = originalUri.getPath();
|
||||||
|
String query = originalUri.getQuery();
|
||||||
|
String fragment = originalUri.getFragment();
|
||||||
|
|
||||||
|
URI redirectUri = new URI(
|
||||||
|
originalUri.getScheme(),
|
||||||
|
originalUri.getAuthority(),
|
||||||
|
fullOriginalPath, // preserves /alfresco/s/index/** whatever it is
|
||||||
|
query,
|
||||||
|
fragment
|
||||||
|
);
|
||||||
|
|
||||||
|
return redirectUri.toASCIIString();
|
||||||
|
}
|
||||||
|
catch (URISyntaxException e)
|
||||||
|
{
|
||||||
|
LOGGER.error("WebScript redirect URI construction failed: {}", e.getMessage(), e);
|
||||||
|
throw new AuthenticationException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void resetCookies(HttpServletResponse response)
|
||||||
|
{
|
||||||
|
cookiesService.resetCookie(ALFRESCO_TOKEN_EXPIRATION, response);
|
||||||
|
cookiesService.resetCookie(ALFRESCO_ACCESS_TOKEN, response);
|
||||||
|
cookiesService.resetCookie(ALFRESCO_REFRESH_TOKEN, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String refreshAuthToken(String refreshToken, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
AccessTokenAuthorization accessTokenAuthorization = doRefreshAuthToken(refreshToken);
|
||||||
|
addCookies(response, accessTokenAuthorization);
|
||||||
|
return accessTokenAuthorization.getAccessToken().getTokenValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccessTokenAuthorization doRefreshAuthToken(String refreshToken)
|
||||||
|
{
|
||||||
|
AccessTokenAuthorization accessTokenAuthorization = identityServiceFacade.authorize(
|
||||||
|
AuthorizationGrant.refreshToken(refreshToken));
|
||||||
|
if (accessTokenAuthorization == null || accessTokenAuthorization.getAccessToken() == null)
|
||||||
|
{
|
||||||
|
throw new AuthenticationException("WebScript refresh token response is invalid.");
|
||||||
|
}
|
||||||
|
return accessTokenAuthorization;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAuthTokenExpired(String authTokenExpiration)
|
||||||
|
{
|
||||||
|
return Instant.now().compareTo(Instant.ofEpochMilli(Long.parseLong(authTokenExpiration))) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpServletRequest decorateBearerHeader(String authToken, HttpServletRequest servletRequest)
|
||||||
|
{
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Authorization", "Bearer " + authToken);
|
||||||
|
return new WebScriptHomeHttpServletRequestWrapper(headers, servletRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIdentityServiceFacade(IdentityServiceFacade identityServiceFacade)
|
||||||
|
{
|
||||||
|
this.identityServiceFacade = identityServiceFacade;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRemoteUserMapper(RemoteUserMapper remoteUserMapper)
|
||||||
|
{
|
||||||
|
this.remoteUserMapper = remoteUserMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookiesService(WebScriptHomeAuthenticationCookiesService cookiesService)
|
||||||
|
{
|
||||||
|
this.cookiesService = cookiesService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIdentityServiceConfig(IdentityServiceConfig identityServiceConfig)
|
||||||
|
{
|
||||||
|
this.identityServiceConfig = identityServiceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive()
|
||||||
|
{
|
||||||
|
return this.isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActive(boolean isEnabled)
|
||||||
|
{
|
||||||
|
this.isEnabled = isEnabled;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.identityservice.webscript;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.Cookie;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.alfresco.repo.admin.SysAdminParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to handle Web Script authentication-related cookies.
|
||||||
|
*/
|
||||||
|
public class WebScriptHomeAuthenticationCookiesService
|
||||||
|
{
|
||||||
|
private final SysAdminParams sysAdminParams;
|
||||||
|
private final int cookieLifetime;
|
||||||
|
|
||||||
|
public WebScriptHomeAuthenticationCookiesService(SysAdminParams sysAdminParams, int cookieLifetime)
|
||||||
|
{
|
||||||
|
this.sysAdminParams = sysAdminParams;
|
||||||
|
this.cookieLifetime = cookieLifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cookie with the given name.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name of the cookie
|
||||||
|
* @param request
|
||||||
|
* the request that might contain the cookie
|
||||||
|
* @return the cookie value, or null if the cookie cannot be found
|
||||||
|
*/
|
||||||
|
public String getCookie(String name, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String result = null;
|
||||||
|
Cookie[] cookies = request.getCookies();
|
||||||
|
|
||||||
|
if (cookies != null)
|
||||||
|
{
|
||||||
|
for (Cookie cookie : cookies)
|
||||||
|
{
|
||||||
|
if (cookie.getName().equals(name))
|
||||||
|
{
|
||||||
|
result = cookie.getValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a cookie to the response.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name of the cookie
|
||||||
|
* @param value
|
||||||
|
* the value of the cookie
|
||||||
|
* @param servletResponse
|
||||||
|
* the response to add the cookie to
|
||||||
|
*/
|
||||||
|
public void addCookie(String name, String value, HttpServletResponse servletResponse)
|
||||||
|
{
|
||||||
|
internalAddCookie(name, value, cookieLifetime, servletResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issue a cookie reset within the given response.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the cookie to reset
|
||||||
|
* @param servletResponse
|
||||||
|
* the response to issue the cookie reset
|
||||||
|
*/
|
||||||
|
public void resetCookie(String name, HttpServletResponse servletResponse)
|
||||||
|
{
|
||||||
|
internalAddCookie(name, "", 0, servletResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void internalAddCookie(String name, String value, int maxAge, HttpServletResponse servletResponse)
|
||||||
|
{
|
||||||
|
Cookie authCookie = new Cookie(name, value);
|
||||||
|
authCookie.setPath("/"); // Set the cookie's valid path
|
||||||
|
authCookie.setMaxAge(maxAge); // Set expiration time (in seconds)
|
||||||
|
|
||||||
|
// Ensure the cookie is only transmitted over secure connections (HTTPS)
|
||||||
|
authCookie.setSecure(sysAdminParams.getAlfrescoProtocol().equalsIgnoreCase("https"));
|
||||||
|
|
||||||
|
// Prevent JavaScript access to this cookie for security reasons (XSS protection)
|
||||||
|
authCookie.setHttpOnly(true);
|
||||||
|
|
||||||
|
// Add the cookie to the response
|
||||||
|
servletResponse.addCookie(authCookie);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2023 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.identityservice.webscript;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.enumeration;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||||
|
|
||||||
|
import org.alfresco.util.PropertyCheck;
|
||||||
|
|
||||||
|
public class WebScriptHomeHttpServletRequestWrapper extends HttpServletRequestWrapper
|
||||||
|
{
|
||||||
|
private final Map<String, String> additionalHeaders;
|
||||||
|
private final HttpServletRequest wrappedRequest;
|
||||||
|
|
||||||
|
public WebScriptHomeHttpServletRequestWrapper(Map<String, String> additionalHeaders, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
super(request);
|
||||||
|
PropertyCheck.mandatory(this, "additionalHeaders", additionalHeaders);
|
||||||
|
this.additionalHeaders = additionalHeaders;
|
||||||
|
this.wrappedRequest = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<String> getHeaderNames()
|
||||||
|
{
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
Enumeration<String> originalHeaders = wrappedRequest.getHeaderNames();
|
||||||
|
if (originalHeaders != null)
|
||||||
|
{
|
||||||
|
while (originalHeaders.hasMoreElements())
|
||||||
|
{
|
||||||
|
String header = originalHeaders.nextElement();
|
||||||
|
if (!additionalHeaders.containsKey(header))
|
||||||
|
{
|
||||||
|
result.add(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.addAll(additionalHeaders.keySet());
|
||||||
|
return enumeration(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String name)
|
||||||
|
{
|
||||||
|
return additionalHeaders.getOrDefault(name, super.getHeader(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<String> getHeaders(String name)
|
||||||
|
{
|
||||||
|
return enumeration(asList(additionalHeaders.getOrDefault(name, super.getHeader(name))));
|
||||||
|
}
|
||||||
|
}
|
@@ -144,6 +144,22 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="WebScriptHomeAuthenticator"
|
||||||
|
class="org.alfresco.repo.management.subsystems.ChainingSubsystemProxyFactory">
|
||||||
|
<property name="applicationContextManager">
|
||||||
|
<ref bean="Authentication" />
|
||||||
|
</property>
|
||||||
|
<property name="interfaces">
|
||||||
|
<list>
|
||||||
|
<value>org.alfresco.repo.security.authentication.external.WebScriptHomeAuthenticator</value>
|
||||||
|
<value>org.alfresco.repo.management.subsystems.ActivateableBean</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="sourceBeanName">
|
||||||
|
<value>webScriptHomeAuthenticator</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- Passwords are encoded using MD4 -->
|
<!-- Passwords are encoded using MD4 -->
|
||||||
<!-- This is not ideal and only done to be compatible with NTLM -->
|
<!-- This is not ideal and only done to be compatible with NTLM -->
|
||||||
<!-- authentication against the default authentication mechanism. -->
|
<!-- authentication against the default authentication mechanism. -->
|
||||||
|
@@ -563,6 +563,7 @@ authentication.ticket.validDuration=PT1H
|
|||||||
authentication.ticket.useSingleTicketPerUser=true
|
authentication.ticket.useSingleTicketPerUser=true
|
||||||
|
|
||||||
authentication.alwaysAllowBasicAuthForAdminConsole.enabled=true
|
authentication.alwaysAllowBasicAuthForAdminConsole.enabled=true
|
||||||
|
authentication.alwaysAllowBasicAuthForWebScriptHome.enabled=true
|
||||||
authentication.getRemoteUserTimeoutMilliseconds=10000
|
authentication.getRemoteUserTimeoutMilliseconds=10000
|
||||||
|
|
||||||
# FTP access
|
# FTP access
|
||||||
|
@@ -104,4 +104,7 @@
|
|||||||
<ref bean="transactionService" />
|
<ref bean="transactionService" />
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="webScriptHomeAuthenticator" class="org.alfresco.repo.security.authentication.external.DefaultWebScriptHomeAuthenticator" />
|
||||||
|
|
||||||
</beans>
|
</beans>
|
@@ -170,6 +170,9 @@
|
|||||||
<property name="adminConsoleScopes">
|
<property name="adminConsoleScopes">
|
||||||
<value>${identity-service.admin-console.scopes:openid,profile,email,offline_access}</value>
|
<value>${identity-service.admin-console.scopes:openid,profile,email,offline_access}</value>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="webScriptHomeScopes">
|
||||||
|
<value>${identity-service.webscript-home.scopes:openid,profile,email,offline_access}</value>
|
||||||
|
</property>
|
||||||
<property name="passwordGrantScopes">
|
<property name="passwordGrantScopes">
|
||||||
<value>${identity-service.password-grant.scopes:openid,profile,email}</value>
|
<value>${identity-service.password-grant.scopes:openid,profile,email}</value>
|
||||||
</property>
|
</property>
|
||||||
@@ -205,6 +208,11 @@
|
|||||||
<constructor-arg value="${admin.console.cookie.lifetime:86400}" />
|
<constructor-arg value="${admin.console.cookie.lifetime:86400}" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="webScriptHomeAuthenticationCookiesService" class="org.alfresco.repo.security.authentication.identityservice.webscript.WebScriptHomeAuthenticationCookiesService">
|
||||||
|
<constructor-arg ref="sysAdminParams" />
|
||||||
|
<constructor-arg value="${admin.console.cookie.lifetime:86400}" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="adminConsoleAuthenticator" class="org.alfresco.repo.security.authentication.identityservice.admin.IdentityServiceAdminConsoleAuthenticator">
|
<bean id="adminConsoleAuthenticator" class="org.alfresco.repo.security.authentication.identityservice.admin.IdentityServiceAdminConsoleAuthenticator">
|
||||||
<property name="active">
|
<property name="active">
|
||||||
<value>${identity-service.authentication.enabled}</value>
|
<value>${identity-service.authentication.enabled}</value>
|
||||||
@@ -223,6 +231,24 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="webScriptHomeAuthenticator" class="org.alfresco.repo.security.authentication.identityservice.webscript.IdentityServiceWebScriptHomeAuthenticator">
|
||||||
|
<property name="active">
|
||||||
|
<value>${identity-service.authentication.enabled}</value>
|
||||||
|
</property>
|
||||||
|
<property name="identityServiceFacade">
|
||||||
|
<ref bean="identityServiceFacade"/>
|
||||||
|
</property>
|
||||||
|
<property name="cookiesService">
|
||||||
|
<ref bean="webScriptHomeAuthenticationCookiesService" />
|
||||||
|
</property>
|
||||||
|
<property name="remoteUserMapper">
|
||||||
|
<ref bean="remoteUserMapper" />
|
||||||
|
</property>
|
||||||
|
<property name="identityServiceConfig">
|
||||||
|
<ref bean="identityServiceConfig" />
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="jitProvisioningHandler" class="org.alfresco.repo.security.authentication.identityservice.IdentityServiceJITProvisioningHandler">
|
<bean id="jitProvisioningHandler" class="org.alfresco.repo.security.authentication.identityservice.IdentityServiceJITProvisioningHandler">
|
||||||
<constructor-arg ref="PersonService"/>
|
<constructor-arg ref="PersonService"/>
|
||||||
<constructor-arg ref="identityServiceFacade"/>
|
<constructor-arg ref="identityServiceFacade"/>
|
||||||
|
@@ -18,6 +18,7 @@ identity-service.first-name-attribute=given_name
|
|||||||
identity-service.last-name-attribute=family_name
|
identity-service.last-name-attribute=family_name
|
||||||
identity-service.email-attribute=email
|
identity-service.email-attribute=email
|
||||||
identity-service.admin-console.scopes=openid,profile,email,offline_access
|
identity-service.admin-console.scopes=openid,profile,email,offline_access
|
||||||
|
identity-service.webscript-home.scopes=openid,profile,email,offline_access
|
||||||
identity-service.password-grant.scopes=openid,profile,email
|
identity-service.password-grant.scopes=openid,profile,email
|
||||||
identity-service.issuer-attribute=issuer
|
identity-service.issuer-attribute=issuer
|
||||||
identity-service.jwt-clock-skew-ms=0
|
identity-service.jwt-clock-skew-ms=0
|
||||||
|
@@ -90,8 +90,6 @@ public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
|||||||
|
|
||||||
StringBuffer adminConsoleURL = new StringBuffer("http://localhost:8080/admin-console");
|
StringBuffer adminConsoleURL = new StringBuffer("http://localhost:8080/admin-console");
|
||||||
|
|
||||||
StringBuffer webScriptHomeURL = new StringBuffer("http://localhost:8080/alfresco/s/index");
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup()
|
public void setup()
|
||||||
{
|
{
|
||||||
@@ -176,29 +174,6 @@ public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
|||||||
assertTrue(authenticationRequest.getValue().contains("state"));
|
assertTrue(authenticationRequest.getValue().contains("state"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldCallAuthChallengeWebScriptHome() throws IOException
|
|
||||||
{
|
|
||||||
|
|
||||||
String redirectPath = "/alfresco/s/index";
|
|
||||||
when(request.getRequestURL()).thenReturn(webScriptHomeURL);
|
|
||||||
when(identityServiceConfig.getAdminConsoleScopes()).thenReturn(Set.of("openid", "email", "profile", "offline_access"));
|
|
||||||
when(identityServiceConfig.getWebScriptHomeRedirectPath()).thenReturn(redirectPath);
|
|
||||||
ArgumentCaptor<String> authenticationRequest = ArgumentCaptor.forClass(String.class);
|
|
||||||
String expectedUri = "http://localhost:8999/auth?client_id=alfresco&redirect_uri=%s%s&response_type=code&scope="
|
|
||||||
.formatted("http://localhost:8080", redirectPath);
|
|
||||||
|
|
||||||
authenticator.requestAuthentication(request, response);
|
|
||||||
|
|
||||||
verify(response).sendRedirect(authenticationRequest.capture());
|
|
||||||
assertTrue(authenticationRequest.getValue().contains(expectedUri));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("openid"));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("profile"));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("email"));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("offline_access"));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("state"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCallAuthChallengeWithAudience() throws IOException
|
public void shouldCallAuthChallengeWithAudience() throws IOException
|
||||||
{
|
{
|
||||||
@@ -224,31 +199,6 @@ public class IdentityServiceAdminConsoleAuthenticatorUnitTest
|
|||||||
assertTrue(authenticationRequest.getValue().contains("state"));
|
assertTrue(authenticationRequest.getValue().contains("state"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldCallAuthChallengeWebScriptHomeWithAudience() throws IOException
|
|
||||||
{
|
|
||||||
String audience = "http://localhost:8082";
|
|
||||||
String redirectPath = "/alfresco/s/index";
|
|
||||||
when(request.getRequestURL()).thenReturn(webScriptHomeURL);
|
|
||||||
when(identityServiceConfig.getAudience()).thenReturn(audience);
|
|
||||||
when(identityServiceConfig.getWebScriptHomeRedirectPath()).thenReturn(redirectPath);
|
|
||||||
when(identityServiceConfig.getAdminConsoleScopes()).thenReturn(Set.of("openid", "email", "profile", "offline_access"));
|
|
||||||
ArgumentCaptor<String> authenticationRequest = ArgumentCaptor.forClass(String.class);
|
|
||||||
String expectedUri = "http://localhost:8999/auth?client_id=alfresco&redirect_uri=%s%s&response_type=code&scope="
|
|
||||||
.formatted("http://localhost:8080", redirectPath);
|
|
||||||
|
|
||||||
authenticator.requestAuthentication(request, response);
|
|
||||||
|
|
||||||
verify(response).sendRedirect(authenticationRequest.capture());
|
|
||||||
assertTrue(authenticationRequest.getValue().contains(expectedUri));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("openid"));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("profile"));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("email"));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("offline_access"));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("audience=%s".formatted(audience)));
|
|
||||||
assertTrue(authenticationRequest.getValue().contains("state"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldResetCookiesAndCallAuthChallenge() throws IOException
|
public void shouldResetCookiesAndCallAuthChallenge() throws IOException
|
||||||
{
|
{
|
||||||
|
@@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2025 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication.identityservice.webscript;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.MockitoAnnotations.initMocks;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import com.nimbusds.oauth2.sdk.Scope;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
|
||||||
|
|
||||||
|
import org.alfresco.repo.security.authentication.external.RemoteUserMapper;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceConfig;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessToken;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AccessTokenAuthorization;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationException;
|
||||||
|
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.AuthorizationGrant;
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.AvoidStringBufferField")
|
||||||
|
public class IdentityServiceWebScriptHomeAuthenticatorUnitTest
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final String ALFRESCO_ACCESS_TOKEN = "ALFRESCO_ACCESS_TOKEN";
|
||||||
|
private static final String ALFRESCO_REFRESH_TOKEN = "ALFRESCO_REFRESH_TOKEN";
|
||||||
|
private static final String ALFRESCO_TOKEN_EXPIRATION = "ALFRESCO_TOKEN_EXPIRATION";
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
HttpServletRequest request;
|
||||||
|
@Mock
|
||||||
|
HttpServletResponse response;
|
||||||
|
@Mock
|
||||||
|
IdentityServiceFacade identityServiceFacade;
|
||||||
|
@Mock
|
||||||
|
IdentityServiceConfig identityServiceConfig;
|
||||||
|
@Mock
|
||||||
|
WebScriptHomeAuthenticationCookiesService cookiesService;
|
||||||
|
@Mock
|
||||||
|
RemoteUserMapper remoteUserMapper;
|
||||||
|
@Mock
|
||||||
|
AccessTokenAuthorization accessTokenAuthorization;
|
||||||
|
@Mock
|
||||||
|
AccessToken accessToken;
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<WebScriptHomeHttpServletRequestWrapper> requestCaptor;
|
||||||
|
|
||||||
|
IdentityServiceWebScriptHomeAuthenticator authenticator;
|
||||||
|
|
||||||
|
StringBuffer webScriptHomeURL = new StringBuffer("http://localhost:8080/alfresco/s/index");
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
initMocks(this);
|
||||||
|
ClientRegistration clientRegistration = mock(ClientRegistration.class);
|
||||||
|
ProviderDetails providerDetails = mock(ProviderDetails.class);
|
||||||
|
Scope scope = Scope.parse(Arrays.asList("openid", "profile", "email", "offline_access"));
|
||||||
|
|
||||||
|
when(clientRegistration.getProviderDetails()).thenReturn(providerDetails);
|
||||||
|
when(clientRegistration.getClientId()).thenReturn("alfresco");
|
||||||
|
when(providerDetails.getAuthorizationUri()).thenReturn("http://localhost:8999/auth");
|
||||||
|
when(providerDetails.getConfigurationMetadata()).thenReturn(Map.of("scopes_supported", scope));
|
||||||
|
when(identityServiceFacade.getClientRegistration()).thenReturn(clientRegistration);
|
||||||
|
when(request.getRequestURL()).thenReturn(webScriptHomeURL);
|
||||||
|
when(remoteUserMapper.getRemoteUser(request)).thenReturn(null);
|
||||||
|
|
||||||
|
authenticator = new IdentityServiceWebScriptHomeAuthenticator();
|
||||||
|
authenticator.setActive(true);
|
||||||
|
authenticator.setIdentityServiceFacade(identityServiceFacade);
|
||||||
|
authenticator.setCookiesService(cookiesService);
|
||||||
|
authenticator.setRemoteUserMapper(remoteUserMapper);
|
||||||
|
authenticator.setIdentityServiceConfig(identityServiceConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCallRemoteMapperIfTokenIsInCookies()
|
||||||
|
{
|
||||||
|
when(cookiesService.getCookie(ALFRESCO_ACCESS_TOKEN, request)).thenReturn("JWT_TOKEN");
|
||||||
|
when(cookiesService.getCookie(ALFRESCO_TOKEN_EXPIRATION, request)).thenReturn(
|
||||||
|
String.valueOf(Instant.now().plusSeconds(60).toEpochMilli()));
|
||||||
|
when(remoteUserMapper.getRemoteUser(requestCaptor.capture())).thenReturn("admin");
|
||||||
|
|
||||||
|
String username = authenticator.getWebScriptHomeUser(request, response);
|
||||||
|
|
||||||
|
assertEquals("Bearer JWT_TOKEN", requestCaptor.getValue().getHeader("Authorization"));
|
||||||
|
assertEquals("admin", username);
|
||||||
|
assertTrue(authenticator.isActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldRefreshExpiredTokenAndCallRemoteMapper()
|
||||||
|
{
|
||||||
|
when(cookiesService.getCookie(ALFRESCO_ACCESS_TOKEN, request)).thenReturn("EXPIRED_JWT_TOKEN");
|
||||||
|
when(cookiesService.getCookie(ALFRESCO_REFRESH_TOKEN, request)).thenReturn("REFRESH_TOKEN");
|
||||||
|
when(cookiesService.getCookie(ALFRESCO_TOKEN_EXPIRATION, request)).thenReturn(
|
||||||
|
String.valueOf(Instant.now().minusSeconds(60).toEpochMilli()));
|
||||||
|
when(accessToken.getTokenValue()).thenReturn("REFRESHED_JWT_TOKEN");
|
||||||
|
when(accessToken.getExpiresAt()).thenReturn(Instant.now().plusSeconds(60));
|
||||||
|
when(accessTokenAuthorization.getAccessToken()).thenReturn(accessToken);
|
||||||
|
when(accessTokenAuthorization.getRefreshTokenValue()).thenReturn("REFRESH_TOKEN");
|
||||||
|
when(identityServiceFacade.authorize(any(AuthorizationGrant.class))).thenReturn(accessTokenAuthorization);
|
||||||
|
when(remoteUserMapper.getRemoteUser(requestCaptor.capture())).thenReturn("admin");
|
||||||
|
|
||||||
|
String username = authenticator.getWebScriptHomeUser(request, response);
|
||||||
|
|
||||||
|
verify(cookiesService).addCookie(ALFRESCO_ACCESS_TOKEN, "REFRESHED_JWT_TOKEN", response);
|
||||||
|
verify(cookiesService).addCookie(ALFRESCO_REFRESH_TOKEN, "REFRESH_TOKEN", response);
|
||||||
|
assertEquals("Bearer REFRESHED_JWT_TOKEN", requestCaptor.getValue().getHeader("Authorization"));
|
||||||
|
assertEquals("admin", username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCallAuthChallengeWebScriptHome() throws IOException
|
||||||
|
{
|
||||||
|
|
||||||
|
String redirectPath = "/alfresco/s/index";
|
||||||
|
when(request.getRequestURL()).thenReturn(webScriptHomeURL);
|
||||||
|
when(identityServiceConfig.getWebScriptHomeScopes()).thenReturn(Set.of("openid", "email", "profile", "offline_access"));
|
||||||
|
when(identityServiceConfig.getWebScriptHomeRedirectPath()).thenReturn(redirectPath);
|
||||||
|
ArgumentCaptor<String> authenticationRequest = ArgumentCaptor.forClass(String.class);
|
||||||
|
String expectedUri = "http://localhost:8999/auth?client_id=alfresco&redirect_uri=%s%s&response_type=code&scope="
|
||||||
|
.formatted("http://localhost:8080", redirectPath);
|
||||||
|
|
||||||
|
authenticator.requestAuthentication(request, response);
|
||||||
|
|
||||||
|
verify(response).sendRedirect(authenticationRequest.capture());
|
||||||
|
assertTrue(authenticationRequest.getValue().contains(expectedUri));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("openid"));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("profile"));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("email"));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("offline_access"));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("state"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCallAuthChallengeWebScriptHomeWithAudience() throws IOException
|
||||||
|
{
|
||||||
|
String audience = "http://localhost:8082";
|
||||||
|
String redirectPath = "/alfresco/s/index";
|
||||||
|
when(request.getRequestURL()).thenReturn(webScriptHomeURL);
|
||||||
|
when(identityServiceConfig.getAudience()).thenReturn(audience);
|
||||||
|
when(identityServiceConfig.getWebScriptHomeRedirectPath()).thenReturn(redirectPath);
|
||||||
|
when(identityServiceConfig.getWebScriptHomeScopes()).thenReturn(Set.of("openid", "email", "profile", "offline_access"));
|
||||||
|
ArgumentCaptor<String> authenticationRequest = ArgumentCaptor.forClass(String.class);
|
||||||
|
String expectedUri = "http://localhost:8999/auth?client_id=alfresco&redirect_uri=%s%s&response_type=code&scope="
|
||||||
|
.formatted("http://localhost:8080", redirectPath);
|
||||||
|
|
||||||
|
authenticator.requestAuthentication(request, response);
|
||||||
|
|
||||||
|
verify(response).sendRedirect(authenticationRequest.capture());
|
||||||
|
assertTrue(authenticationRequest.getValue().contains(expectedUri));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("openid"));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("profile"));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("email"));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("offline_access"));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("audience=%s".formatted(audience)));
|
||||||
|
assertTrue(authenticationRequest.getValue().contains("state"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldResetCookiesAndCallAuthChallenge() throws IOException
|
||||||
|
{
|
||||||
|
when(cookiesService.getCookie(ALFRESCO_ACCESS_TOKEN, request)).thenReturn("EXPIRED_JWT_TOKEN");
|
||||||
|
when(cookiesService.getCookie(ALFRESCO_REFRESH_TOKEN, request)).thenReturn("REFRESH_TOKEN");
|
||||||
|
when(cookiesService.getCookie(ALFRESCO_TOKEN_EXPIRATION, request)).thenReturn(
|
||||||
|
String.valueOf(Instant.now().minusSeconds(60).toEpochMilli()));
|
||||||
|
|
||||||
|
when(identityServiceFacade.authorize(any(AuthorizationGrant.class))).thenThrow(AuthorizationException.class);
|
||||||
|
|
||||||
|
String username = authenticator.getWebScriptHomeUser(request, response);
|
||||||
|
|
||||||
|
verify(cookiesService).resetCookie(ALFRESCO_ACCESS_TOKEN, response);
|
||||||
|
verify(cookiesService).resetCookie(ALFRESCO_REFRESH_TOKEN, response);
|
||||||
|
verify(cookiesService).resetCookie(ALFRESCO_TOKEN_EXPIRATION, response);
|
||||||
|
assertNull(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldAuthorizeCodeAndSetCookies()
|
||||||
|
{
|
||||||
|
when(request.getParameter("code")).thenReturn("auth_code");
|
||||||
|
when(accessToken.getTokenValue()).thenReturn("JWT_TOKEN");
|
||||||
|
when(accessToken.getExpiresAt()).thenReturn(Instant.now().plusSeconds(60));
|
||||||
|
when(accessTokenAuthorization.getAccessToken()).thenReturn(accessToken);
|
||||||
|
when(accessTokenAuthorization.getRefreshTokenValue()).thenReturn("REFRESH_TOKEN");
|
||||||
|
when(identityServiceFacade.authorize(
|
||||||
|
AuthorizationGrant.authorizationCode("auth_code", webScriptHomeURL.toString())))
|
||||||
|
.thenReturn(accessTokenAuthorization);
|
||||||
|
when(remoteUserMapper.getRemoteUser(requestCaptor.capture())).thenReturn("admin");
|
||||||
|
|
||||||
|
String username = authenticator.getWebScriptHomeUser(request, response);
|
||||||
|
|
||||||
|
verify(cookiesService).addCookie(ALFRESCO_ACCESS_TOKEN, "JWT_TOKEN", response);
|
||||||
|
verify(cookiesService).addCookie(ALFRESCO_REFRESH_TOKEN, "REFRESH_TOKEN", response);
|
||||||
|
assertEquals("Bearer JWT_TOKEN", requestCaptor.getValue().getHeader("Authorization"));
|
||||||
|
assertEquals("admin", username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldExtractUsernameFromAuthorizationHeader()
|
||||||
|
{
|
||||||
|
when(remoteUserMapper.getRemoteUser(request)).thenReturn("admin");
|
||||||
|
|
||||||
|
String username = authenticator.getWebScriptHomeUser(request, response);
|
||||||
|
|
||||||
|
assertEquals("admin", username);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user