Skip to content

Commit

Permalink
Add Security to StateChanging REST methods
Browse files Browse the repository at this point in the history
* POST, PUT, DELETE now require authentication
* Change to use Twitter Sign In, instead of app authentication
* Workaround AGOVA-53

When the Security Module is included in the deployment
all REST services are locked down. This means any state changing
methods will require authentication. The Security Module test-jar
comes with a Arquillian Extension that will setup do a full
authentication in AfterDeploy and setup RestAssured with a
valid Http Session. This allow the conference, user, attachment
REST Stories used as final functional tests in the Application
to work with our without the Security Module included. The REST
tests have no knowledge of the Security Module.

Issue: #52
  • Loading branch information
aslakknutsen committed Aug 23, 2013
1 parent 19d380b commit c9ed918
Show file tree
Hide file tree
Showing 12 changed files with 304 additions and 39 deletions.
12 changes: 12 additions & 0 deletions code/application/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.graphene</groupId>
<artifactId>graphene-webdriver</artifactId>
<type>pom</type>
<scope>test</scope>
</dependency>

<!-- Import Test Stories -->
<dependency>
Expand All @@ -109,6 +115,12 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.cedj.geekseek</groupId>
<artifactId>geekseek-service-security</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>

</dependencies>

Expand Down
19 changes: 16 additions & 3 deletions code/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@

<version.jboss_spec>3.0.1.Final</version.jboss_spec>

<version.arquillian_core>1.1.0.Final</version.arquillian_core>
<version.arquillian_core>1.1.1.Final</version.arquillian_core>
<version.arquillian_persistence>1.0.0.Alpha6</version.arquillian_persistence>
<version.arquillian_drone>1.1.1.Final</version.arquillian_drone>
<version.arquillian_graphene>2.0.0.Alpha3</version.arquillian_graphene>
<version.arquillian_drone>1.2.0.Alpha3</version.arquillian_drone>
<version.arquillian_graphene>2.0.0.Alpha4</version.arquillian_graphene>
<version.arquillian_warp>1.0.0.Alpha3</version.arquillian_warp>
<version.arquillian_warp_rest>1.0.0.Final-SNAPSHOT</version.arquillian_warp_rest>
<version.restassured>1.8.0</version.restassured>
Expand Down Expand Up @@ -120,6 +120,12 @@
<artifactId>resteasy-jaxrs</artifactId>
<version>${version.resteasy}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
Expand Down Expand Up @@ -363,6 +369,13 @@
<artifactId>geekseek-service-security</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cedj.geekseek</groupId>
<artifactId>geekseek-service-security</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
41 changes: 41 additions & 0 deletions code/application/service/security/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
<!-- Our old v. of resteasy use a old v. of httpclient which is uncompatible with graphene -->
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
Expand All @@ -65,16 +72,50 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.cedj.geekseek</groupId>
<artifactId>geekseek-domain-core</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.cedj.geekseek</groupId>
<artifactId>geekseek-domain-persistence</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.cedj.geekseek</groupId>
<artifactId>geekseek-domain-user</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-impl-maven-archive</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.shrinkwrap.descriptors</groupId>
<artifactId>shrinkwrap-descriptors-impl-javaee</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-warp-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.graphene</groupId>
<artifactId>graphene-webdriver</artifactId>
<type>pom</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.cedj.geekseek.service.security.oauth;

import java.io.Serializable;

import org.agorava.core.api.UserProfile;
import org.agorava.core.api.oauth.OAuthToken;

public class SuccessfulAuthentication implements Serializable {

private static final long serialVersionUID = 1L;

private UserProfile profile;
private OAuthToken token;

public SuccessfulAuthentication(UserProfile profile, OAuthToken token) {
this.profile = profile;
this.token = token;
}

public UserProfile getProfile() {
return profile;
}

public OAuthToken getToken() {
return token;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.cedj.geekseek.service.security.picketlink;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.servlet.http.Cookie;
Expand All @@ -15,6 +16,7 @@
import org.agorava.twitter.model.TwitterProfile;
import org.cedj.geekseek.domain.Repository;
import org.cedj.geekseek.domain.user.model.User;
import org.cedj.geekseek.service.security.oauth.SuccessfulAuthentication;
import org.picketlink.annotations.PicketLink;
import org.picketlink.authentication.BaseAuthenticator;

Expand All @@ -40,6 +42,8 @@ public class OAuthAuthenticator extends BaseAuthenticator {
@Inject @Twitter @Current
private OAuthSession session;

@Inject
private Event<SuccessfulAuthentication> successfull;

@Override
public void authenticate() {
Expand Down Expand Up @@ -67,6 +71,9 @@ public void authenticate() {
session.setVerifier(verifier);
service.initAccessToken();

// https://issues.jboss.org/browse/AGOVA-53
successfull.fire(new SuccessfulAuthentication(service.getSession().getUserProfile(), service.getAccessToken()));

String screenName = ((TwitterProfile)service.getSession().getUserProfile()).getScreenName();
User user = repository.get(screenName);
if(user == null) { // can't find a matching account
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,34 @@
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import org.agorava.core.api.event.SocialEvent;
import org.agorava.core.api.oauth.OAuthSession;
import org.agorava.core.api.oauth.OAuthToken;
import org.agorava.twitter.model.TwitterProfile;
import org.cedj.geekseek.domain.Repository;
import org.cedj.geekseek.domain.user.model.User;
import org.cedj.geekseek.service.security.oauth.SuccessfulAuthentication;

public class UserRegistration {

@Inject
private Repository<User> repository;

public void registerUser(@Observes SocialEvent<OAuthSession> event) {
if(event.getStatus() == SocialEvent.Status.SUCCESS) {
TwitterProfile profile = (TwitterProfile)event.getEventData().getUserProfile();

User user = repository.get(profile.getScreenName());
if(user == null) {
user = new User(profile.getScreenName());
}
user.setName(profile.getFullName());
user.setBio(profile.getDescription());
user.setAvatarUrl(profile.getProfileImageUrl());
OAuthToken token = event.getEventData().getAccessToken();
user.setAccessToken(token.getSecret() + "|" + token.getToken());
if(user.getApiToken() == null) {
user.setApiToken(UUID.randomUUID().toString());
}

repository.store(user);
//public void registerUser(@Observes SocialEvent<OAuthSession> event) { https://issues.jboss.org/browse/AGOVA-53
public void registerUser(@Observes SuccessfulAuthentication event) {
TwitterProfile profile = (TwitterProfile)event.getProfile();

User user = repository.get(profile.getScreenName());
if(user == null) {
user = new User(profile.getScreenName());
}
user.setName(profile.getFullName());
user.setBio(profile.getDescription());
user.setAvatarUrl(profile.getProfileImageUrl());
OAuthToken token = event.getToken();
user.setAccessToken(token.getSecret() + "|" + token.getToken());
if(user.getApiToken() == null) {
user.setApiToken(UUID.randomUUID().toString());
}

repository.store(user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apiClass=org.scribe.builder.api.TwitterApi$Authenticate
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.cedj.geekseek.service.security.test.arquillian;

import java.net.URI;
import java.util.Collection;

import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
import org.jboss.arquillian.config.descriptor.api.ExtensionDef;
import org.jboss.arquillian.container.spi.client.protocol.metadata.HTTPContext;
import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData;
import org.jboss.arquillian.container.spi.event.container.AfterDeploy;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.core.spi.LoadableExtension;

import com.jayway.restassured.RestAssured;
import com.jayway.restassured.config.RestAssuredConfig;
import com.jayway.restassured.config.SessionConfig;

/**
* Arquillian Extension that will do a authorization request
* after deployment to create a active session and set the session
* id as 'current session' for the RestAssured client lib.
*
* Since security is only active if the Security module is deployed,
* rest clients in the test suite shouldn't have to tie them selves
* into how security is done, if at all active.
*
* This Extension enables/activates security in the Test suite
* if the security module is included.
*
* This behavior can be disabled by configuring the authsession
* extension in arquillian.xml, using the disabled=true property.
*
* @author <a href="mailto:aslak@redhat.com">Aslak Knutsen</a>
*/
public class AuthorizedSessionExtension implements LoadableExtension {

private static final String EXT_NAME = "authsession";
private static final String EXT_DISABLE = "disabled";

@Override
public void register(ExtensionBuilder builder) {
builder.observer(CreateAuthorizedSession.class);
}

public static class CreateAuthorizedSession {

@Inject
private Instance<ArquillianDescriptor> desc;

public void performLogin(@Observes AfterDeploy event, ProtocolMetaData data) {

if(isDisabled()) {
return;
}

URI authUri = locateAuthURI(data);
String sessionId = authenticate(authUri);

RestAssured.config = RestAssuredConfig.config().sessionConfig(
SessionConfig.sessionConfig().sessionIdValue(sessionId));
}

private String authenticate(URI authUri) {
return new TwitterLogin().login(authUri.toASCIIString());
}

private URI locateAuthURI(ProtocolMetaData data) {
Collection<HTTPContext> contexts = data.getContexts(HTTPContext.class);
if(contexts == null || contexts.size() == 0 || contexts.size() > 1) {
throw new RuntimeException("Could not determine auth URL: " + contexts);
}

HTTPContext context = contexts.iterator().next();
return URI.create(context.getServlets().get(0).getBaseURI()+ "auth");
}

private boolean isDisabled() {
for(ExtensionDef def : desc.get().getExtensions()) {
if(EXT_NAME.equals(def.getExtensionName())) {
if(def.getExtensionProperties().containsKey(EXT_DISABLE)) {
if(def.getExtensionProperties().get(EXT_DISABLE).equals("true")) {
return true;
}
}
}
}
return false;
}
}
}
Loading

0 comments on commit c9ed918

Please sign in to comment.