Skip to content

Commit

Permalink
[JIRA GEOS-8551] - [AuthKey - WebService Key Mapper] - Add the possib…
Browse files Browse the repository at this point in the history
…ility of retrieving principal Authorities from the WebService Response
  • Loading branch information
Alessio Fabiani committed Jan 25, 2018
1 parent c5523c5 commit 180c4ad
Show file tree
Hide file tree
Showing 27 changed files with 1,227 additions and 352 deletions.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
103 changes: 103 additions & 0 deletions doc/en/user/source/community/authkey/index.rst
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -110,6 +110,109 @@ that can be used are:


Synchronizing users with the user/group service is not supported by this mapper. Synchronizing users with the user/group service is not supported by this mapper.


AuthKEY WebService Body Response UserGroup Service
**************************************************

When using an external web service to get Auth details, it is possible to define a custom ``GeoServer UserGroup Service`` being able to fetch Authorities - aka user's Roles - from the HTTP Body Response.

The rationale is mostly the same; that kind of ``GeoServer UserGroup Service`` will be apply a ``rolesRegex`` - Roles Regular Expression - to the body response - which can be either XML, JSON or Plain Text/HTML - in order to fetch the list of available Authorities.

In order to do this, it is possible to configure instances of **AuthKEY WebService Body Response** User Group Service.

First thing to do is to:

1. Login as an ``Administrator``

2. Move to ``Security`` > ``Users, Groups, Roles`` and select ``Add new`` from ``User Group Services``

.. figure:: images/001_user_group_service.png
:align: center

3. Click on ``AuthKEY WebService Body Response``

.. figure:: images/002_user_group_service.png
:align: center

4. Provide a ``Name`` and select anything you want from ``Passwords`` - those won't be used by this service, but they are still mandatory for GeoServer -

.. figure:: images/003_user_group_service.png
:align: center

5. Provide a suitable ``Roles Regex`` to apply to your Web Service Response

.. note:: This is the only real mandatory value to provide. The others are optional and will allow you to customize the User Group Service behavior (see below)

.. figure:: images/004_user_group_service.png
:align: center

Once the new ``GeoServer UserGroup Service`` has been configured, it can be easily linked to the ``Key Provider Web Service Mapper``.

1. From ``Authentication`` > ``Authentication Filters``, select - or add new - ``AuthKEY`` using ``Web Service`` as key mapper

2. Select the newly defined ``UserGroup Service`` and save

.. figure:: images/005_user_group_service.png
:align: center

**Additional Options**

1. *Optional static comma-separated list of available Groups from the Web Service response*

It is worth notice that this ``UserGroup Service`` will **always** translate fetched Roles in the form ``ROLE_<ROLENAME>``

As an instance, if the ``Roles Regular Expression`` will match something like::

my_user_role1, another_custom_user_role, role_External_Role_X
this will be converted into **3** different ``GeoServer User Roles`` named as::

ROLE_MY_USER_ROLE1
ROLE_ANOTHER_CUSTOM_USER_ROLE
ROLE_EXTERNAL_ROLE_X

Of course the role names are known only at runtime; nevertheless it is possible to **statically** specify associated ``GeoServer User Groups`` to be mapped later to other internal ``GeoServer User Roles``.

What does this means? A ``GeoServer User Group`` can be defined on the GeoServer Catalog and can be mapped by the active ``Role Services`` to one or more specific ``GeoServer User Roles``.

This mainly depends on the ``GeoServer Role Service`` you use. By default, the internal ``GeoServer Role Service`` can map Roles and Groups through static configuration stored on the GeoServer Data Dir.
This is possible by editing ``GeoServer User Group`` details from the ``Users, Groups, and Roles`` panel

.. figure:: images/006_user_group_service.png
:align: center

.. figure:: images/007_user_group_service.png
:align: center

Now, this custom ``UserGroup Service`` maps dynamically ``GeoServer User Role`` to ``GeoServer User Group`` as follows::

ROLE_MY_USER_ROLE1 <> GROUP_MY_USER_ROLE1
ROLE_ANOTHER_CUSTOM_USER_ROLE <> GROUP_ANOTHER_CUSTOM_USER_ROLE
ROLE_EXTERNAL_ROLE_X <> GROUP_EXTERNAL_ROLE_X

In order to be able to assign any ``GeoServer User Group`` to other internal ``GeoServer User Roles``, since those are known only at runtime, the ``UserGroup Service`` allows us to **statically** specify the ``GeoServer User Groups`` the Web Service can use;
this possible by setting the ``Optional static comma-separated list of available Groups from the Web Service response`` option:

.. figure:: images/008_user_group_service.png
:align: center

Once this is correctly configured, it will be possible to edit and assign ``GeoServer User Roles`` to the Groups by using the standard way

.. figure:: images/009_user_group_service.png
:align: center


2. *Role Service to use*

By default, if no ``Role Service`` specified, the ``UserGroup Service`` will use the ``GeoServer Active Role Service`` to resolve ``GeoServer User Roles`` from ``GeoServer User Groups`` - as specified above -

.. figure:: images/010_user_group_service.png
:align: center

It is possible to define a ``Custom Role Service`` to use instead, to resole ``GeoServer User Roles``; this is possible simply by selecting the ``Role Service`` to use from the ``Role Service to use`` option

.. figure:: images/011_user_group_service.png
:align: center

Configuration Configuration
------------- -------------


Expand Down
12 changes: 12 additions & 0 deletions src/community/authkey/src/main/java/applicationContext.xml
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@
<property name="titleKey" value="AuthenticationKeyFilterPanel.title"/> <property name="titleKey" value="AuthenticationKeyFilterPanel.title"/>
<property name="descriptionKey" value="AuthenticationKeyFilterPanel.description"/> <property name="descriptionKey" value="AuthenticationKeyFilterPanel.description"/>
</bean> </bean>

<!-- WebService UserGroup service panel infos -->
<bean id="authKeyWebServiceBodyResponseSecurityProvider" class="org.geoserver.security.WebServiceBodyResponseSecurityProvider">
<constructor-arg ref="geoServerSecurityManager"/>
</bean>

<bean id="authKeyWebServiceBodyResponseUserGroupServicePanelInfo" class="org.geoserver.security.web.WebServiceBodyResponseUserGroupServicePanelInfo">
<property name="id" value="security.authKeyWebServiceBodyResponseUserGroupService" />
<property name="shortTitleKey" value="WebServiceBodyResponseUserGroupServicePanel.short"/>
<property name="titleKey" value="WebServiceBodyResponseUserGroupServicePanel.title"/>
<property name="descriptionKey" value="WebServiceBodyResponseUserGroupServicePanel.description"/>
</bean>


<!-- REST based role service panel infos --> <!-- REST based role service panel infos -->
<bean id="authKeyRESTRoleServicePanelInfo" class="org.geoserver.security.web.GeoServerRestRoleServicePanelInfo"> <bean id="authKeyRESTRoleServicePanelInfo" class="org.geoserver.security.web.GeoServerRestRoleServicePanelInfo">
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;


import javax.servlet.FilterChain; import javax.servlet.FilterChain;
Expand All @@ -24,99 +23,95 @@
import org.geoserver.security.config.SecurityNamedServiceConfig; import org.geoserver.security.config.SecurityNamedServiceConfig;
import org.geoserver.security.filter.AuthenticationCachingFilter; import org.geoserver.security.filter.AuthenticationCachingFilter;
import org.geoserver.security.filter.GeoServerAuthenticationFilter; import org.geoserver.security.filter.GeoServerAuthenticationFilter;
import org.geoserver.security.filter.GeoServerSecurityContextPersistenceFilter;
import org.geoserver.security.filter.GeoServerSecurityFilter; import org.geoserver.security.filter.GeoServerSecurityFilter;
import org.geoserver.security.impl.GeoServerRole; import org.geoserver.security.impl.GeoServerRole;
import org.geoserver.security.impl.GeoServerUser; import org.geoserver.security.impl.GeoServerUser;
import org.geoserver.security.impl.GeoServerUserGroup;
import org.geoserver.security.impl.RoleCalculator;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;



/** /**
* Filter extending {@link GeoServerSecurityFilter}. * Filter extending {@link GeoServerSecurityFilter}.
* *
* The encoded user name is passed as an URL parameter named {@link #authKeyParamName}. * The encoded user name is passed as an URL parameter named {@link #authKeyParamName}.
* *
* The real user name is retrieved by querying an {@link AuthenticationKeyMapper} object stored * The real user name is retrieved by querying an {@link AuthenticationKeyMapper} object stored in {@link #authKeyMapperName}
* in {@link #authKeyMapperName} *
* * This filter needs a {@link GeoServerUserGroupService} for authentication
* This filter needs a {@link GeoServerUserGroupService} for authentication
* *
* @author christian * @author christian
* *
*/ */
public class GeoServerAuthenticationKeyFilter extends GeoServerSecurityFilter public class GeoServerAuthenticationKeyFilter extends GeoServerSecurityFilter
implements AuthenticationCachingFilter, GeoServerAuthenticationFilter implements AuthenticationCachingFilter, GeoServerAuthenticationFilter {
{

private String authKeyMapperName, authKeyParamName;
private String authKeyMapperName,authKeyParamName;
private AuthenticationKeyMapper mapper; private AuthenticationKeyMapper mapper;
private String userGroupServiceName;
protected AuthenticationEntryPoint aep;


private String userGroupServiceName;


protected AuthenticationEntryPoint aep;


@Override @Override
public void initializeFromConfig(SecurityNamedServiceConfig config) throws IOException { public void initializeFromConfig(SecurityNamedServiceConfig config) throws IOException {
super.initializeFromConfig(config); super.initializeFromConfig(config);


aep=new Http403ForbiddenEntryPoint(); aep = new Http403ForbiddenEntryPoint();


AuthenticationKeyFilterConfig authConfig = AuthenticationKeyFilterConfig authConfig = (AuthenticationKeyFilterConfig) config;
(AuthenticationKeyFilterConfig) config;
setAuthKeyParamName(authConfig.getAuthKeyParamName()); setAuthKeyParamName(authConfig.getAuthKeyParamName());
setUserGroupServiceName(authConfig.getUserGroupServiceName()); setUserGroupServiceName(authConfig.getUserGroupServiceName());
setAuthKeyMapperName(authConfig.getAuthKeyMapperName()); setAuthKeyMapperName(authConfig.getAuthKeyMapperName());
mapper=(AuthenticationKeyMapper) GeoServerExtensions.bean(authKeyMapperName); mapper = (AuthenticationKeyMapper) GeoServerExtensions.bean(authKeyMapperName);
mapper.setUserGroupServiceName(userGroupServiceName); mapper.setUserGroupServiceName(userGroupServiceName);
mapper.setSecurityManager(getSecurityManager()); mapper.setSecurityManager(getSecurityManager());
mapper.configureMapper(authConfig.getMapperParameters()); mapper.configureMapper(authConfig.getMapperParameters());

} }


@Override @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {


// String authKey = getAuthKey((HttpServletRequest) request); // String authKey = getAuthKey((HttpServletRequest) request);
// if (authKey==null) { // nothing to do // if (authKey==null) { // nothing to do
// chain.doFilter(request, response); // chain.doFilter(request, response);
// return; // return;
// } // }


String cacheKey=authenticateFromCache(this, (HttpServletRequest) request); String cacheKey = authenticateFromCache(this, (HttpServletRequest) request);


if (SecurityContextHolder.getContext().getAuthentication()==null) { if (SecurityContextHolder.getContext().getAuthentication() == null) {
doAuthenticate((HttpServletRequest) request, (HttpServletResponse) response, cacheKey); doAuthenticate((HttpServletRequest) request, (HttpServletResponse) response, cacheKey);


Authentication postAuthentication = SecurityContextHolder.getContext().getAuthentication(); Authentication postAuthentication = SecurityContextHolder.getContext()
if (postAuthentication != null && cacheKey!=null) { .getAuthentication();
if (cacheAuthentication(postAuthentication,(HttpServletRequest)request)) { if (postAuthentication != null && cacheKey != null) {
getSecurityManager().getAuthenticationCache().put(getName(), cacheKey,postAuthentication); if (cacheAuthentication(postAuthentication, (HttpServletRequest) request)) {
getSecurityManager().getAuthenticationCache().put(getName(), cacheKey,
postAuthentication);
} }
} }
} }

request.setAttribute(GeoServerSecurityFilter.AUTHENTICATION_ENTRY_POINT_HEADER, aep); request.setAttribute(GeoServerSecurityFilter.AUTHENTICATION_ENTRY_POINT_HEADER, aep);
chain.doFilter(request, response); chain.doFilter(request, response);
} }





public String getAuthKeyMapperName() { public String getAuthKeyMapperName() {
return authKeyMapperName; return authKeyMapperName;
} }



public void setAuthKeyMapperName(String authKeyMapperName) { public void setAuthKeyMapperName(String authKeyMapperName) {
this.authKeyMapperName = authKeyMapperName; this.authKeyMapperName = authKeyMapperName;
} }

public String getAuthKeyParamName() { public String getAuthKeyParamName() {
return authKeyParamName; return authKeyParamName;
} }
Expand All @@ -125,63 +120,59 @@ public void setAuthKeyParamName(String authKeyParamName) {
this.authKeyParamName = authKeyParamName; this.authKeyParamName = authKeyParamName;
} }



public String getUserGroupServiceName() { public String getUserGroupServiceName() {
return userGroupServiceName; return userGroupServiceName;
} }



public void setUserGroupServiceName(String userGroupServiceName) { public void setUserGroupServiceName(String userGroupServiceName) {
this.userGroupServiceName = userGroupServiceName; this.userGroupServiceName = userGroupServiceName;
} }



/** /**
* Try to authenticate and adds {@link GeoServerRole#AUTHENTICATED_ROLE} * Try to authenticate and adds {@link GeoServerRole#AUTHENTICATED_ROLE} Does NOT authenticate {@link GeoServerUser#ROOT_USERNAME}
* Does NOT authenticate {@link GeoServerUser#ROOT_USERNAME}
* *
* @param request * @param request
* @param response * @param response
* @param authkey * @param authkey
*/ */
protected void doAuthenticate(HttpServletRequest request, protected void doAuthenticate(HttpServletRequest request, HttpServletResponse response,
HttpServletResponse response,String authKey) throws IOException{ String authKey) throws IOException {


if (authKey==null) if (authKey == null)
return; return;

GeoServerUser user = mapper.getUser(authKey); GeoServerUser user = mapper.getUser(authKey);
if (user==null) { if (user == null) {
return; return;
} }

// no support for root login // no support for root login
if (GeoServerUser.ROOT_USERNAME.equals(user.getUsername())) { if (GeoServerUser.ROOT_USERNAME.equals(user.getUsername())) {
LOGGER.warning("Authentication key login does accept the root user"); LOGGER.warning("Authentication key login does accept the root user");
return; return;
} }

LOGGER.log(Level.FINE, "found user: = " + user.getUsername() + ", trying to authenticate");

Collection<GeoServerRole> roles = new ArrayList<GeoServerRole>();
for (GrantedAuthority auth : user.getAuthorities()) {
roles.add((GeoServerRole) auth);
}
if (roles.contains(GeoServerRole.AUTHENTICATED_ROLE) == false)
roles.add(GeoServerRole.AUTHENTICATED_ROLE);

KeyAuthenticationToken result = new KeyAuthenticationToken(authKey, authKeyParamName, user,
roles);


LOGGER.log(Level.FINE,"found user: = " + user.getUsername() + ", trying to authenticate");


Collection<GeoServerRole> roles= new ArrayList<GeoServerRole>();
for (GrantedAuthority auth: user.getAuthorities()) {
roles.add((GeoServerRole)auth);
}
if (roles.contains(GeoServerRole.AUTHENTICATED_ROLE)==false)
roles.add(GeoServerRole.AUTHENTICATED_ROLE);
KeyAuthenticationToken result = new KeyAuthenticationToken(authKey, authKeyParamName,user, roles);

SecurityContextHolder.getContext().setAuthentication(result); SecurityContextHolder.getContext().setAuthentication(result);
} }



public String getAuthKey(HttpServletRequest req) { public String getAuthKey(HttpServletRequest req) {
String authKey=getAuthKeyParamValue(req); String authKey = getAuthKeyParamValue(req);
if (StringUtils.hasLength(authKey)==false) if (StringUtils.hasLength(authKey) == false)
return null; return null;
return authKey; return authKey;
} }


/** /**
* Extracts authkey value from the request. * Extracts authkey value from the request.
Expand Down Expand Up @@ -218,28 +209,25 @@ public boolean applicableForHtml() {
return true; return true;
} }



/** /**
* @see org.geoserver.security.filter.GeoServerAuthenticationFilter#applicableForServices() * @see org.geoserver.security.filter.GeoServerAuthenticationFilter#applicableForServices()
*/ */
@Override @Override
public boolean applicableForServices() { public boolean applicableForServices() {
return true; return true;
} }

protected boolean cacheAuthentication(Authentication auth,HttpServletRequest request) { protected boolean cacheAuthentication(Authentication auth, HttpServletRequest request) {
// only cache if no HTTP session is available // only cache if no HTTP session is available
if ( request.getSession(false) != null) if (request.getSession(false) != null)
return false; return false;

return true; return true;

} }


public AuthenticationKeyMapper getMapper() { public AuthenticationKeyMapper getMapper() {
return mapper; return mapper;
} }




} }

0 comments on commit 180c4ad

Please sign in to comment.