-
Notifications
You must be signed in to change notification settings - Fork 1
GSIP 151
Alessio Fabiani (GeoSolutions)
This proposal is for GeoServer 2.9 and 2.10.
- Under Discussion
- In Progress
- Completed
- Rejected
- Deferred
With the introduction of new GeoServer Security Plugins, especially the ones requiring user to login on an external site, it would be useful to have multiple, pluggable, login buttons allowing the user to choose the login method to access the GeoServer WEB UI.
The idea would be to allow security plugin to easily configure a login end-point which is rendered as an extension by the GeoServer Base Page, similar to the figure shown below
Allow the GeoServer Base Page to:
- Hide the default “form” login module if the “form filter chain” has been disabled
- Scan for other login endpoints through GeoServer Extensions and render login buttons accordingly
- Allow Security Plugins to easily declare specific login endpoints extensions and icons to be rendered on the GeoServer Base Page
In order to do this, we propose the introduction of a new “ComponentInfo” base class on GeoServer Web Core module
public class LoginFormInfo extends ComponentInfo<GeoServerBasePage> implements Comparable<LoginFormInfo> {
String name;
String icon;
private String loginPath;
public void setName(String name){
this.name = name;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public String getName(){
return name;
}
/**
* @return the loginPath
*/
public String getLoginPath() {
return loginPath;
}
/**
* @param loginPath the loginPath to set
*/
public void setLoginPath(String loginPath) {
this.loginPath = loginPath;
}
public int compareTo(LoginFormInfo other){
return getOrder().compareTo(other.getOrder());
}
}
A security plugin exposing a specific authentication endpoint and willing to show a new “login button” on the GeoServer Base Page, can easily declare it via “applicationContext.xml” resources like as follows:
<!-- login button -->
<bean id="googleOAuth2AuthLoginButton" class="org.geoserver.web.LoginFormInfo">
<property name="id" value="googleOAuth2LoginInfo" />
<property name="titleKey" value="GoogleOAuth2AuthProviderPanel.login" />
<property name="descriptionKey"
value="GoogleOAuth2AuthProviderPanel.description" />
<property name="componentClass"
value="org.geoserver.web.security.oauth2.GoogleOAuth2AuthProviderPanel" />
<property name="name" value="google " />
<property name="icon" value="google.png" />
<property name="loginPath" value="j_spring_outh2_google_login" />
</bean>
In order to enable GeoServer Base Page to render those elements, we will need to slightly modify the “GeoServerBasePage” wicket core class like this
GeoServerBasePage.java
...
// login stuff
List<String> securityFilters = getGeoServerApplication().getSecurityManager()
.getSecurityConfig().getFilterChain().filtersFor("/web/**");
// login form
WebMarkupContainer loginForm = new WebMarkupContainer("loginform") {
protected void onComponentTag(org.apache.wicket.markup.ComponentTag tag) {
String path = getRequest().getUrl().getPath();
StringBuilder loginPath = new StringBuilder();
if(path.isEmpty()) {
// home page
loginPath.append("../j_spring_security_check");
} else {
// boomarkable page of sorts
String[] pathElements = path.split("/");
for (String pathElement : pathElements) {
if(!pathElement.isEmpty()) {
loginPath.append("../");
}
}
loginPath.append("j_spring_security_check");
}
tag.put("action", loginPath);
};
};
add(loginForm);
final Authentication user = GeoServerSession.get().getAuthentication();
final boolean anonymous = user == null || user instanceof AnonymousAuthenticationToken;
loginForm.setVisible(anonymous || !securityFilters.contains("form"));
WebMarkupContainer logoutForm = new WebMarkupContainer("logoutform");
logoutForm.setVisible(!anonymous);
add(logoutForm);
logoutForm.add(new Label("username", anonymous ? "Nobody" : user.getName()));
// other login buttons
List<LoginFormInfo> loginforms = filterByAuth(getGeoServerApplication().getBeansOfType(LoginFormInfo.class));
add(new ListView<LoginFormInfo>("loginforms", loginforms) {
public void populateItem(ListItem<LoginFormInfo> item) {
LoginFormInfo info = item.getModelObject();
WebMarkupContainer loginForm = new WebMarkupContainer("loginform") {
protected void onComponentTag(org.apache.wicket.markup.ComponentTag tag) {
String path = getRequest().getUrl().getPath();
StringBuilder loginPath = new StringBuilder();
if(path.isEmpty()) {
// home page
loginPath.append("../" + info.getLoginPath());
} else {
// boomarkable page of sorts
String[] pathElements = path.split("/");
for (String pathElement : pathElements) {
if(!pathElement.isEmpty()) {
loginPath.append("../");
}
}
loginPath.append(info.getLoginPath());
}
tag.put("action", loginPath);
};
};
Image image;
if(info.getIcon() != null) {
image = new Image("link.icon", new PackageResourceReference(info.getComponentClass(), info.getIcon()));
} else {
image = new Image("link.icon", new PackageResourceReference(GeoServerBasePage.class, "img/icons/silk/wrench.png"));
}
image.add(AttributeModifier.replace("alt", new ParamResourceModel(info.getTitleKey(), null)));
loginForm.add(image);
loginForm.add(new Label("link.label", new StringResourceModel(info.getTitleKey(), (Component) null, null)));
item.add(loginForm);
loginForm.setVisible(anonymous);
}
});
...
GeoServerBasePage.html
...
<div id="header">
<div class="wrap">
<h2><a wicket:id="home" class="pngfix" href="#"><span wicket:id="label">GeoServer 2.0</span></a></h2>
<div class="button-group selfclear">
<form wicket:id="loginform" method="post" action="../j_spring_security_check">
<label class="noshow" for="username"><wicket:message key="username">Username</wicket:message></label>
<input id="username" type="text" name="username" value="" title="username" placeholder="username" wicket:message="title:username,placeholder:username"/>
<label class="noshow" for="password"><wicket:message key="password">Password</wicket:message></label>
<input id="password" type="password" name="password" value="" title="password" placeholder="password" wicket:message="title:password,placeholder:passwordGeoServer"/>
<label class="shown" for="_spring_security_remember_me"><wicket:message key="rememberMe">Remember me</wicket:message></label>
<input id="_spring_security_remember_me" type="checkbox" name="_spring_security_remember_me" />
<button class="button-login positive icon" type="submit"><div><wicket:message key="login">Login</wicket:message></div></button>
<script type="text/javascript">
$('input, textarea').placeholder();
</script>
</form>
<div wicket:id="logoutform">
<a class="button-logout icon" href="j_spring_security_logout"><span><wicket:message key="logout">Logout</wicket:message></span></a>
<span class="username"><wicket:message key="loggedInAs">Logged in as</wicket:message> <span wicket:id="username">User von Testenheimer</span></span>.
</div>
<div wicket:id="loginforms">
<form wicket:id="loginform" method="post" action="../j_spring_security_check">
<button class="positive icon" type="submit">
<div><img src="#" wicket:id="link.icon"/><span wicket:id="link.label"></span></div>
</button>
<script type="text/javascript">
$('input, textarea').placeholder();
</script>
</form>
</div>
</div>
</div><!-- /.wrap -->
</div><!-- /#header -->
...
It is worth notice that the proposed changes are not invasive and easily portable back to previous GeoServer versions.
No issues.
Project Steering Committee:
- Alessio Fabiani: +1
- Andrea Aime
- Ben Caradoc-Davies
- Brad Hards
- Christian Mueller: +1
- Ian Turton +1
- Jody Garnett +1
- Jukka Rahkonen
- Kevin Smith
- Simone Giannecchini
Committers:
©2020 Open Source Geospatial Foundation