Commit 13cfec90 authored by Misagh Moayyed's avatar Misagh Moayyed
Browse files

Merge branch 'master' into radius-config-auto

Conflicts:
	cas-server-webapp/src/main/webapp/WEB-INF/cas.properties
parents 1756ca52 4fab89a3
/*
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import java.io.Serializable;
/**
* Represents the task of building a CAS response
* that is returned by a service.
* @author Misagh Moayyed
* @param <T> the type parameter
* @since 4.2.0
*/
public interface ResponseBuilder<T extends WebApplicationService> extends Serializable {
/**
* Build response. The implementation must produce
* a response that is based on the type passed. Given
* the protocol used, the ticket id may be passed
* as part of the response. If the response type
* is not recognized, an error must be thrown back.
*
* @param service the service
* @param ticketId the ticket id
* @return the response
*/
Response build(T service, String ticketId);
}
......@@ -54,18 +54,23 @@ public abstract class AbstractWebApplicationService implements SingleLogoutServi
private boolean loggedOutAlready;
private final ResponseBuilder<WebApplicationService> responseBuilder;
/**
* Instantiates a new abstract web application service.
*
* @param id the id
* @param originalUrl the original url
* @param artifactId the artifact id
* @param responseBuilder the response builder
*/
protected AbstractWebApplicationService(final String id, final String originalUrl,
final String artifactId) {
final String artifactId, final ResponseBuilder<WebApplicationService> responseBuilder) {
this.id = id;
this.originalUrl = originalUrl;
this.artifactId = artifactId;
this.responseBuilder = responseBuilder;
}
@Override
......@@ -73,14 +78,17 @@ public abstract class AbstractWebApplicationService implements SingleLogoutServi
return this.id;
}
@Override
public final String getId() {
return this.id;
}
@Override
public final String getArtifactId() {
return this.artifactId;
}
@Override
public final Map<String, Object> getAttributes() {
return EMPTY_MAP;
}
......@@ -91,6 +99,7 @@ public abstract class AbstractWebApplicationService implements SingleLogoutServi
*
* @return the original url provided.
*/
@Override
public final String getOriginalUrl() {
return this.originalUrl;
}
......@@ -117,10 +126,11 @@ public abstract class AbstractWebApplicationService implements SingleLogoutServi
.toHashCode();
}
protected Principal getPrincipal() {
public Principal getPrincipal() {
return this.principal;
}
@Override
public void setPrincipal(final Principal principal) {
this.principal = principal;
}
......@@ -144,6 +154,7 @@ public abstract class AbstractWebApplicationService implements SingleLogoutServi
*
* @return if the service is already logged out.
*/
@Override
public boolean isLoggedOutAlready() {
return loggedOutAlready;
}
......@@ -153,7 +164,18 @@ public abstract class AbstractWebApplicationService implements SingleLogoutServi
*
* @param loggedOutAlready if the service is already logged out.
*/
@Override
public final void setLoggedOutAlready(final boolean loggedOutAlready) {
this.loggedOutAlready = loggedOutAlready;
}
protected ResponseBuilder<? extends WebApplicationService> getResponseBuilder() {
return responseBuilder;
}
@Override
public Response getResponse(final String ticketId) {
return this.responseBuilder.build(this, ticketId);
}
}
/*
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* Abstract response builder that provides wrappers for building
* post and redirect responses.
* @author Misagh Moayyed
* @since 4.2
*/
public abstract class AbstractWebApplicationServiceResponseBuilder implements ResponseBuilder<WebApplicationService> {
private static final long serialVersionUID = -4584738964007702423L;
protected final Logger logger = LoggerFactory.getLogger(getClass());
/**
* Build redirect.
*
* @param service the service
* @param parameters the parameters
* @return the response
*/
protected Response buildRedirect(final WebApplicationService service, final Map<String, String> parameters) {
return DefaultResponse.getRedirectResponse(service.getOriginalUrl(), parameters);
}
/**
* Build post.
*
* @param service the service
* @param parameters the parameters
* @return the response
*/
protected Response buildPost(final WebApplicationService service, final Map<String, String> parameters) {
return DefaultResponse.getPostResponse(service.getOriginalUrl(), parameters);
}
}
......@@ -86,11 +86,10 @@ public class PersonDirectoryPrincipalResolver implements PrincipalResolver {
attributes = personAttributes.getAttributes();
}
if (attributes == null & !this.returnNullIfNoAttributes) {
if (attributes == null || attributes.isEmpty()) {
if (!this.returnNullIfNoAttributes) {
return this.principalFactory.createPrincipal(principalId);
}
if (attributes == null) {
return null;
}
......
......@@ -18,12 +18,6 @@
*/
package org.jasig.cas.authentication.principal;
import org.jasig.cas.CasProtocolConstants;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Represents a service which wishes to use the CAS protocol.
*
......@@ -34,7 +28,6 @@ public final class SimpleWebApplicationServiceImpl extends AbstractWebApplicatio
private static final long serialVersionUID = 8334068957483758042L;
private final Response.ResponseType responseType;
/**
* Instantiates a new simple web application service impl.
......@@ -42,26 +35,11 @@ public final class SimpleWebApplicationServiceImpl extends AbstractWebApplicatio
* @param id the id
* @param originalUrl the original url
* @param artifactId the artifact id
* @param responseType the response type
* @param responseBuilder the response builder
*/
protected SimpleWebApplicationServiceImpl(final String id,
final String originalUrl, final String artifactId,
final Response.ResponseType responseType) {
super(id, originalUrl, artifactId);
this.responseType = responseType;
}
@Override
public Response getResponse(final String ticketId) {
final Map<String, String> parameters = new HashMap<>();
if (StringUtils.hasText(ticketId)) {
parameters.put(CasProtocolConstants.PARAMETER_TICKET, ticketId);
}
if (Response.ResponseType.POST == this.responseType) {
return DefaultResponse.getPostResponse(getOriginalUrl(), parameters);
}
return DefaultResponse.getRedirectResponse(getOriginalUrl(), parameters);
protected SimpleWebApplicationServiceImpl(final String id, final String originalUrl, final String artifactId,
final ResponseBuilder<WebApplicationService> responseBuilder) {
super(id, originalUrl, artifactId, responseBuilder);
}
}
......@@ -55,9 +55,12 @@ public final class WebApplicationServiceFactory extends AbstractServiceFactory<W
final String id = cleanupUrl(serviceToUse);
final String artifactId = request.getParameter(CasProtocolConstants.PARAMETER_TICKET);
return new SimpleWebApplicationServiceImpl(id, serviceToUse,
artifactId, "POST".equalsIgnoreCase(method) ? Response.ResponseType.POST
: Response.ResponseType.REDIRECT);
final Response.ResponseType type = "POST".equalsIgnoreCase(method) ? Response.ResponseType.POST
: Response.ResponseType.REDIRECT;
final WebApplicationService webApplicationService = new SimpleWebApplicationServiceImpl(id, serviceToUse,
artifactId, new WebApplicationServiceResponseBuilder(type));
return webApplicationService;
}
@Override
......
/*
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import org.jasig.cas.CasProtocolConstants;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Default response builder that passes back the ticket
* id to the original url of the service based on the response type.
* @author Misagh Moayyed
* @since 4.2
*/
public class WebApplicationServiceResponseBuilder extends AbstractWebApplicationServiceResponseBuilder {
private static final long serialVersionUID = -851233878780818494L;
private final Response.ResponseType responseType;
/**
* Instantiates a new Web application service response builder.
* @param type the type
*/
public WebApplicationServiceResponseBuilder(final Response.ResponseType type) {
this.responseType = type;
}
@Override
public Response build(final WebApplicationService service, final String ticketId) {
final Map<String, String> parameters = new HashMap<>();
if (StringUtils.hasText(ticketId)) {
parameters.put(CasProtocolConstants.PARAMETER_TICKET, ticketId);
}
if (responseType.equals(Response.ResponseType.POST)) {
return buildPost(service, parameters);
}
if (responseType.equals(Response.ResponseType.REDIRECT)) {
return buildRedirect(service, parameters);
}
throw new IllegalArgumentException("Response type is valid. Only POST/REDIRECT are supported");
}
}
......@@ -18,6 +18,7 @@
*/
package org.jasig.cas;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.jasig.cas.authentication.Authentication;
import org.jasig.cas.authentication.AuthenticationHandler;
......@@ -44,6 +45,7 @@ import org.jasig.cas.services.ReturnAllowedAttributeReleasePolicy;
import org.jasig.cas.services.support.RegisteredServiceRegexAttributeFilter;
import org.jasig.cas.validation.Assertion;
import org.jasig.cas.validation.ImmutableAssertion;
import org.jasig.services.persondir.IPersonAttributeDao;
import org.jasig.services.persondir.support.StubPersonAttributeDao;
import org.jasig.services.persondir.support.merger.NoncollidingAttributeAdder;
import org.springframework.mock.web.MockHttpServletRequest;
......@@ -131,6 +133,15 @@ public final class TestUtils {
}
}
public static IPersonAttributeDao getAttributeRepository() {
final Map<String, List<Object>> attributes = new HashMap<>();
attributes.put("uid", (List) ImmutableList.of(CONST_USERNAME));
attributes.put("cn", (List) ImmutableList.of(CONST_USERNAME.toUpperCase()));
attributes.put("givenName", (List) ImmutableList.of(CONST_USERNAME));
attributes.put("memberOf", (List) ImmutableList.of("system", "admin", "cas"));
return new StubPersonAttributeDao(attributes);
}
public static Principal getPrincipal() {
return getPrincipal(CONST_USERNAME);
}
......
/*
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import org.jasig.cas.TestUtils;
import org.jasig.cas.authentication.Credential;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Test cases for {@link PersonDirectoryPrincipalResolver}.
* @author Misagh Moayyed
* @since 4.2
*/
public class PersonDirectoryPrincipalResolverTests {
@Test
public void verifyNullPrincipal() {
final PersonDirectoryPrincipalResolver resolver = new PersonDirectoryPrincipalResolver();
final Principal p = resolver.resolve(new Credential() {
@Override
public String getId() {
return null;
}
});
assertNull(p);
}
@Test
public void verifyNullAttributes() {
final PersonDirectoryPrincipalResolver resolver = new PersonDirectoryPrincipalResolver();
resolver.setReturnNullIfNoAttributes(true);
resolver.setPrincipalAttributeName(TestUtils.CONST_USERNAME);
final Credential c = TestUtils.getCredentialsWithSameUsernameAndPassword();
final Principal p = resolver.resolve(c);
assertNull(p);
}
@Test
public void verifyNoAttributesWithPrincipal() {
final PersonDirectoryPrincipalResolver resolver = new PersonDirectoryPrincipalResolver();
resolver.setPrincipalAttributeName(TestUtils.CONST_USERNAME);
final Credential c = TestUtils.getCredentialsWithSameUsernameAndPassword();
final Principal p = resolver.resolve(c);
assertNotNull(p);
}
@Test
public void verifyAttributesWithPrincipal() {
final PersonDirectoryPrincipalResolver resolver = new PersonDirectoryPrincipalResolver();
resolver.setAttributeRepository(TestUtils.getAttributeRepository());
resolver.setPrincipalAttributeName("cn");
final Credential c = TestUtils.getCredentialsWithSameUsernameAndPassword();
final Principal p = resolver.resolve(c);
assertNotNull(p);
assertNotEquals(p.getId(), TestUtils.CONST_USERNAME);
assertTrue(p.getAttributes().containsKey("memberOf"));
}
}
......@@ -18,18 +18,16 @@
*/
package org.jasig.cas.util.http;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.security.cert.X509Certificate;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.junit.Test;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Test cases for {@link SimpleHttpClient}.
......@@ -71,10 +69,13 @@ public class SimpleHttpClientTests {
private SSLConnectionSocketFactory getFriendlyToAllSSLSocketFactory() throws Exception {
final TrustManager trm = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(final X509Certificate[] certs, final String authType) {}
@Override
public void checkServerTrusted(final X509Certificate[] certs, final String authType) {}
};
final SSLContext sc = SSLContext.getInstance("SSL");
......
......@@ -4,16 +4,24 @@ title: CAS - Remote Address Authentication
---
# Remote Address Authentication
This handler uses the request's remote address to transparently authenticate a user, having verified the address against a range of configured IP addresses. The mechanics of this approach are very similar to X.509 certificate authentication, but trust is instead placed on the client internal network address.
This handler uses the request's remote address to transparently authenticate a user, having verified
the address against a range of configured IP addresses. The mechanics of this approach are very similar
to X.509 certificate authentication, but trust is instead placed on the client internal network address.
The benefit of this approach is that transparent authentication is achieved within a large corporate network without the need to manage certificates.
The benefit of this approach is that transparent authentication is achieved within a large corporate
network without the need to manage certificates.
<div class="alert alert-danger"><strong>Be Careful</strong><p>Keep in mind that this authentication mechanism should only be enabled for internal network clients with relatively static IP addresses.</p></div>
<div class="alert alert-danger"><strong>Be Careful</strong><p>Keep in mind that this authentication
mechanism should only be enabled for internal network clients with relatively static IP addresses.</p></div>
## Caveats
This method of authentication assumes internal clients will be hitting the CAS server directly and not coming via a web proxy. In the event of the client using the web proxy the likelihood of the remote address lookup succeeding is reduced because to CAS the client address is that of the proxy server and not the client. Given that this form of CAS authentication would typically be deployed within an internal network this is generally not a problem.
This method of authentication assumes internal clients will be hitting the CAS server directly
and not coming via a web proxy. In the event of the client using the web proxy the likelihood
of the remote address lookup succeeding is reduced because to CAS the client address is that
of the proxy server and not the client. Given that this form of CAS authentication would typically
be deployed within an internal network this is generally not a problem.
## Authentication Components
......@@ -34,17 +42,12 @@ Support is enabled by including the following dependency in the Maven WAR overla
</bean>
{% endhighlight %}
This authentication handler checks the inbound client IP address to see if it falls within the internal network. The `RemoteAddressAuthenticationHandler` bean has one property:
This authentication handler checks the inbound client IP address to see if it falls within the
internal network. The `RemoteAddressAuthenticationHandler` bean has one property:
- `ipNetworkRange` - This defines the internal network parameters in the form of a subnet and netmask. e.g. `192.168.1.0/255.255.255.0` or `10.0.0.0/255.255.0.0`.
- `ipNetworkRange` - This defines the internal network parameters in the form of a subnet and
netmask. e.g. `192.168.1.0/255.255.255.0` or `10.0.0.0/255.255.0.0`.
Also declare the following bean, which extracts the client's IP address from the request.
{% highlight xml %}
<bean id="remoteAddressCheck" class="org.jasig.cas.adaptors.generic.remote.RemoteAddressNonInteractiveCredentialsAction">
<property name="centralAuthenticationService" ref="centralAuthenticationService"/>
</bean>
{% endhighlight %}
### Configuring Webflow
......
......@@ -128,10 +128,12 @@ Then verify that your are able to read the keytab file:
* Internet Explorer - Enable _Integrated Windows Authentication_ and add the CAS server URL to the _Local Intranet_
zone.
* Firefox - Set the `network.negotiate-auth.trusted-uris` configuration parameter in `about:config` to the CAS server
URL, e.g. https://cas.example.com.
URL, e.g. `https://cas.example.com`.
### CAS Component Configuration
### Configuration
####Webflow Configuration
Define two new action states in `login-webflow.xml` before the `viewLoginForm` state:
{% highlight xml %}
......@@ -149,33 +151,27 @@ Define two new action states in `login-webflow.xml` before the `viewLoginForm` s
Additionally, find action `generateLoginTicket` - replace `viewLoginForm` with `startAuthenticate`.
Add two bean definitions in `cas-servlet.xml`:
Insert