Spring Security OAuth2 samples with custom OAuth2 endpoint URLs
Created
April 19, 2012 14:06
-
-
Save dsyer/2421187 to your computer and use it in GitHub Desktop.
Spring Security OAuth2 samples with custom OAuth2 endpoint URLs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*/src/META-INF/ | |
*/src/main/java/META-INF/ | |
samples/*/*/src/main/webapp/META-INF/ | |
target/ | |
.classpath | |
.project | |
.DS_Store | |
.settings/ | |
*.iml | |
*.iws | |
*.ipr | |
.idea/ | |
cargo-installs/ | |
atlassian-ide-plugin.xml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<beansProjectDescription> | |
<version>1</version> | |
<pluginVersion><![CDATA[2.6.0.201104111100-PATCH]]></pluginVersion> | |
<configSuffixes> | |
<configSuffix><![CDATA[xml]]></configSuffix> | |
</configSuffixes> | |
<enableImports><![CDATA[false]]></enableImports> | |
<configs> | |
<config>src/main/webapp/WEB-INF/spring-servlet.xml</config> | |
</configs> | |
<configSets> | |
</configSets> | |
</beansProjectDescription> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<beansProjectDescription> | |
<version>1</version> | |
<pluginVersion><![CDATA[2.6.0.201104111100-PATCH]]></pluginVersion> | |
<configSuffixes> | |
<configSuffix><![CDATA[xml]]></configSuffix> | |
</configSuffixes> | |
<enableImports><![CDATA[false]]></enableImports> | |
<configs> | |
<config>src/main/webapp/WEB-INF/spring-servlet.xml</config> | |
</configs> | |
<configSets> | |
</configSets> | |
</beansProjectDescription> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ page import="org.springframework.security.core.AuthenticationException" %> | |
<%@ page import="org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter" %> | |
<%@ page import="org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException" %> | |
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %> | |
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/> | |
<title>Sparklr</title> | |
<link type="text/css" rel="stylesheet" href="<c:url value="/style.css"/>"/> | |
</head> | |
<body> | |
<h1>Sparklr</h1> | |
<div id="content"> | |
<% if (session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY) != null && !(session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY) instanceof UnapprovedClientAuthenticationException)) { %> | |
<div class="error"> | |
<h2>Woops!</h2> | |
<p>Access could not be granted. (<%= ((AuthenticationException) session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>)</p> | |
</div> | |
<% } %> | |
<c:remove scope="session" var="SPRING_SECURITY_LAST_EXCEPTION"/> | |
<authz:authorize ifAllGranted="ROLE_USER"> | |
<h2>Please Confirm</h2> | |
<p>You hereby authorize "<c:out value="${client.clientId}"/>" to access your protected resources.</p> | |
<form id="confirmationForm" name="confirmationForm" action="<%=request.getContextPath()%>/authorize" method="post"> | |
<input name="user_oauth_approval" value="true" type="hidden"/> | |
<label><input name="authorize" value="Authorize" type="submit"></label> | |
</form> | |
<form id="denialForm" name="denialForm" action="<%=request.getContextPath()%>/authorize" method="post"> | |
<input name="user_oauth_approval" value="false" type="hidden"/> | |
<label><input name="deny" value="Deny" type="submit"></label> | |
</form> | |
</authz:authorize> | |
</div> | |
<div id="footer">Sample application for <a href="http://github.com/SpringSource/spring-security-oauth" target="_blank">Spring Security OAuth</a></div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.sparklr.mvc; | |
import java.util.TreeMap; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.security.oauth2.provider.AuthorizationRequest; | |
import org.springframework.security.oauth2.provider.ClientDetails; | |
import org.springframework.security.oauth2.provider.ClientDetailsService; | |
import org.springframework.stereotype.Controller; | |
import org.springframework.web.bind.annotation.ModelAttribute; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.SessionAttributes; | |
import org.springframework.web.servlet.ModelAndView; | |
/** | |
* Controller for retrieving the model for and displaying the confirmation page | |
* for access to a protected resource. | |
* | |
* @author Ryan Heaton | |
*/ | |
@Controller | |
@SessionAttributes(types = AuthorizationRequest.class) | |
public class AccessConfirmationController { | |
private ClientDetailsService clientDetailsService; | |
@RequestMapping("/oauth/confirm_access") | |
public ModelAndView getAccessConfirmation(@ModelAttribute AuthorizationRequest clientAuth) throws Exception { | |
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); | |
TreeMap<String, Object> model = new TreeMap<String, Object>(); | |
model.put("auth_request", clientAuth); | |
model.put("client", client); | |
return new ModelAndView("access_confirmation", model); | |
} | |
@Autowired | |
public void setClientDetailsService(ClientDetailsService clientDetailsService) { | |
this.clientDetailsService = clientDetailsService; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.sparklr.mvc; | |
import java.security.Principal; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.Map; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.http.ResponseEntity; | |
import org.springframework.security.access.AccessDeniedException; | |
import org.springframework.security.oauth.examples.sparklr.oauth.SparklrUserApprovalHandler; | |
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; | |
import org.springframework.security.oauth2.common.OAuth2AccessToken; | |
import org.springframework.security.oauth2.provider.OAuth2Authentication; | |
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; | |
import org.springframework.stereotype.Controller; | |
import org.springframework.web.bind.annotation.PathVariable; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.RequestMethod; | |
import org.springframework.web.bind.annotation.ResponseBody; | |
import org.springframework.web.bind.annotation.ResponseStatus; | |
/** | |
* Controller for resetting the token store for testing purposes. | |
* | |
* @author Dave Syer | |
*/ | |
@Controller | |
public class AdminController { | |
private ConsumerTokenServices tokenServices; | |
private SparklrUserApprovalHandler userApprovalHandler; | |
@RequestMapping("/oauth/cache_approvals") | |
@ResponseStatus(HttpStatus.NO_CONTENT) | |
public void startCaching() throws Exception { | |
userApprovalHandler.setUseTokenServices(true); | |
} | |
@RequestMapping("/oauth/uncache_approvals") | |
@ResponseStatus(HttpStatus.NO_CONTENT) | |
public void stopCaching() throws Exception { | |
userApprovalHandler.setUseTokenServices(false); | |
} | |
@RequestMapping("/oauth/users/{user}/tokens") | |
@ResponseBody | |
public Collection<OAuth2AccessToken> listTokensForUser(@PathVariable String user, Principal principal) | |
throws Exception { | |
checkResourceOwner(user, principal); | |
return enhance(tokenServices.findTokensByUserName(user)); | |
} | |
@RequestMapping(value = "/oauth/users/{user}/tokens/{token}", method = RequestMethod.DELETE) | |
public ResponseEntity<Void> revokeToken(@PathVariable String user, @PathVariable String token, Principal principal) | |
throws Exception { | |
checkResourceOwner(user, principal); | |
if (tokenServices.revokeToken(token)) { | |
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT); | |
} else { | |
return new ResponseEntity<Void>(HttpStatus.NOT_FOUND); | |
} | |
} | |
@RequestMapping("/oauth/clients/{client}/tokens") | |
@ResponseBody | |
public Collection<OAuth2AccessToken> listTokensForClient(@PathVariable String client) throws Exception { | |
return enhance(tokenServices.findTokensByClientId(client)); | |
} | |
private Collection<OAuth2AccessToken> enhance(Collection<OAuth2AccessToken> tokens) { | |
Collection<OAuth2AccessToken> result = new ArrayList<OAuth2AccessToken>(); | |
for (OAuth2AccessToken prototype : tokens) { | |
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(prototype); | |
String clientId = tokenServices.getClientId(token.getValue()); | |
if (clientId != null) { | |
Map<String, Object> map = new HashMap<String, Object>(token.getAdditionalInformation()); | |
map.put("client_id", clientId); | |
token.setAdditionalInformation(map); | |
result.add(token); | |
} | |
} | |
return result; | |
} | |
private void checkResourceOwner(String user, Principal principal) { | |
if (principal instanceof OAuth2Authentication) { | |
OAuth2Authentication authentication = (OAuth2Authentication) principal; | |
if (!authentication.isClientOnly() && !user.equals(principal.getName())) { | |
throw new AccessDeniedException(String.format("User '%s' cannot obtain tokens for user '%s'", | |
principal.getName(), user)); | |
} | |
} | |
} | |
/** | |
* @param userApprovalHandler the userApprovalHandler to set | |
*/ | |
public void setUserApprovalHandler(SparklrUserApprovalHandler userApprovalHandler) { | |
this.userApprovalHandler = userApprovalHandler; | |
} | |
/** | |
* @param tokenServices the consumerTokenServices to set | |
*/ | |
public void setTokenServices(ConsumerTokenServices tokenServices) { | |
this.tokenServices = tokenServices; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
This file contains useful Ant definitions for users of App Engine. | |
To use these macrodefs and taskdefs, import the file into your own build.xml: | |
<property name="appengine.sdk.dir" location="/some_dir/appengine-java-sdk-trunk"/> | |
<import file="${appengine.sdk.dir}/config/user/ant-macros.xml"/> | |
For example uses of the macros, see the template project's build.xml. | |
--> | |
<project name="appengine-ant-macros"> | |
<property name="appengine.sdk.home" location="${sdk.dir}"/> | |
<property name="appengine.tools.classpath" | |
location="${appengine.sdk.home}/lib/appengine-tools-api.jar"/> | |
<!-- | |
A macrodef for dev_appserver. Use like: | |
<dev_appserver war="${war}"/> | |
--> | |
<macrodef name="dev_appserver" description="Runs the App Engine Development App Server"> | |
<attribute name="war" description="The exploded war directory containing the application"/> | |
<attribute name="port" default="8080" description="The port the server starts on"/> | |
<attribute name="address" default="localhost" description="The interface the server binds to"/> | |
<element name="options" optional="true" description="Additional options for dev_appserver"/> | |
<element name="args" optional="true" description="Additional arguments for the java task"/> | |
<sequential> | |
<java classname="com.google.appengine.tools.KickStart" | |
classpath="${appengine.tools.classpath}" | |
fork="true"> | |
<arg value="com.google.appengine.tools.development.DevAppServerMain"/> | |
<arg value="--port=@{port}"/> | |
<arg value="--address=@{address}"/> | |
<options/> | |
<arg value="@{war}"/> | |
<args/> | |
</java> | |
</sequential> | |
</macrodef> | |
<!-- | |
A macrodef for appcfg. Use like: | |
<appcfg action="update" war="${war}"/> | |
--> | |
<macrodef name="appcfg" description="Manages an application"> | |
<attribute name="war" description="The exploded war directory containing the application"/> | |
<attribute name="action" description="One of (update, rollback, update_indexes, request_logs)"/> | |
<element name="options" optional="true" description="Options for appcfg (such as --server, --num_days, etc...)"/> | |
<element name="args" optional="true" description="Additional arguments for the java task"/> | |
<sequential> | |
<java classname="com.google.appengine.tools.admin.AppCfg" | |
classpath="${appengine.tools.classpath}" | |
fork="true"> | |
<arg value="--disable_prompt"/> | |
<options/> | |
<arg value="@{action}"/> | |
<arg value="@{war}"/> | |
<args/> | |
</java> | |
</sequential> | |
</macrodef> | |
<!-- | |
A taskdef for ORM enhancement. Use like: | |
<enhance failonerror="true"> | |
<classpath> | |
<pathelement path="${appengine.tools.classpath}"/> | |
<pathelement path="@{war}/WEB-INF/classes"/> | |
<fileset dir="@{war}/WEB-INF/lib" includes="*.jar"/> | |
</classpath> | |
<fileset dir="@{war}/WEB-INF/classes" includes="**/*.class"/> | |
</enhance> | |
Alternatively, use the <enhance_war/> macrodef below. | |
--> | |
<taskdef name="enhance" | |
classpath="${appengine.tools.classpath}" | |
classname="com.google.appengine.tools.enhancer.EnhancerTask"/> | |
<!-- | |
A macrodef for ORM enhancement for a war. Use like: | |
<enhance_war war="${war}"/> | |
--> | |
<macrodef name="enhance_war" description="Run the ORM enhancer on an exploded war"> | |
<attribute name="war" description="The exploded war directory containing the application"/> | |
<element name="args" optional="true" description="Additional arguments to the enhancer"/> | |
<sequential> | |
<enhance failonerror="true"> | |
<args/> | |
<classpath> | |
<pathelement path="${appengine.tools.classpath}"/> | |
<pathelement path="@{war}/WEB-INF/classes"/> | |
<fileset dir="@{war}/WEB-INF/lib" includes="*.jar"/> | |
</classpath> | |
<fileset dir="@{war}/WEB-INF/classes" includes="**/*.class"/> | |
</enhance> | |
</sequential> | |
</macrodef> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> | |
<application>${gae.application.name}</application> | |
<version>1</version> | |
<sessions-enabled>true</sessions-enabled> | |
</appengine-web-app> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> | |
<title>Client Authentication Example</title> | |
<link type="text/css" rel="stylesheet" href="style.css" /> | |
<script type="text/javascript" | |
src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> | |
<script> | |
function authenticate() { | |
var appID = "my-less-trusted-client"; | |
var path = 'oauth/authorize?'; | |
var queryParams = [ 'client_id=' + appID, | |
'redirect_uri=' + window.location, 'scope=trust', | |
'response_type=token' ]; | |
var query = queryParams.join('&'); | |
var url = path + query; | |
window.open(url); | |
} | |
function display() { | |
var hash = window.location.hash; | |
var accessToken = hash.split('&')[0].split("=")[1]; | |
console.log('access-token:' + accessToken); | |
var headers = { | |
'Authorization' : 'Bearer ' + accessToken, | |
'Accept' : 'application/json' | |
}; | |
$.ajaxSetup({ | |
'headers' : headers, | |
dataType : 'text' | |
}); | |
$.get('photos/user/message', function(data) { | |
console.log('data:' + data); | |
$('#message').text(data); | |
}); | |
} | |
$(function() { | |
if (window.location.hash.length == 0) { | |
authenticate(); | |
} else { | |
display(); | |
} | |
}) | |
</script> | |
</head> | |
<body> | |
<h1>Sparklr Client Authentication Sample</h1> | |
<div id="content"> | |
<p>Once you have authenticated and approved the access, some | |
JavaScript in this page will render a message from Sparklr below</p> | |
<p id="message" /> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<project name="sparklr2"> | |
<fail unless="gae.sdk" message="You must specify a 'gae.sdk' property."/> | |
<property name="sdk.dir" location="${gae.sdk}"/> | |
<import file="ant-macros.xml"/> | |
<property name="war.dir" value="target/sparklr2-2.0-SNAPSHOT"/> | |
<target name="runserver" description="Starts the development server."> | |
<dev_appserver war="${war.dir}" port="8080"/> | |
</target> | |
<target name="update" description="Uploads the application to App Engine."> | |
<appcfg action="update" war="${war.dir}"/> | |
</target> | |
<target name="update_indexes" description="Uploads just the datastore index configuration to App Engine."> | |
<appcfg action="update_indexes" war="${war.dir}"/> | |
</target> | |
<target name="rollback" description="Rolls back an interrupted application update."> | |
<appcfg action="rollback" war="${war.dir}"/> | |
</target> | |
<target name="request_logs" description="Downloads log data from App Engine for the application."> | |
<appcfg action="request_logs" war="${war.dir}"> | |
<options> | |
<arg value="--num_days=5"/> | |
</options> | |
<args> | |
<arg value="logs.txt"/> | |
</args> | |
</appcfg> | |
</target> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
org.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.LogFactoryImpl | |
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
org.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.LogFactoryImpl | |
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<meta charset="utf-8"> | |
<link href="main.css" rel="stylesheet" type="text/css"/> | |
<title>Tonr JS Client Demo</title> | |
<script src="js/libs/json2.js"></script> | |
<script src="js/libs/localstorage.js"></script> | |
<script src="js/libs/modernizr-2.5.3.min.js"></script> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> | |
<script src="js/libs/jso.js"></script> | |
<script type="text/javascript"> | |
$(document).ready(function() { | |
// Add configuration for one or more providers. | |
jso_configure({ | |
"sparklr": { | |
client_id: "tonr", | |
redirect_uri: "http://localhost:8080/tonr2/demo.html", | |
authorization: "http://localhost:8080/sparklr2/oauth/authorize", | |
} | |
}); | |
// Perform a data request | |
$.oajax({ | |
url: "http://localhost:8080/sparklr2/photos?format=json", | |
jso_provider: "sparklr", // Will match the config identifier | |
jso_scopes: ["read"], // List of scopes (OPTIONAL) | |
jso_allowia: true, // Allow user interaction (OPTIONAL, default: false) | |
dataType: 'json', | |
success: function(data) { | |
console.log({response:data}); | |
$('#message').text(JSON.stringify(data)); | |
} | |
}); | |
jso_wipe(); | |
}); | |
</script> | |
</head> | |
<body> | |
<h1>Sparklr Client Authentication Sample</h1> | |
<div id="content"> | |
<p>Once you have authenticated and approved the access, some | |
JavaScript in this page will render a list of photos from Sparklr below:</p> | |
<p id="message" /> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %> | |
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<link href="<c:url value="/main.css"/>" rel="stylesheet" type="text/css"/> | |
<title>tonr</title> | |
</head> | |
<body> | |
<div id="container"> | |
<ul id="mainlinks"> | |
<li><a href="<c:url value="/index.jsp"/>">home</a></li> | |
<authz:authorize ifNotGranted="ROLE_USER"> | |
<li><a href="<c:url value="/login.jsp"/>">login</a></li> | |
</authz:authorize> | |
<li><a href="<c:url value="/sparklr/photos"/>">sparklr pics</a></li> | |
<li><a href="<c:url value="/facebook/info"/>" class="selected">facebook friends</a></li> | |
</ul> | |
<div id="content"> | |
<h1>Your Facebook Friends:</h1> | |
<ul> | |
<c:forEach var="friendName" items="${friends}"> | |
<li><c:out value="${friendName}"/></li> | |
</c:forEach> | |
</ul> | |
</div> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.tonr.mvc; | |
import java.util.ArrayList; | |
import org.codehaus.jackson.JsonNode; | |
import org.codehaus.jackson.node.ArrayNode; | |
import org.codehaus.jackson.node.ObjectNode; | |
import org.springframework.security.oauth2.client.OAuth2RestTemplate; | |
import org.springframework.stereotype.Controller; | |
import org.springframework.ui.Model; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.client.RestOperations; | |
/** | |
* @author Ryan Heaton | |
* @author Dave Syer | |
*/ | |
@Controller | |
public class FacebookController { | |
private RestOperations facebookRestTemplate; | |
@RequestMapping("/facebook/info") | |
public String photos(Model model) throws Exception { | |
ObjectNode result = facebookRestTemplate | |
.getForObject("https://graph.facebook.com/me/friends", ObjectNode.class); | |
ArrayNode data = (ArrayNode) result.get("data"); | |
ArrayList<String> friends = new ArrayList<String>(); | |
for (JsonNode dataNode : data) { | |
friends.add(dataNode.get("name").getTextValue()); | |
} | |
model.addAttribute("friends", friends); | |
return "facebook"; | |
} | |
public void setFacebookRestTemplate(OAuth2RestTemplate facebookRestTemplate) { | |
this.facebookRestTemplate = facebookRestTemplate; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %> | |
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/> | |
<title>Sparklr</title> | |
<link type="text/css" rel="stylesheet" href="<c:url value="/style.css"/>"/> | |
<authz:authorize ifAllGranted="ROLE_USER"> | |
<script type='text/javascript'> | |
function pictureDisplay(json) { | |
for (var i = 0; i < json.photos.length; i++) { | |
var photo = json.photos[i]; | |
document.write('<img src="photos/' + photo.id + '" alt="' + photo.name + '">'); | |
} | |
} | |
</script> | |
</authz:authorize> | |
</head> | |
<body> | |
<h1>Sparklr</h1> | |
<div id="content"> | |
<h2>Home</h2> | |
<p>This is a great site to store and view your photos. Unfortunately, we don't have any services | |
for printing your photos. For that, you'll have to go to Tonr.</p> | |
<authz:authorize ifNotGranted="ROLE_USER"> | |
<h2>Login</h2> | |
<form id="loginForm" name="loginForm" action="<c:url value="/login.do"/>" method="post"> | |
<p><label>Username: <input type='text' name='j_username' value="marissa"></label></p> | |
<p><label>Password: <input type='text' name='j_password' value="koala"></label></p> | |
<p><input name="login" value="Login" type="submit"></p> | |
</form> | |
</authz:authorize> | |
<authz:authorize ifAllGranted="ROLE_USER"> | |
<div style="text-align: center"><form action="<c:url value="/logout.do"/>"><input type="submit" value="Logout"></form></div> | |
<h2>Your Photos</h2> | |
<p> | |
<script type='text/javascript' src='photos?callback=pictureDisplay&format=json'></script> | |
</p> | |
</authz:authorize> | |
</div> | |
<div id="footer">Sample application for <a href="http://github.com/SpringSource/spring-security-oauth" target="_blank">Spring Security OAuth</a></div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %> | |
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<link href="<c:url value="/main.css"/>" rel="stylesheet" type="text/css"/> | |
<title>tonr</title> | |
</head> | |
<body> | |
<div id="container"> | |
<ul id="mainlinks"> | |
<li><a href="<c:url value="/index.jsp"/>" class="selected">home</a></li> | |
<authz:authorize ifNotGranted="ROLE_USER"> | |
<li><a href="<c:url value="/login.jsp"/>">login</a></li> | |
</authz:authorize> | |
<li><a href="<c:url value="/sparklr/photos"/>">sparklr pics</a></li> | |
<li><a href="<c:url value="/facebook/info"/>">facebook friends</a></li> | |
</ul> | |
<div id="content"> | |
<h1>Welcome to Tonr.com!</h1> | |
<p>This is a website that will allow you to print your photos that you've uploaded to <a href="http://localhost:8080/sparklr/">sparklr.com</a>! | |
And since this site uses <a href="http://oauth.net">OAuth</a> to access your photos, we will never ask you | |
for your Sparklr credentials.</p> | |
<p>Tonr.com has only two users: "marissa" and "sam". The password for "marissa" is password is "wombat" and for "sam" is password is "kangaroo".</p> | |
<authz:authorize ifNotGranted="ROLE_USER"> | |
<p><a href="<c:url value="login.jsp"/>">Login to Tonr</a></p> | |
</authz:authorize> | |
<authz:authorize ifAllGranted="ROLE_USER"> | |
<p><a href="<c:url value="/sparklr/photos"/>">View my Sparklr photos</a></p> | |
</authz:authorize> | |
<p class="footer">Courtesy <a href="http://www.openwebdesign.org">Open Web Design</a> Thanks to <a href="http://www.dubaiapartments.biz/">Dubai Hotels</a></p> | |
</div> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(exp, $) { | |
var | |
config = {}, | |
default_lifetime = 3600; | |
/* | |
* ------ SECTION: Utilities | |
*/ | |
/* | |
* Returns a random string used for state | |
*/ | |
var uuid = function() { | |
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { | |
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); | |
return v.toString(16); | |
}); | |
} | |
/* | |
* Takes an URL as input and a params object. | |
* Each property in the params is added to the url as query string parameters | |
*/ | |
var encodeURL= function(url, params) { | |
var res = url; | |
var k, i = 0; | |
for(k in params) { | |
res += (i++ === 0 ? '?' : '&') + encodeURIComponent(k) + '=' + encodeURIComponent(params[k]); | |
} | |
return res; | |
} | |
/* | |
* Returns epoch, seconds since 1970. | |
* Used for calculation of expire times. | |
*/ | |
var epoch = function() { | |
return Math.round(new Date().getTime()/1000.0); | |
} | |
/* | |
* Redirects the user to a specific URL | |
*/ | |
var redirect = function(url) { | |
window.location = url; | |
// $("body").append('<p><a href="' + url + '">Go here...</a></p>'); | |
} | |
var parseQueryString = function (qs) { | |
var e, | |
a = /\+/g, // Regex for replacing addition symbol with a space | |
r = /([^&;=]+)=?([^&;]*)/g, | |
d = function (s) { return decodeURIComponent(s.replace(a, " ")); }, | |
q = qs, | |
urlParams = {}; | |
while (e = r.exec(q)) | |
urlParams[d(e[1])] = d(e[2]); | |
return urlParams; | |
} | |
/* | |
* ------ / SECTION: Utilities | |
*/ | |
/* | |
* ------ SECTION: Storage for Tokens and state | |
*/ | |
/* | |
saveState stores an object with an Identifier. | |
TODO: Ensure that both localstorage and JSON encoding has fallbacks for ancient browsers. | |
In the state object, we put the request object, plus these parameters: | |
* restoreHash | |
* providerID | |
* scopes | |
*/ | |
var saveState = function(state, obj) { | |
// console.log("SaveState (" + state+ ")"); | |
localStorage.setItem("state-" + state, JSON.stringify(obj)); | |
}; | |
/* | |
* getState returns the state object, but also removes it. | |
*/ | |
var getState = function(state) { | |
// console.log("getState (" + state+ ")"); | |
var obj = JSON.parse(localStorage.getItem("state-" + state)); | |
localStorage.removeItem("state-" + state) | |
return obj; | |
}; | |
/* | |
* Checks if a token, has includes a specific scope. | |
* If token has no scope at all, false is returned. | |
*/ | |
var hasScope = function(token, scope) { | |
var i; | |
if (!token.scopes) return false; | |
for(i = 0; i < token.scopes.length; i++) { | |
if (token.scopes[i] === scope) return true; | |
} | |
return false; | |
}; | |
/* | |
* Takes an array of tokens, and removes the ones that | |
* are expired, and the ones that do not meet a scopes requirement. | |
*/ | |
var filterTokens = function(tokens, scopes) { | |
var i, j, | |
result = [], | |
now = epoch(), | |
usethis; | |
if (!scopes) scopes = []; | |
for(i = 0; i < tokens.length; i++) { | |
usethis = true; | |
// Filter out expired tokens. Tokens that is expired in 1 second from now. | |
if (tokens[i].expires && tokens[i].expires < (now+1)) usethis = false; | |
// Filter out this token if not all scope requirements are met | |
for(j = 0; j < scopes.length; j++) { | |
if (!hasScope(tokens[i], scopes[j])) usethis = false; | |
} | |
if (usethis) result.push(tokens[i]); | |
} | |
return result; | |
}; | |
/* | |
* saveTokens() stores a list of tokens for a provider. | |
Usually the tokens stored are a plain Access token plus: | |
* expires : time that the token expires | |
* providerID: the provider of the access token? | |
* scopes: an array with the scopes (not string) | |
*/ | |
var saveTokens = function(provider, tokens) { | |
// console.log("Save Tokens (" + provider+ ")"); | |
localStorage.setItem("tokens-" + provider, JSON.stringify(tokens)); | |
}; | |
var getTokens = function(provider) { | |
// console.log("Get Tokens (" + provider+ ")"); | |
var tokens = JSON.parse(localStorage.getItem("tokens-" + provider)); | |
if (!tokens) tokens = []; | |
// console.log(tokens) | |
return tokens; | |
}; | |
var wipeTokens = function(provider) { | |
localStorage.removeItem("tokens-" + provider); | |
}; | |
/* | |
* Save a single token for a provider. | |
* This also cleans up expired tokens for the same provider. | |
*/ | |
var saveToken = function(provider, token) { | |
var tokens = getTokens(provider); | |
tokens = filterTokens(tokens); | |
tokens.push(token); | |
saveTokens(provider, tokens); | |
}; | |
/* | |
* Get a token if exists for a provider with a set of scopes. | |
* The scopes parameter is OPTIONAL. | |
*/ | |
var getToken = function(provider, scopes) { | |
var tokens = getTokens(provider); | |
tokens = filterTokens(tokens, scopes); | |
if (tokens.length < 1) return null; | |
return tokens[0]; | |
}; | |
/* | |
* ------ /SECTION: Storage for Tokens and state | |
*/ | |
/** | |
* Check if the hash contains an access token. | |
* And if it do, extract the state, compare with | |
* config, and store the access token for later use. | |
*/ | |
var jso_checkfortoken = function() { | |
var | |
atoken, | |
h = window.location.hash, | |
now = epoch(), | |
state, | |
co; | |
/* | |
* Start with checking if there is a token in the hash | |
*/ | |
if (h.length < 2) return; | |
if (h.indexOf("access_token") === -1) return; | |
h = h.substring(1); | |
var atoken = parseQueryString(h); | |
if (!atoken.state) return; | |
state = getState(atoken.state); | |
if (!state) throw "Could not retrieve state"; | |
if (!state.providerID) throw "Could not get providerid from state"; | |
if (!config[state.providerID]) throw "Could not retrieve config for this provider."; | |
co = config[state.providerID]; | |
/* | |
* Decide when this token should expire. | |
* Priority fallback: | |
* 1. Access token expires_in | |
* 2. Life time in config (may be false = permanent...) | |
* 3. Specific permanent scope. | |
* 4. Default library lifetime: | |
*/ | |
if (atoken["expires_in"]) { | |
atoken["expires"] = now + atoken["expires_in"]; | |
} else if (co["default_lifetime"] === false) { | |
// Token is permanent. | |
} else if (co["default_lifetime"]) { | |
atoken["expires"] = now + co["default_lifetime"]; | |
} else if (co["permanent_scope"]) { | |
if (!hasScope(atoken, co["permanent_scope"])) { | |
atoken["expires"] = now + default_lifetime; | |
} | |
} else { | |
atoken["expires"] = now + default_lifetime; | |
} | |
/* | |
* Handle scopes for this token | |
*/ | |
if (atoken["scope"]) { | |
atoken["scopes"] = atoken["scope"].split(" "); | |
} else if (state["scopes"]) { | |
atoken["scopes"] = state["scopes"]; | |
} | |
saveToken(state.providerID, atoken); | |
if (state.restoreHash) { | |
window.location.hash = state.restoreHash; | |
} else { | |
window.location.hash = ''; | |
} | |
// console.log(atoken); | |
} | |
/* | |
* A config object contains: | |
*/ | |
var jso_authrequest = function(providerid, scopes) { | |
var | |
state, | |
request, | |
authurl, | |
co; | |
if (!config[providerid]) throw "Could not find configuration for provider " + providerid; | |
co = config[providerid]; | |
// console.log("About to send an authorization request to [" + providerid + "]. Config:") | |
// console.log(co); | |
state = uuid(); | |
request = { | |
"response_type": "token" | |
}; | |
request.state = state; | |
if (co["redirect_uri"]) { | |
request["redirect_uri"] = co["redirect_uri"]; | |
} | |
if (co["client_id"]) { | |
request["client_id"] = co["client_id"]; | |
} | |
if (scopes) { | |
request["scope"] = scopes.join(" "); | |
} | |
authurl = encodeURL(co.authorization, request); | |
// We'd like to cache the hash for not loosing Application state. | |
// With the implciit grant flow, the hash will be replaced with the access | |
// token when we return after authorization. | |
if (window.location.hash) { | |
request["restoreHash"] = window.location.hash; | |
} | |
request["providerID"] = providerid; | |
if (scopes) { | |
request["scopes"] = scopes; | |
} | |
// console.log("Saving state [" + state+ "]"); | |
// console.log(JSON.parse(JSON.stringify(request))); | |
saveState(state, request); | |
redirect(authurl); | |
}; | |
exp.jso_ensureTokens = function (ensure) { | |
var providerid, scopes, token; | |
for(providerid in ensure) { | |
scopes = undefined; | |
if (ensure[providerid]) scopes = ensure[providerid]; | |
token = getToken(providerid, scopes); | |
// console.log("Ensure token for provider [" + providerid + "] "); | |
// console.log(token); | |
if (token === null) { | |
jso_authrequest(providerid, scopes); | |
} | |
} | |
return true; | |
} | |
exp.jso_configure = function(c) { | |
config = c; | |
try { | |
jso_checkfortoken(); | |
} catch(e) { | |
console.log("Error when retrieving token from hash: " + e); | |
window.location.hash = ""; | |
} | |
} | |
exp.jso_dump = function() { | |
var key; | |
for(key in config) { | |
console.log("=====> Processing provider [" + key + "]"); | |
console.log("=] Config"); | |
console.log(config[key]); | |
console.log("=] Tokens") | |
console.log(getTokens(key)); | |
} | |
} | |
exp.jso_wipe = function() { | |
var key; | |
for(key in config) { | |
wipeTokens(key); | |
} | |
} | |
exp.jso_getToken = function(providerid, scopes) { | |
var token = getToken(providerid, scopes); | |
if (!token) return null; | |
if (!token["access_token"]) return null; | |
return token["access_token"]; | |
} | |
$.oajax = function(settings) { | |
var | |
allowia, | |
scopes, | |
token, | |
providerid, | |
co; | |
providerid = settings.jso_provider; | |
allowia = settings.jso_allowia || false; | |
scopes = settings.jso_scopes; | |
token = getToken(providerid, scopes); | |
co = config[providerid]; | |
if (!token) { | |
if (allowia) { | |
jso_authrequest(providerid, scopes); | |
return; | |
} else { | |
throw "Could not perform AJAX call because no valid tokens was found."; | |
} | |
} | |
if (co["presenttoken"] && co["presenttoken"] === "qs") { | |
// settings.url += ((h.indexOf("?") === -1) ? '?' : '&') + "access_token=" + encodeURIComponent(token["access_token"]); | |
if (!settings.data) settings.data = {}; | |
settings.data["access_token"] = token["access_token"]; | |
} else { | |
if (!settings.headers) settings.headers = {}; | |
settings.headers["Authorization"] = "Bearer " + token["access_token"]; | |
} | |
$.ajax(settings); | |
}; | |
})(window, jQuery); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
http://www.JSON.org/json2.js | |
2011-10-19 | |
Public Domain. | |
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. | |
See http://www.JSON.org/js.html | |
This code should be minified before deployment. | |
See http://javascript.crockford.com/jsmin.html | |
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO | |
NOT CONTROL. | |
This file creates a global JSON object containing two methods: stringify | |
and parse. | |
JSON.stringify(value, replacer, space) | |
value any JavaScript value, usually an object or array. | |
replacer an optional parameter that determines how object | |
values are stringified for objects. It can be a | |
function or an array of strings. | |
space an optional parameter that specifies the indentation | |
of nested structures. If it is omitted, the text will | |
be packed without extra whitespace. If it is a number, | |
it will specify the number of spaces to indent at each | |
level. If it is a string (such as '\t' or ' '), | |
it contains the characters used to indent at each level. | |
This method produces a JSON text from a JavaScript value. | |
When an object value is found, if the object contains a toJSON | |
method, its toJSON method will be called and the result will be | |
stringified. A toJSON method does not serialize: it returns the | |
value represented by the name/value pair that should be serialized, | |
or undefined if nothing should be serialized. The toJSON method | |
will be passed the key associated with the value, and this will be | |
bound to the value | |
For example, this would serialize Dates as ISO strings. | |
Date.prototype.toJSON = function (key) { | |
function f(n) { | |
// Format integers to have at least two digits. | |
return n < 10 ? '0' + n : n; | |
} | |
return this.getUTCFullYear() + '-' + | |
f(this.getUTCMonth() + 1) + '-' + | |
f(this.getUTCDate()) + 'T' + | |
f(this.getUTCHours()) + ':' + | |
f(this.getUTCMinutes()) + ':' + | |
f(this.getUTCSeconds()) + 'Z'; | |
}; | |
You can provide an optional replacer method. It will be passed the | |
key and value of each member, with this bound to the containing | |
object. The value that is returned from your method will be | |
serialized. If your method returns undefined, then the member will | |
be excluded from the serialization. | |
If the replacer parameter is an array of strings, then it will be | |
used to select the members to be serialized. It filters the results | |
such that only members with keys listed in the replacer array are | |
stringified. | |
Values that do not have JSON representations, such as undefined or | |
functions, will not be serialized. Such values in objects will be | |
dropped; in arrays they will be replaced with null. You can use | |
a replacer function to replace those with JSON values. | |
JSON.stringify(undefined) returns undefined. | |
The optional space parameter produces a stringification of the | |
value that is filled with line breaks and indentation to make it | |
easier to read. | |
If the space parameter is a non-empty string, then that string will | |
be used for indentation. If the space parameter is a number, then | |
the indentation will be that many spaces. | |
Example: | |
text = JSON.stringify(['e', {pluribus: 'unum'}]); | |
// text is '["e",{"pluribus":"unum"}]' | |
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); | |
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' | |
text = JSON.stringify([new Date()], function (key, value) { | |
return this[key] instanceof Date ? | |
'Date(' + this[key] + ')' : value; | |
}); | |
// text is '["Date(---current time---)"]' | |
JSON.parse(text, reviver) | |
This method parses a JSON text to produce an object or array. | |
It can throw a SyntaxError exception. | |
The optional reviver parameter is a function that can filter and | |
transform the results. It receives each of the keys and values, | |
and its return value is used instead of the original value. | |
If it returns what it received, then the structure is not modified. | |
If it returns undefined then the member is deleted. | |
Example: | |
// Parse the text. Values that look like ISO date strings will | |
// be converted to Date objects. | |
myData = JSON.parse(text, function (key, value) { | |
var a; | |
if (typeof value === 'string') { | |
a = | |
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); | |
if (a) { | |
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], | |
+a[5], +a[6])); | |
} | |
} | |
return value; | |
}); | |
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { | |
var d; | |
if (typeof value === 'string' && | |
value.slice(0, 5) === 'Date(' && | |
value.slice(-1) === ')') { | |
d = new Date(value.slice(5, -1)); | |
if (d) { | |
return d; | |
} | |
} | |
return value; | |
}); | |
This is a reference implementation. You are free to copy, modify, or | |
redistribute. | |
*/ | |
/*jslint evil: true, regexp: true */ | |
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, | |
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, | |
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, | |
lastIndex, length, parse, prototype, push, replace, slice, stringify, | |
test, toJSON, toString, valueOf | |
*/ | |
// Create a JSON object only if one does not already exist. We create the | |
// methods in a closure to avoid creating global variables. | |
var JSON; | |
if (!JSON) { | |
JSON = {}; | |
} | |
(function () { | |
'use strict'; | |
function f(n) { | |
// Format integers to have at least two digits. | |
return n < 10 ? '0' + n : n; | |
} | |
if (typeof Date.prototype.toJSON !== 'function') { | |
Date.prototype.toJSON = function (key) { | |
return isFinite(this.valueOf()) | |
? this.getUTCFullYear() + '-' + | |
f(this.getUTCMonth() + 1) + '-' + | |
f(this.getUTCDate()) + 'T' + | |
f(this.getUTCHours()) + ':' + | |
f(this.getUTCMinutes()) + ':' + | |
f(this.getUTCSeconds()) + 'Z' | |
: null; | |
}; | |
String.prototype.toJSON = | |
Number.prototype.toJSON = | |
Boolean.prototype.toJSON = function (key) { | |
return this.valueOf(); | |
}; | |
} | |
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, | |
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, | |
gap, | |
indent, | |
meta = { // table of character substitutions | |
'\b': '\\b', | |
'\t': '\\t', | |
'\n': '\\n', | |
'\f': '\\f', | |
'\r': '\\r', | |
'"' : '\\"', | |
'\\': '\\\\' | |
}, | |
rep; | |
function quote(string) { | |
// If the string contains no control characters, no quote characters, and no | |
// backslash characters, then we can safely slap some quotes around it. | |
// Otherwise we must also replace the offending characters with safe escape | |
// sequences. | |
escapable.lastIndex = 0; | |
return escapable.test(string) ? '"' + string.replace(escapable, function (a) { | |
var c = meta[a]; | |
return typeof c === 'string' | |
? c | |
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); | |
}) + '"' : '"' + string + '"'; | |
} | |
function str(key, holder) { | |
// Produce a string from holder[key]. | |
var i, // The loop counter. | |
k, // The member key. | |
v, // The member value. | |
length, | |
mind = gap, | |
partial, | |
value = holder[key]; | |
// If the value has a toJSON method, call it to obtain a replacement value. | |
if (value && typeof value === 'object' && | |
typeof value.toJSON === 'function') { | |
value = value.toJSON(key); | |
} | |
// If we were called with a replacer function, then call the replacer to | |
// obtain a replacement value. | |
if (typeof rep === 'function') { | |
value = rep.call(holder, key, value); | |
} | |
// What happens next depends on the value's type. | |
switch (typeof value) { | |
case 'string': | |
return quote(value); | |
case 'number': | |
// JSON numbers must be finite. Encode non-finite numbers as null. | |
return isFinite(value) ? String(value) : 'null'; | |
case 'boolean': | |
case 'null': | |
// If the value is a boolean or null, convert it to a string. Note: | |
// typeof null does not produce 'null'. The case is included here in | |
// the remote chance that this gets fixed someday. | |
return String(value); | |
// If the type is 'object', we might be dealing with an object or an array or | |
// null. | |
case 'object': | |
// Due to a specification blunder in ECMAScript, typeof null is 'object', | |
// so watch out for that case. | |
if (!value) { | |
return 'null'; | |
} | |
// Make an array to hold the partial results of stringifying this object value. | |
gap += indent; | |
partial = []; | |
// Is the value an array? | |
if (Object.prototype.toString.apply(value) === '[object Array]') { | |
// The value is an array. Stringify every element. Use null as a placeholder | |
// for non-JSON values. | |
length = value.length; | |
for (i = 0; i < length; i += 1) { | |
partial[i] = str(i, value) || 'null'; | |
} | |
// Join all of the elements together, separated with commas, and wrap them in | |
// brackets. | |
v = partial.length === 0 | |
? '[]' | |
: gap | |
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' | |
: '[' + partial.join(',') + ']'; | |
gap = mind; | |
return v; | |
} | |
// If the replacer is an array, use it to select the members to be stringified. | |
if (rep && typeof rep === 'object') { | |
length = rep.length; | |
for (i = 0; i < length; i += 1) { | |
if (typeof rep[i] === 'string') { | |
k = rep[i]; | |
v = str(k, value); | |
if (v) { | |
partial.push(quote(k) + (gap ? ': ' : ':') + v); | |
} | |
} | |
} | |
} else { | |
// Otherwise, iterate through all of the keys in the object. | |
for (k in value) { | |
if (Object.prototype.hasOwnProperty.call(value, k)) { | |
v = str(k, value); | |
if (v) { | |
partial.push(quote(k) + (gap ? ': ' : ':') + v); | |
} | |
} | |
} | |
} | |
// Join all of the member texts together, separated with commas, | |
// and wrap them in braces. | |
v = partial.length === 0 | |
? '{}' | |
: gap | |
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' | |
: '{' + partial.join(',') + '}'; | |
gap = mind; | |
return v; | |
} | |
} | |
// If the JSON object does not yet have a stringify method, give it one. | |
if (typeof JSON.stringify !== 'function') { | |
JSON.stringify = function (value, replacer, space) { | |
// The stringify method takes a value and an optional replacer, and an optional | |
// space parameter, and returns a JSON text. The replacer can be a function | |
// that can replace values, or an array of strings that will select the keys. | |
// A default replacer method can be provided. Use of the space parameter can | |
// produce text that is more easily readable. | |
var i; | |
gap = ''; | |
indent = ''; | |
// If the space parameter is a number, make an indent string containing that | |
// many spaces. | |
if (typeof space === 'number') { | |
for (i = 0; i < space; i += 1) { | |
indent += ' '; | |
} | |
// If the space parameter is a string, it will be used as the indent string. | |
} else if (typeof space === 'string') { | |
indent = space; | |
} | |
// If there is a replacer, it must be a function or an array. | |
// Otherwise, throw an error. | |
rep = replacer; | |
if (replacer && typeof replacer !== 'function' && | |
(typeof replacer !== 'object' || | |
typeof replacer.length !== 'number')) { | |
throw new Error('JSON.stringify'); | |
} | |
// Make a fake root object containing our value under the key of ''. | |
// Return the result of stringifying the value. | |
return str('', {'': value}); | |
}; | |
} | |
// If the JSON object does not yet have a parse method, give it one. | |
if (typeof JSON.parse !== 'function') { | |
JSON.parse = function (text, reviver) { | |
// The parse method takes a text and an optional reviver function, and returns | |
// a JavaScript value if the text is a valid JSON text. | |
var j; | |
function walk(holder, key) { | |
// The walk method is used to recursively walk the resulting structure so | |
// that modifications can be made. | |
var k, v, value = holder[key]; | |
if (value && typeof value === 'object') { | |
for (k in value) { | |
if (Object.prototype.hasOwnProperty.call(value, k)) { | |
v = walk(value, k); | |
if (v !== undefined) { | |
value[k] = v; | |
} else { | |
delete value[k]; | |
} | |
} | |
} | |
} | |
return reviver.call(holder, key, value); | |
} | |
// Parsing happens in four stages. In the first stage, we replace certain | |
// Unicode characters with escape sequences. JavaScript handles many characters | |
// incorrectly, either silently deleting them, or treating them as line endings. | |
text = String(text); | |
cx.lastIndex = 0; | |
if (cx.test(text)) { | |
text = text.replace(cx, function (a) { | |
return '\\u' + | |
('0000' + a.charCodeAt(0).toString(16)).slice(-4); | |
}); | |
} | |
// In the second stage, we run the text against regular expressions that look | |
// for non-JSON patterns. We are especially concerned with '()' and 'new' | |
// because they can cause invocation, and '=' because it can cause mutation. | |
// But just to be safe, we want to reject all unexpected forms. | |
// We split the second stage into 4 regexp operations in order to work around | |
// crippling inefficiencies in IE's and Safari's regexp engines. First we | |
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we | |
// replace all simple value tokens with ']' characters. Third, we delete all | |
// open brackets that follow a colon or comma or that begin the text. Finally, | |
// we look to see that the remaining characters are only whitespace or ']' or | |
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. | |
if (/^[\],:{}\s]*$/ | |
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') | |
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') | |
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { | |
// In the third stage we use the eval function to compile the text into a | |
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity | |
// in JavaScript: it can begin a block or an object literal. We wrap the text | |
// in parens to eliminate the ambiguity. | |
j = eval('(' + text + ')'); | |
// In the optional fourth stage, we recursively walk the new structure, passing | |
// each name/value pair to a reviver function for possible transformation. | |
return typeof reviver === 'function' | |
? walk({'': j}, '') | |
: j; | |
} | |
// If the text is not JSON parseable, then a SyntaxError is thrown. | |
throw new SyntaxError('JSON.parse'); | |
}; | |
} | |
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE | |
("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE | |
OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. | |
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS | |
OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE | |
OF SUCH TERMS AND CONDITIONS. | |
1. Definitions | |
a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, | |
in which the Work in its entirety in unmodified form, along with a number of other | |
contributions, constituting separate and independent works in themselves, are assembled | |
into a collective whole. A work that constitutes a Collective Work will not be | |
considered a Derivative Work (as defined below) for the purposes of this License. | |
b. "Derivative Work" means a work based upon the Work or upon the Work and other | |
pre-existing works, such as a translation, musical arrangement, dramatization, | |
fictionalization, motion picture version, sound recording, art reproduction, abridgment, | |
condensation, or any other form in which the Work may be recast, transformed, or adapted, | |
except that a work that constitutes a Collective Work will not be considered a Derivative | |
Work for the purpose of this License. For the avoidance of doubt, where the Work is a | |
musical composition or sound recording, the synchronization of the Work in timed-relation | |
with a moving image ("synching") will be considered a Derivative Work for the purpose of | |
this License. | |
c. "Licensor" means the individual or entity that offers the Work under the terms of this | |
License. | |
d. "Original Author" means the individual or entity who created the Work. | |
e. "Work" means the copyrightable work of authorship offered under the terms of this | |
License. | |
f. "You" means an individual or entity exercising rights under this License who has not | |
previously violated the terms of this License with respect to the Work, or who has | |
received express permission from the Licensor to exercise rights under this License | |
despite a previous violation. | |
g. "License Elements" means the following high-level license attributes as selected by | |
Licensor and indicated in the title of this License: Attribution, ShareAlike. | |
2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights | |
arising from fair use, first sale or other limitations on the exclusive rights of the copyright | |
owner under copyright law or other applicable laws. | |
3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants | |
You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable | |
copyright) license to exercise the rights in the Work as stated below: | |
a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to | |
reproduce the Work as incorporated in the Collective Works; | |
b. to create and reproduce Derivative Works; | |
c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform | |
publicly by means of a digital audio transmission the Work including as incorporated in | |
Collective Works; | |
d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform | |
publicly by means of a digital audio transmission Derivative Works. | |
e. For the avoidance of doubt, where the work is a musical composition: | |
i. Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to | |
collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, | |
SESAC), royalties for the public performance or public digital performance (e.g. | |
webcast) of the Work. | |
ii. Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to | |
collect, whether individually or via a music rights society or designated agent (e.g. | |
Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover | |
version") and distribute, subject to the compulsory license created by 17 USC Section | |
115 of the US Copyright Act (or the equivalent in other jurisdictions). | |
f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is | |
a sound recording, Licensor waives the exclusive right to collect, whether individually | |
or via a performance-rights society (e.g. SoundExchange), royalties for the public | |
digital performance (e.g. webcast) of the Work, subject to the compulsory license created | |
by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions). | |
The above rights may be exercised in all media and formats whether now known or hereafter devised. | |
The above rights include the right to make such modifications as are technically necessary to | |
exercise the rights in other media and formats. All rights not expressly granted by Licensor are | |
hereby reserved. | |
4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by | |
the following restrictions: | |
a. You may distribute, publicly display, publicly perform, or publicly digitally perform the | |
Work only under the terms of this License, and You must include a copy of, or the Uniform | |
Resource Identifier for, this License with every copy or phonorecord of the Work You | |
distribute, publicly display, publicly perform, or publicly digitally perform. You may | |
not offer or impose any terms on the Work that alter or restrict the terms of this | |
License or the recipients' exercise of the rights granted hereunder. You may not | |
sublicense the Work. You must keep intact all notices that refer to this License and to | |
the disclaimer of warranties. You may not distribute, publicly display, publicly perform, | |
or publicly digitally perform the Work with any technological measures that control | |
access or use of the Work in a manner inconsistent with the terms of this License | |
Agreement. The above applies to the Work as incorporated in a Collective Work, but this | |
does not require the Collective Work apart from the Work itself to be made subject to the | |
terms of this License. If You create a Collective Work, upon notice from any Licensor You | |
must, to the extent practicable, remove from the Collective Work any reference to such | |
Licensor or the Original Author, as requested. If You create a Derivative Work, upon | |
notice from any Licensor You must, to the extent practicable, remove from the Derivative | |
Work any reference to such Licensor or the Original Author, as requested. | |
b. You may distribute, publicly display, publicly perform, or publicly digitally perform a | |
Derivative Work only under the terms of this License, a later version of this License | |
with the same License Elements as this License, or a Creative Commons iCommons license | |
that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.0 | |
Japan). You must include a copy of, or the Uniform Resource Identifier for, this License | |
or other license specified in the previous sentence with every copy or phonorecord of | |
each Derivative Work You distribute, publicly display, publicly perform, or publicly | |
digitally perform. You may not offer or impose any terms on the Derivative Works that | |
alter or restrict the terms of this License or the recipients' exercise of the rights | |
granted hereunder, and You must keep intact all notices that refer to this License and to | |
the disclaimer of warranties. You may not distribute, publicly display, publicly perform, | |
or publicly digitally perform the Derivative Work with any technological measures that | |
control access or use of the Work in a manner inconsistent with the terms of this License | |
Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, | |
but this does not require the Collective Work apart from the Derivative Work itself to be | |
made subject to the terms of this License. | |
c. If you distribute, publicly display, publicly perform, or publicly digitally perform the | |
Work or any Derivative Works or Collective Works, You must keep intact all copyright | |
notices for the Work and give the Original Author credit reasonable to the medium or | |
means You are utilizing by conveying the name (or pseudonym if applicable) of the | |
Original Author if supplied; the title of the Work if supplied; to the extent reasonably | |
practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be | |
associated with the Work, unless such URI does not refer to the copyright notice or | |
licensing information for the Work; and in the case of a Derivative Work, a credit | |
identifying the use of the Work in the Derivative Work (e.g., "French translation of the | |
Work by Original Author," or "Screenplay based on original Work by Original Author"). | |
Such credit may be implemented in any reasonable manner; provided, however, that in the | |
case of a Derivative Work or Collective Work, at a minimum such credit will appear where | |
any other comparable authorship credit appears and in a manner at least as prominent as | |
such other comparable authorship credit. | |
5. Representations, Warranties and Disclaimer | |
UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO | |
REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR | |
OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A | |
PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE | |
PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE | |
EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. | |
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL | |
LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE | |
OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN | |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
7. Termination | |
a. This License and the rights granted hereunder will terminate automatically upon any | |
breach by You of the terms of this License. Individuals or entities who have received | |
Derivative Works or Collective Works from You under this License, however, will not have | |
their licenses terminated provided such individuals or entities remain in full compliance | |
with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this | |
License. | |
b. Subject to the above terms and conditions, the license granted here is perpetual (for the | |
duration of the applicable copyright in the Work). Notwithstanding the above, Licensor | |
reserves the right to release the Work under different license terms or to stop | |
distributing the Work at any time; provided, however that any such election will not | |
serve to withdraw this License (or any other license that has been, or is required to be, | |
granted under the terms of this License), and this License will continue in full force | |
and effect unless terminated as stated above. | |
8. Miscellaneous | |
a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the | |
Licensor offers to the recipient a license to the Work on the same terms and conditions | |
as the license granted to You under this License. | |
b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers | |
to the recipient a license to the original Work on the same terms and conditions as the | |
license granted to You under this License. | |
c. If any provision of this License is invalid or unenforceable under applicable law, it | |
shall not affect the validity or enforceability of the remainder of the terms of this | |
License, and without further action by the parties to this agreement, such provision | |
shall be reformed to the minimum extent necessary to make such provision valid and | |
enforceable. | |
d. No term or provision of this License shall be deemed waived and no breach consented to | |
unless such waiver or consent shall be in writing and signed by the party to be charged | |
with such waiver or consent. | |
e. This License constitutes the entire agreement between the parties with respect to the | |
Work licensed here. There are no understandings, agreements or representations with | |
respect to the Work not specified here. Licensor shall not be bound by any additional | |
provisions that may appear in any communication from You. This License may not be | |
modified without the mutual written agreement of the Licensor and You. | |
Creative Commons is not a party to this License, and makes no warranty whatsoever in connection | |
with the Work. Creative Commons will not be liable to You or any party on any legal theory for | |
any damages whatsoever, including without limitation any general, special, incidental or | |
consequential damages arising in connection to this license. Notwithstanding the foregoing two | |
(2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, | |
it shall have all rights and obligations of Licensor. | |
Except for the limited purpose of indicating to the public that the Work is licensed under the | |
CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo | |
of Creative Commons without the prior written consent of Creative Commons. Any permitted use will | |
be in compliance with Creative Commons' then-current trademark usage guidelines, as may be | |
published on its website or otherwise made available upon request from time to time. | |
Creative Commons may be contacted at http://creativecommons.org/. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE | |
("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE | |
OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. | |
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS | |
OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE | |
OF SUCH TERMS AND CONDITIONS. | |
1. Definitions | |
a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, | |
in which the Work in its entirety in unmodified form, along with a number of other | |
contributions, constituting separate and independent works in themselves, are assembled | |
into a collective whole. A work that constitutes a Collective Work will not be | |
considered a Derivative Work (as defined below) for the purposes of this License. | |
b. "Derivative Work" means a work based upon the Work or upon the Work and other | |
pre-existing works, such as a translation, musical arrangement, dramatization, | |
fictionalization, motion picture version, sound recording, art reproduction, abridgment, | |
condensation, or any other form in which the Work may be recast, transformed, or adapted, | |
except that a work that constitutes a Collective Work will not be considered a Derivative | |
Work for the purpose of this License. For the avoidance of doubt, where the Work is a | |
musical composition or sound recording, the synchronization of the Work in timed-relation | |
with a moving image ("synching") will be considered a Derivative Work for the purpose of | |
this License. | |
c. "Licensor" means the individual or entity that offers the Work under the terms of this | |
License. | |
d. "Original Author" means the individual or entity who created the Work. | |
e. "Work" means the copyrightable work of authorship offered under the terms of this | |
License. | |
f. "You" means an individual or entity exercising rights under this License who has not | |
previously violated the terms of this License with respect to the Work, or who has | |
received express permission from the Licensor to exercise rights under this License | |
despite a previous violation. | |
g. "License Elements" means the following high-level license attributes as selected by | |
Licensor and indicated in the title of this License: Attribution, ShareAlike. | |
2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights | |
arising from fair use, first sale or other limitations on the exclusive rights of the copyright | |
owner under copyright law or other applicable laws. | |
3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants | |
You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable | |
copyright) license to exercise the rights in the Work as stated below: | |
a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to | |
reproduce the Work as incorporated in the Collective Works; | |
b. to create and reproduce Derivative Works; | |
c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform | |
publicly by means of a digital audio transmission the Work including as incorporated in | |
Collective Works; | |
d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform | |
publicly by means of a digital audio transmission Derivative Works. | |
e. For the avoidance of doubt, where the work is a musical composition: | |
i. Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to | |
collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, | |
SESAC), royalties for the public performance or public digital performance (e.g. | |
webcast) of the Work. | |
ii. Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to | |
collect, whether individually or via a music rights society or designated agent (e.g. | |
Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover | |
version") and distribute, subject to the compulsory license created by 17 USC Section | |
115 of the US Copyright Act (or the equivalent in other jurisdictions). | |
f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is | |
a sound recording, Licensor waives the exclusive right to collect, whether individually | |
or via a performance-rights society (e.g. SoundExchange), royalties for the public | |
digital performance (e.g. webcast) of the Work, subject to the compulsory license created | |
by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions). | |
The above rights may be exercised in all media and formats whether now known or hereafter devised. | |
The above rights include the right to make such modifications as are technically necessary to | |
exercise the rights in other media and formats. All rights not expressly granted by Licensor are | |
hereby reserved. | |
4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by | |
the following restrictions: | |
a. You may distribute, publicly display, publicly perform, or publicly digitally perform the | |
Work only under the terms of this License, and You must include a copy of, or the Uniform | |
Resource Identifier for, this License with every copy or phonorecord of the Work You | |
distribute, publicly display, publicly perform, or publicly digitally perform. You may | |
not offer or impose any terms on the Work that alter or restrict the terms of this | |
License or the recipients' exercise of the rights granted hereunder. You may not | |
sublicense the Work. You must keep intact all notices that refer to this License and to | |
the disclaimer of warranties. You may not distribute, publicly display, publicly perform, | |
or publicly digitally perform the Work with any technological measures that control | |
access or use of the Work in a manner inconsistent with the terms of this License | |
Agreement. The above applies to the Work as incorporated in a Collective Work, but this | |
does not require the Collective Work apart from the Work itself to be made subject to the | |
terms of this License. If You create a Collective Work, upon notice from any Licensor You | |
must, to the extent practicable, remove from the Collective Work any reference to such | |
Licensor or the Original Author, as requested. If You create a Derivative Work, upon | |
notice from any Licensor You must, to the extent practicable, remove from the Derivative | |
Work any reference to such Licensor or the Original Author, as requested. | |
b. You may distribute, publicly display, publicly perform, or publicly digitally perform a | |
Derivative Work only under the terms of this License, a later version of this License | |
with the same License Elements as this License, or a Creative Commons iCommons license | |
that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.0 | |
Japan). You must include a copy of, or the Uniform Resource Identifier for, this License | |
or other license specified in the previous sentence with every copy or phonorecord of | |
each Derivative Work You distribute, publicly display, publicly perform, or publicly | |
digitally perform. You may not offer or impose any terms on the Derivative Works that | |
alter or restrict the terms of this License or the recipients' exercise of the rights | |
granted hereunder, and You must keep intact all notices that refer to this License and to | |
the disclaimer of warranties. You may not distribute, publicly display, publicly perform, | |
or publicly digitally perform the Derivative Work with any technological measures that | |
control access or use of the Work in a manner inconsistent with the terms of this License | |
Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, | |
but this does not require the Collective Work apart from the Derivative Work itself to be | |
made subject to the terms of this License. | |
c. If you distribute, publicly display, publicly perform, or publicly digitally perform the | |
Work or any Derivative Works or Collective Works, You must keep intact all copyright | |
notices for the Work and give the Original Author credit reasonable to the medium or | |
means You are utilizing by conveying the name (or pseudonym if applicable) of the | |
Original Author if supplied; the title of the Work if supplied; to the extent reasonably | |
practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be | |
associated with the Work, unless such URI does not refer to the copyright notice or | |
licensing information for the Work; and in the case of a Derivative Work, a credit | |
identifying the use of the Work in the Derivative Work (e.g., "French translation of the | |
Work by Original Author," or "Screenplay based on original Work by Original Author"). | |
Such credit may be implemented in any reasonable manner; provided, however, that in the | |
case of a Derivative Work or Collective Work, at a minimum such credit will appear where | |
any other comparable authorship credit appears and in a manner at least as prominent as | |
such other comparable authorship credit. | |
5. Representations, Warranties and Disclaimer | |
UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO | |
REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR | |
OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A | |
PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE | |
PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE | |
EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. | |
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL | |
LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE | |
OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN | |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
7. Termination | |
a. This License and the rights granted hereunder will terminate automatically upon any | |
breach by You of the terms of this License. Individuals or entities who have received | |
Derivative Works or Collective Works from You under this License, however, will not have | |
their licenses terminated provided such individuals or entities remain in full compliance | |
with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this | |
License. | |
b. Subject to the above terms and conditions, the license granted here is perpetual (for the | |
duration of the applicable copyright in the Work). Notwithstanding the above, Licensor | |
reserves the right to release the Work under different license terms or to stop | |
distributing the Work at any time; provided, however that any such election will not | |
serve to withdraw this License (or any other license that has been, or is required to be, | |
granted under the terms of this License), and this License will continue in full force | |
and effect unless terminated as stated above. | |
8. Miscellaneous | |
a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the | |
Licensor offers to the recipient a license to the Work on the same terms and conditions | |
as the license granted to You under this License. | |
b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers | |
to the recipient a license to the original Work on the same terms and conditions as the | |
license granted to You under this License. | |
c. If any provision of this License is invalid or unenforceable under applicable law, it | |
shall not affect the validity or enforceability of the remainder of the terms of this | |
License, and without further action by the parties to this agreement, such provision | |
shall be reformed to the minimum extent necessary to make such provision valid and | |
enforceable. | |
d. No term or provision of this License shall be deemed waived and no breach consented to | |
unless such waiver or consent shall be in writing and signed by the party to be charged | |
with such waiver or consent. | |
e. This License constitutes the entire agreement between the parties with respect to the | |
Work licensed here. There are no understandings, agreements or representations with | |
respect to the Work not specified here. Licensor shall not be bound by any additional | |
provisions that may appear in any communication from You. This License may not be | |
modified without the mutual written agreement of the Licensor and You. | |
Creative Commons is not a party to this License, and makes no warranty whatsoever in connection | |
with the Work. Creative Commons will not be liable to You or any party on any legal theory for | |
any damages whatsoever, including without limitation any general, special, incidental or | |
consequential damages arising in connection to this license. Notwithstanding the foregoing two | |
(2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, | |
it shall have all rights and obligations of Licensor. | |
Except for the limited purpose of indicating to the public that the Work is licensed under the | |
CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo | |
of Creative Commons without the prior written consent of Creative Commons. Any permitted use will | |
be in compliance with Creative Commons' then-current trademark usage guidelines, as may be | |
published on its website or otherwise made available upon request from time to time. | |
Creative Commons may be contacted at http://creativecommons.org/. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(){if(!this.localStorage)if(this.globalStorage)try{this.localStorage=this.globalStorage}catch(e){}else{var a=document.createElement("div");a.style.display="none";document.getElementsByTagName("head")[0].appendChild(a);if(a.addBehavior){a.addBehavior("#default#userdata");var d=this.localStorage={length:0,setItem:function(b,d){a.load("localStorage");b=c(b);a.getAttribute(b)||this.length++;a.setAttribute(b,d);a.save("localStorage")},getItem:function(b){a.load("localStorage");b=c(b);return a.getAttribute(b)}, | |
removeItem:function(b){a.load("localStorage");b=c(b);a.removeAttribute(b);a.save("localStorage");this.length--;if(0>this.length)this.length=0},clear:function(){a.load("localStorage");for(var b=0;attr=a.XMLDocument.documentElement.attributes[b++];)a.removeAttribute(attr.name);a.save("localStorage");this.length=0},key:function(b){a.load("localStorage");return a.XMLDocument.documentElement.attributes[b]}},c=function(a){return a.replace(/[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g, | |
"-")};a.load("localStorage");d.length=a.XMLDocument.documentElement.attributes.length}}})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core"%> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> | |
<title>Sparklr</title> | |
<link type="text/css" rel="stylesheet" | |
href="<c:url value="/style.css"/>" /> | |
</head> | |
<body> | |
<h1>Sparklr</h1> | |
<div id="content"> | |
<c:if test="${not empty param.authentication_error}"> | |
<h1>Woops!</h1> | |
<p class="error">Your login attempt was not successful.</p> | |
</c:if> | |
<c:if test="${not empty param.authorization_error}"> | |
<h1>Woops!</h1> | |
<p class="error">You are not permitted to access that resource.</p> | |
</c:if> | |
<h2>Login</h2> | |
<p>We've got a grand total of 2 users: marissa and paul. Go ahead | |
and log in. Marissa's password is "koala" and Paul's password is | |
"emu".</p> | |
<form id="loginForm" name="loginForm" | |
action="<c:url value="/login.do"/>" method="post"> | |
<p> | |
<label>Username: <input type='text' name='j_username' | |
value="marissa" /></label> | |
</p> | |
<p> | |
<label>Password: <input type='text' name='j_password' | |
value="koala" /></label> | |
</p> | |
<p> | |
<input name="login" value="Login" type="submit"/> | |
</p> | |
</form> | |
</div> | |
<div id="footer"> | |
Sample application for <a | |
href="http://github.com/SpringSource/spring-security-oauth" | |
target="_blank">Spring Security OAuth</a> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core"%> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<link href="<c:url value="/main.css"/>" rel="stylesheet" type="text/css" /> | |
<title>tonr</title> | |
</head> | |
<body> | |
<div id="container"> | |
<ul id="mainlinks"> | |
<li><a href="<c:url value="/index.jsp"/>">home</a></li> | |
<li><a href="<c:url value="/login.jsp"/>" class="selected">login</a></li> | |
<li><a href="<c:url value="/sparklr/photos"/>">sparklr pics</a></li> | |
<li><a href="<c:url value="/facebook/info"/>">facebook | |
friends</a></li> | |
</ul> | |
<div id="content"> | |
<c:if test="${not empty param.authentication_error}"> | |
<h1>Woops!</h1> | |
<p class="error">Your login attempt was not successful.</p> | |
</c:if> | |
<c:if test="${not empty param.authorization_error}"> | |
<h1>Woops!</h1> | |
<p class="error">You are not permitted to access that resource.</p> | |
</c:if> | |
<h1>Login</h1> | |
<p>Tonr.com has only two users: "marissa" and "sam". The password | |
for "marissa" is password is "wombat" and for "sam" is password is | |
"kangaroo".</p> | |
<form action="<c:url value="/login.do"/>" method="post"> | |
<p> | |
<label>Username: <input type='text' name='j_username' | |
value="marissa"/></label> | |
</p> | |
<p> | |
<label>Password: <input type='text' name='j_password' | |
value="wombat"/></label> | |
</p> | |
<p> | |
<input name="login" value="Login" type="submit"/> | |
</p> | |
</form> | |
<p class="footer"> | |
Courtesy <a href="http://www.openwebdesign.org">Open Web Design</a> | |
Thanks to <a href="http://www.dubaiapartments.biz/">Dubai Hotels</a> | |
</p> | |
</div> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
font-size: 10pt; | |
letter-spacing: 1px; | |
background: url( images/xbg.gif ) no-repeat; | |
font-family: Helvetica, Arial, sans-serif; | |
word-spacing: 5px; | |
color: #515151; | |
} | |
#container { | |
margin: 100px auto 0 auto; | |
width: 855px; | |
} | |
#content { | |
background-color: #f7f7f7; | |
padding: 20px 0 20px 20px; | |
} | |
#mainlinks { | |
height: 60px; | |
list-style: none; | |
margin: 0; | |
} | |
#mainlinks li { | |
float: right; | |
margin-right: 10px; | |
list-style-type: none; | |
list-style-image: none; | |
} | |
#mainlinks li a { | |
padding: 30px 0 0 0; | |
background: #d0d0d0; | |
display: block; | |
padding-top: 10px; | |
width: 100px; | |
height: 50px; | |
text-align: center; | |
color: #515151; | |
text-decoration: none; | |
} | |
#mainlinks li a:hover { | |
background: #f7f7f7; | |
} | |
#mainlinks li a.selected { | |
background: #f7f7f7; | |
color: #515151; | |
} | |
p { | |
line-height: 1.4em; | |
} | |
p.error { | |
color: red; | |
} | |
h1 { | |
padding: 25px 0 5px 0; | |
} | |
#picturelist { | |
text-align: left; | |
margin-right: auto; | |
margin-left: auto; | |
} | |
#picturelist li { | |
list-style: none; | |
margin: 42px; | |
font-weight: bold; | |
color: #777777; | |
} | |
#picturelist img { | |
display: block; | |
border: 2px solid gray; | |
} | |
#picturelist img:hover { | |
border: 2px solid #515151; | |
} | |
#picturelist a { | |
color: #777777; | |
text-decoration: none; | |
} | |
#picturelist a:hover { | |
color: #999999; | |
} | |
.footer { | |
margin-top: 100px; | |
font-size: .8em; | |
text-align:center; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Modernizr 2.5.3 (Custom Build) | MIT & BSD | |
* Build: http://www.modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load | |
*/ | |
;window.Modernizr=function(a,b,c){function D(a){j.cssText=a}function E(a,b){return D(n.join(a+";")+(b||""))}function F(a,b){return typeof a===b}function G(a,b){return!!~(""+a).indexOf(b)}function H(a,b){for(var d in a)if(j[a[d]]!==c)return b=="pfx"?a[d]:!0;return!1}function I(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:F(f,"function")?f.bind(d||b):f}return!1}function J(a,b,c){var d=a.charAt(0).toUpperCase()+a.substr(1),e=(a+" "+p.join(d+" ")+d).split(" ");return F(b,"string")||F(b,"undefined")?H(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),I(e,b,c))}function L(){e.input=function(c){for(var d=0,e=c.length;d<e;d++)u[c[d]]=c[d]in k;return u.list&&(u.list=!!b.createElement("datalist")&&!!a.HTMLDataListElement),u}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),e.inputtypes=function(a){for(var d=0,e,f,h,i=a.length;d<i;d++)k.setAttribute("type",f=a[d]),e=k.type!=="text",e&&(k.value=l,k.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(f)&&k.style.WebkitAppearance!==c?(g.appendChild(k),h=b.defaultView,e=h.getComputedStyle&&h.getComputedStyle(k,null).WebkitAppearance!=="textfield"&&k.offsetHeight!==0,g.removeChild(k)):/^(search|tel)$/.test(f)||(/^(url|email)$/.test(f)?e=k.checkValidity&&k.checkValidity()===!1:/^color$/.test(f)?(g.appendChild(k),g.offsetWidth,e=k.value!=l,g.removeChild(k)):e=k.value!=l)),t[a[d]]=!!e;return t}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var d="2.5.3",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k=b.createElement("input"),l=":)",m={}.toString,n=" -webkit- -moz- -o- -ms- ".split(" "),o="Webkit Moz O ms",p=o.split(" "),q=o.toLowerCase().split(" "),r={svg:"http://www.w3.org/2000/svg"},s={},t={},u={},v=[],w=v.slice,x,y=function(a,c,d,e){var f,i,j,k=b.createElement("div"),l=b.body,m=l?l:b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),k.appendChild(j);return f=["­","<style>",a,"</style>"].join(""),k.id=h,m.innerHTML+=f,m.appendChild(k),l||(m.style.background="",g.appendChild(m)),i=c(k,a),l?k.parentNode.removeChild(k):m.parentNode.removeChild(m),!!i},z=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return y("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},A=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=F(e[d],"function"),F(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),B={}.hasOwnProperty,C;!F(B,"undefined")&&!F(B.call,"undefined")?C=function(a,b){return B.call(a,b)}:C=function(a,b){return b in a&&F(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e});var K=function(c,d){var f=c.join(""),g=d.length;y(f,function(c,d){var f=b.styleSheets[b.styleSheets.length-1],h=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"",i=c.childNodes,j={};while(g--)j[i[g].id]=i[g];e.touch="ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch||(j.touch&&j.touch.offsetTop)===9,e.csstransforms3d=(j.csstransforms3d&&j.csstransforms3d.offsetLeft)===9&&j.csstransforms3d.offsetHeight===3,e.generatedcontent=(j.generatedcontent&&j.generatedcontent.offsetHeight)>=1,e.fontface=/src/i.test(h)&&h.indexOf(d.split(" ")[0])===0},g,d)}(['@font-face {font-family:"font";src:url("https://")}',["@media (",n.join("touch-enabled),("),h,")","{#touch{top:9px;position:absolute}}"].join(""),["@media (",n.join("transform-3d),("),h,")","{#csstransforms3d{left:9px;position:absolute;height:3px;}}"].join(""),['#generatedcontent:after{content:"',l,'";visibility:hidden}'].join("")],["fontface","touch","csstransforms3d","generatedcontent"]);s.flexbox=function(){return J("flexOrder")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!F(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){try{var d=b.createElement("canvas"),e;e=!(!a.WebGLRenderingContext||!d.getContext("experimental-webgl")&&!d.getContext("webgl")),d=c}catch(f){e=!1}return e},s.touch=function(){return e.touch},s.geolocation=function(){return!!navigator.geolocation},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!J("indexedDB",a)},s.hashchange=function(){return A("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){for(var b=-1,c=p.length;++b<c;)if(a[p[b]+"WebSocket"])return!0;return"WebSocket"in a},s.rgba=function(){return D("background-color:rgba(150,255,150,.5)"),G(j.backgroundColor,"rgba")},s.hsla=function(){return D("background-color:hsla(120,40%,100%,.5)"),G(j.backgroundColor,"rgba")||G(j.backgroundColor,"hsla")},s.multiplebgs=function(){return D("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return J("backgroundSize")},s.borderimage=function(){return J("borderImage")},s.borderradius=function(){return J("borderRadius")},s.boxshadow=function(){return J("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return E("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return J("animationName")},s.csscolumns=function(){return J("columnCount")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return D((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),G(j.backgroundImage,"gradient")},s.cssreflections=function(){return J("boxReflect")},s.csstransforms=function(){return!!J("transform")},s.csstransforms3d=function(){var a=!!J("perspective");return a&&"webkitPerspective"in g.style&&(a=e.csstransforms3d),a},s.csstransitions=function(){return J("transition")},s.fontface=function(){return e.fontface},s.generatedcontent=function(){return e.generatedcontent},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect},s.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="<svg/>",(a.firstChild&&a.firstChild.namespaceURI)==r.svg},s.smil=function(){return!!b.createElementNS&&/SVGAnimate/.test(m.call(b.createElementNS(r.svg,"animate")))},s.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(m.call(b.createElementNS(r.svg,"clipPath")))};for(var M in s)C(s,M)&&(x=M.toLowerCase(),e[x]=s[M](),v.push((e[x]?"":"no-")+x));return e.input||L(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)C(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,g.className+=" "+(b?"":"no-")+a,e[a]=b}return e},D(""),i=k=null,function(a,b){function g(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function h(){var a=k.elements;return typeof a=="string"?a.split(" "):a}function i(a){var b={},c=a.createElement,e=a.createDocumentFragment,f=e();a.createElement=function(a){var e=(b[a]||(b[a]=c(a))).cloneNode();return k.shivMethods&&e.canHaveChildren&&!d.test(a)?f.appendChild(e):e},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+h().join().replace(/\w+/g,function(a){return b[a]=c(a),f.createElement(a),'c("'+a+'")'})+");return n}")(k,f)}function j(a){var b;return a.documentShived?a:(k.shivCSS&&!e&&(b=!!g(a,"article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio{display:none}canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}mark{background:#FF0;color:#000}")),f||(b=!i(a)),b&&(a.documentShived=b),a)}var c=a.html5||{},d=/^<|^(?:button|form|map|select|textarea)$/i,e,f;(function(){var a=b.createElement("a");a.innerHTML="<xyz></xyz>",e="hidden"in a,f=a.childNodes.length==1||function(){try{b.createElement("a")}catch(a){return!0}var c=b.createDocumentFragment();return typeof c.cloneNode=="undefined"||typeof c.createDocumentFragment=="undefined"||typeof c.createElement=="undefined"}()})();var k={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:j};a.html5=k,j(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.mq=z,e.hasEvent=A,e.testProp=function(a){return H([a])},e.testAllProps=J,e.testStyles=y,e.prefixed=function(a,b,c){return b?J(a,b,c):J(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return o.call(a)=="[object Function]"}function e(a){return typeof a=="string"}function f(){}function g(a){return!a||a=="loaded"||a=="complete"||a=="uninitialized"}function h(){var a=p.shift();q=1,a?a.t?m(function(){(a.t=="c"?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){a!="img"&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l={},o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};y[c]===1&&(r=1,y[c]=[],l=b.createElement(a)),a=="object"?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),a!="img"&&(r||y[c]===2?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i(b=="c"?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),p.length==1&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&o.call(a.opera)=="[object Opera]",l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return o.call(a)=="[object Array]"},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,i){var j=b(a),l=j.autoCallback;j.url.split(".").pop().split("?").shift(),j.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]||h),j.instead?j.instead(a,e,f,g,i):(y[j.url]?j.noexec=!0:y[j.url]=1,f.load(j.url,j.forceCSS||!j.forceJS&&"css"==j.url.split(".").pop().split("?").shift()?"c":c,j.noexec,j.attrs,j.timeout),(d(e)||d(l))&&f.load(function(){k(),e&&e(j.origUrl,i,g),l&&l(j.origUrl,i,g),y[j.url]=2})))}function i(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var j,l,m=this.yepnope.loader;if(e(a))g(a,0,m,0);else if(w(a))for(j=0;j<a.length;j++)l=a[j],e(l)?g(l,0,m,0):w(l)?B(l):Object(l)===l&&i(l,m);else Object(a)===a&&i(a,m)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,b.readyState==null&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}}(this,document),Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0))}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ page import="java.io.PrintWriter" %> | |
<%@ page import="java.io.StringWriter" %> | |
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %> | |
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<link href="<c:url value="/main.css"/>" rel="stylesheet" type="text/css"/> | |
<title>tonr</title> | |
</head> | |
<body> | |
<div id="container"> | |
<ul id="mainlinks"> | |
<li><a href="<c:url value="/index.jsp"/>">home</a></li> | |
<authz:authorize ifNotGranted="ROLE_USER"> | |
<li><a href="<c:url value="/login.jsp"/>">login</a></li> | |
</authz:authorize> | |
<li><a href="<c:url value="/sparklr/photos"/>">sparklr pics</a></li> | |
<li><a href="<c:url value="/facebook/info"/>">facebook friends</a></li> | |
</ul> | |
<div id="content"> | |
<p class="footer">Courtesy <a href="http://www.openwebdesign.org">Open Web Design</a> Thanks to <a | |
href="http://www.dubaiapartments.biz/">Dubai Hotels</a></p> | |
</div> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.sparklr.mvc; | |
import java.io.ByteArrayOutputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.security.Principal; | |
import java.util.Collection; | |
import java.util.Iterator; | |
import org.springframework.http.HttpHeaders; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.http.ResponseEntity; | |
import org.springframework.security.access.prepost.PreAuthorize; | |
import org.springframework.security.oauth.examples.sparklr.PhotoInfo; | |
import org.springframework.security.oauth.examples.sparklr.PhotoService; | |
import org.springframework.stereotype.Controller; | |
import org.springframework.web.bind.annotation.PathVariable; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.RequestParam; | |
import org.springframework.web.bind.annotation.ResponseBody; | |
/** | |
* @author Ryan Heaton | |
* @author Dave Syer | |
*/ | |
@Controller | |
public class PhotoController { | |
private PhotoService photoService; | |
@RequestMapping("/photos/{photoId}") | |
public ResponseEntity<byte[]> getPhoto(@PathVariable("photoId") String id) throws IOException { | |
InputStream photo = getPhotoService().loadPhoto(id); | |
if (photo == null) { | |
return new ResponseEntity<byte[]>(HttpStatus.NOT_FOUND); | |
} else { | |
ByteArrayOutputStream out = new ByteArrayOutputStream(); | |
byte[] buffer = new byte[1024]; | |
int len = photo.read(buffer); | |
while (len >= 0) { | |
out.write(buffer, 0, len); | |
len = photo.read(buffer); | |
} | |
HttpHeaders headers = new HttpHeaders(); | |
headers.set("Content-Type", "image/jpeg"); | |
return new ResponseEntity<byte[]>(out.toByteArray(), headers, HttpStatus.OK); | |
} | |
} | |
@RequestMapping(value = "/photos", params = "format=json") | |
public ResponseEntity<String> getJsonPhotos(@RequestParam(value = "callback", required = false) String callback, Principal principal) { | |
Collection<PhotoInfo> photos = getPhotoService().getPhotosForCurrentUser(principal.getName()); | |
StringBuilder out = new StringBuilder(); | |
if (callback != null) { | |
out.append(callback).append("( "); | |
} | |
out.append("{ \"photos\" : [ "); | |
Iterator<PhotoInfo> photosIt = photos.iterator(); | |
while (photosIt.hasNext()) { | |
PhotoInfo photo = photosIt.next(); | |
out.append(String.format("{ \"id\" : \"%s\" , \"name\" : \"%s\" }", photo.getId(), photo.getName())); | |
if (photosIt.hasNext()) { | |
out.append(" , "); | |
} | |
} | |
out.append("] }"); | |
if (callback != null) { | |
out.append(" )"); | |
} | |
HttpHeaders headers = new HttpHeaders(); | |
headers.set("Content-Type", "application/json"); | |
return new ResponseEntity<String>(out.toString(), headers, HttpStatus.OK); | |
} | |
@RequestMapping(value = "/photos", params = "format=xml") | |
public ResponseEntity<String> getXmlPhotos(Principal principal) { | |
Collection<PhotoInfo> photos = photoService.getPhotosForCurrentUser(principal.getName()); | |
StringBuilder out = new StringBuilder(); | |
out.append("<photos>"); | |
for (PhotoInfo photo : photos) { | |
out.append(String.format("<photo id=\"%s\" name=\"%s\"/>", photo.getId(), photo.getName())); | |
} | |
out.append("</photos>"); | |
HttpHeaders headers = new HttpHeaders(); | |
headers.set("Content-Type", "application/xml"); | |
return new ResponseEntity<String>(out.toString(), headers, HttpStatus.OK); | |
} | |
@RequestMapping("/photos/trusted/message") | |
@PreAuthorize("oauthClientHasRole('ROLE_CLIENT')") | |
@ResponseBody | |
public String getTrustedClientMessage() { | |
return "Hello, Trusted Client"; | |
} | |
@RequestMapping("/photos/user/message") | |
@ResponseBody | |
public String getTrustedUserMessage(Principal principal) { | |
return "Hello, Trusted User" + (principal!=null ? " " + principal.getName() : ""); | |
} | |
public PhotoService getPhotoService() { | |
return photoService; | |
} | |
public void setPhotoService(PhotoService photoService) { | |
this.photoService = photoService; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.sparklr; | |
/** | |
* Photo information. | |
* | |
* @author Ryan Heaton | |
*/ | |
public class PhotoInfo { | |
private String id; | |
private String resourceURL; | |
private String name; | |
private String userId; | |
/** | |
* Id of the photo. | |
* | |
* @return Id of the photo. | |
*/ | |
public String getId() { | |
return id; | |
} | |
/** | |
* Id of the photo. | |
* | |
* @param id Id of the photo. | |
*/ | |
public void setId(String id) { | |
this.id = id; | |
} | |
/** | |
* The resource URL. | |
* | |
* @return The resource URL. | |
*/ | |
public String getResourceURL() { | |
return resourceURL; | |
} | |
/** | |
* The resource URL. | |
* | |
* @param resourceURL The resource URL | |
*/ | |
public void setResourceURL(String resourceURL) { | |
this.resourceURL = resourceURL; | |
} | |
/** | |
* Name of the photo. | |
* | |
* @return Name of the photo. | |
*/ | |
public String getName() { | |
return name; | |
} | |
/** | |
* Name of the photo. | |
* | |
* @param name Name of the photo. | |
*/ | |
public void setName(String name) { | |
this.name = name; | |
} | |
/** | |
* The id of the user to whom the photo belongs. | |
* | |
* @return The id of the user to whom the photo belongs. | |
*/ | |
public String getUserId() { | |
return userId; | |
} | |
/** | |
* The id of the user to whom the photo belongs. | |
* | |
* @param userId The id of the user to whom the photo belongs. | |
*/ | |
public void setUserId(String userId) { | |
this.userId = userId; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.sparklr; | |
import java.util.Collection; | |
import java.io.InputStream; | |
/** | |
* Service for retrieving photos. | |
* | |
* @author Ryan Heaton | |
*/ | |
public interface PhotoService { | |
/** | |
* Load the photos for the current user. | |
* | |
* @return The photos for the current user. | |
*/ | |
Collection<PhotoInfo> getPhotosForCurrentUser(String username); | |
/** | |
* Load a photo by id. | |
* | |
* @param id The id of the photo. | |
* @return The photo that was read. | |
*/ | |
InputStream loadPhoto(String id); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.sparklr.impl; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.URL; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.List; | |
import org.springframework.security.core.Authentication; | |
import org.springframework.security.core.context.SecurityContextHolder; | |
import org.springframework.security.core.userdetails.UserDetails; | |
import org.springframework.security.oauth.examples.sparklr.PhotoInfo; | |
import org.springframework.security.oauth.examples.sparklr.PhotoService; | |
/** | |
* Basic implementation for the photo service. | |
* | |
* @author Ryan Heaton | |
*/ | |
public class PhotoServiceImpl implements PhotoService { | |
private List<PhotoInfo> photos; | |
public Collection<PhotoInfo> getPhotosForCurrentUser(String username) { | |
ArrayList<PhotoInfo> infos = new ArrayList<PhotoInfo>(); | |
for (PhotoInfo info : getPhotos()) { | |
if (username.equals(info.getUserId())) { | |
infos.add(info); | |
} | |
} | |
return infos; | |
} | |
public InputStream loadPhoto(String id) { | |
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | |
if (authentication.getPrincipal() instanceof UserDetails) { | |
UserDetails details = (UserDetails) authentication.getPrincipal(); | |
String username = details.getUsername(); | |
for (PhotoInfo photoInfo : getPhotos()) { | |
if (id.equals(photoInfo.getId()) && username.equals(photoInfo.getUserId())) { | |
URL resourceURL = getClass().getResource(photoInfo.getResourceURL()); | |
if (resourceURL != null) { | |
try { | |
return resourceURL.openStream(); | |
} catch (IOException e) { | |
// fall through... | |
} | |
} | |
} | |
} | |
} | |
return null; | |
} | |
public List<PhotoInfo> getPhotos() { | |
return photos; | |
} | |
public void setPhotos(List<PhotoInfo> photos) { | |
this.photos = photos; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<parent> | |
<groupId>org.springframework.security.oauth</groupId> | |
<artifactId>spring-security-oauth-parent</artifactId> | |
<version>1.0.0.BUILD-SNAPSHOT</version> | |
<relativePath>../../..</relativePath> | |
</parent> | |
<artifactId>sparklr2</artifactId> | |
<packaging>war</packaging> | |
<name>OAuth for Spring Security - Sparklr2 (OAuth 2 Provider Example)</name> | |
<properties> | |
<m2eclipse.wtp.contextRoot>/sparklr2</m2eclipse.wtp.contextRoot> | |
</properties> | |
<profiles> | |
<profile> | |
<id>integration</id> | |
<build> | |
<plugins> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-surefire-plugin</artifactId> | |
<version>2.7.1</version> | |
<executions> | |
<execution> | |
<id>surefire-it</id> | |
<phase>integration-test</phase> | |
<goals> | |
<goal>test</goal> | |
</goals> | |
<configuration> | |
<skip>${skipTests}</skip> | |
<testFailureIgnore>true</testFailureIgnore> | |
</configuration> | |
</execution> | |
</executions> | |
</plugin> | |
<plugin> | |
<groupId>org.codehaus.mojo</groupId> | |
<artifactId>tomcat-maven-plugin</artifactId> | |
<version>1.1</version> | |
<executions> | |
<execution> | |
<id>start-tomcat</id> | |
<phase>pre-integration-test</phase> | |
<goals> | |
<goal>run</goal> | |
</goals> | |
<configuration> | |
<fork>true</fork> | |
</configuration> | |
</execution> | |
</executions> | |
</plugin> | |
</plugins> | |
</build> | |
</profile> | |
</profiles> | |
<build> | |
<plugins> | |
<plugin> | |
<!--skip deploy (this is just a test module) --> | |
<artifactId>maven-deploy-plugin</artifactId> | |
<version>2.6</version> | |
<configuration> | |
<skip>true</skip> | |
</configuration> | |
</plugin> | |
</plugins> | |
<pluginManagement> | |
<plugins> | |
<plugin> | |
<groupId>org.codehaus.mojo</groupId> | |
<artifactId>tomcat-maven-plugin</artifactId> | |
<configuration> | |
<addContextWarDependencies>true</addContextWarDependencies> | |
</configuration> | |
</plugin> | |
</plugins> | |
</pluginManagement> | |
</build> | |
<repositories> | |
<repository> | |
<id>spring-milestone</id> | |
<name>Spring Framework Milestone Repository</name> | |
<url>http://maven.springframework.org/milestone</url> | |
</repository> | |
<repository> | |
<id>spring-release</id> | |
<name>Spring Framework Release Repository</name> | |
<url>http://maven.springframework.org/release</url> | |
</repository> | |
<repository> | |
<id>java.net</id> | |
<url>http://download.java.net/maven/2</url> | |
</repository> | |
</repositories> | |
<dependencies> | |
<dependency> | |
<groupId>${project.groupId}</groupId> | |
<artifactId>spring-security-oauth2</artifactId> | |
<version>${project.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-web</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-jdbc</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-webmvc</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-test</artifactId> | |
<version>${spring.version}</version> | |
<scope>test</scope> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-tx</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.security</groupId> | |
<artifactId>spring-security-taglibs</artifactId> | |
<version>${spring.security.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.codehaus.jackson</groupId> | |
<artifactId>jackson-mapper-asl</artifactId> | |
<version>1.8.3</version> | |
</dependency> | |
<dependency> | |
<groupId>cglib</groupId> | |
<artifactId>cglib</artifactId> | |
<version>2.2.2</version> | |
</dependency> | |
<dependency> | |
<groupId>javax.servlet</groupId> | |
<artifactId>servlet-api</artifactId> | |
<version>2.3</version> | |
<scope>provided</scope> | |
</dependency> | |
<dependency> | |
<groupId>javax.servlet</groupId> | |
<artifactId>jstl</artifactId> | |
<version>1.2</version> | |
</dependency> | |
<dependency> | |
<groupId>junit</groupId> | |
<artifactId>junit</artifactId> | |
<version>4.8.2</version> | |
<scope>test</scope> | |
</dependency> | |
<dependency> | |
<groupId>org.apache.httpcomponents</groupId> | |
<artifactId>httpclient</artifactId> | |
<version>4.1.2</version> | |
<scope>test</scope> | |
</dependency> | |
</dependencies> | |
<!-- <profiles> <profile> <id>gae</id> <dependencies> <dependency> <groupId>com.google.appengine</groupId> <artifactId>appengine-api-1.0-sdk</artifactId> | |
<version>1.2.2</version> </dependency> </dependencies> </profile> </profiles> --> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<parent> | |
<groupId>org.springframework.security.oauth</groupId> | |
<artifactId>spring-security-oauth-parent</artifactId> | |
<version>1.0.0.BUILD-SNAPSHOT</version> | |
<relativePath>../../..</relativePath> | |
</parent> | |
<artifactId>tonr2</artifactId> | |
<packaging>war</packaging> | |
<name>OAuth for Spring Security - Tonr2 (OAuth 2 Client Example)</name> | |
<properties> | |
<m2eclipse.wtp.contextRoot>/tonr2</m2eclipse.wtp.contextRoot> | |
</properties> | |
<profiles> | |
<profile> | |
<id>integration</id> | |
<build> | |
<plugins> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-surefire-plugin</artifactId> | |
<version>2.7.1</version> | |
<executions> | |
<execution> | |
<id>surefire-it</id> | |
<phase>integration-test</phase> | |
<goals> | |
<goal>test</goal> | |
</goals> | |
<configuration> | |
<skip>${skipTests}</skip> | |
<testFailureIgnore>true</testFailureIgnore> | |
</configuration> | |
</execution> | |
</executions> | |
</plugin> | |
<plugin> | |
<groupId>org.codehaus.mojo</groupId> | |
<artifactId>tomcat-maven-plugin</artifactId> | |
<version>1.1</version> | |
<executions> | |
<execution> | |
<id>start-tomcat</id> | |
<phase>pre-integration-test</phase> | |
<goals> | |
<goal>run</goal> | |
</goals> | |
<configuration> | |
<fork>true</fork> | |
</configuration> | |
</execution> | |
</executions> | |
</plugin> | |
</plugins> | |
</build> | |
</profile> | |
</profiles> | |
<build> | |
<plugins> | |
<plugin> | |
<!--skip deploy (this is just a test module) --> | |
<artifactId>maven-deploy-plugin</artifactId> | |
<version>2.6</version> | |
<configuration> | |
<skip>true</skip> | |
</configuration> | |
</plugin> | |
</plugins> | |
<pluginManagement> | |
<plugins> | |
<plugin> | |
<groupId>org.codehaus.mojo</groupId> | |
<artifactId>tomcat-maven-plugin</artifactId> | |
<configuration> | |
<addContextWarDependencies>true</addContextWarDependencies> | |
</configuration> | |
</plugin> | |
</plugins> | |
</pluginManagement> | |
</build> | |
<repositories> | |
<repository> | |
<id>spring-milestone</id> | |
<name>Spring Framework Milestone Repository</name> | |
<url>http://maven.springframework.org/milestone</url> | |
</repository> | |
<repository> | |
<id>spring-release</id> | |
<name>Spring Framework Release Repository</name> | |
<url>http://maven.springframework.org/release</url> | |
</repository> | |
</repositories> | |
<dependencies> | |
<dependency> | |
<groupId>${pom.groupId}</groupId> | |
<artifactId>sparklr2</artifactId> | |
<version>${pom.version}</version> | |
<type>war</type> | |
<scope>tomcat</scope> | |
</dependency> | |
<dependency> | |
<groupId>${project.groupId}</groupId> | |
<artifactId>spring-security-oauth2</artifactId> | |
<version>${project.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.security</groupId> | |
<artifactId>spring-security-taglibs</artifactId> | |
<version>${spring.security.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-webmvc</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-web</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-jdbc</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-context</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-aop</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-expression</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-tx</artifactId> | |
<version>${spring.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.codehaus.jackson</groupId> | |
<artifactId>jackson-mapper-asl</artifactId> | |
<version>1.5.5</version> | |
</dependency> | |
<dependency> | |
<groupId>javax.servlet</groupId> | |
<artifactId>servlet-api</artifactId> | |
<version>2.3</version> | |
<scope>provided</scope> | |
</dependency> | |
<dependency> | |
<groupId>javax.servlet</groupId> | |
<artifactId>jstl</artifactId> | |
<version>1.2</version> | |
</dependency> | |
<dependency> | |
<groupId>junit</groupId> | |
<artifactId>junit</artifactId> | |
<version>4.8.2</version> | |
<scope>test</scope> | |
</dependency> | |
</dependencies> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth2.provider; | |
import java.io.IOException; | |
import java.net.HttpURLConnection; | |
import java.net.URI; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.LinkedHashMap; | |
import java.util.Map; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import org.apache.http.client.HttpClient; | |
import org.apache.http.client.params.ClientPNames; | |
import org.apache.http.client.params.CookiePolicy; | |
import org.junit.Assume; | |
import org.junit.internal.AssumptionViolatedException; | |
import org.junit.rules.MethodRule; | |
import org.junit.runners.model.FrameworkMethod; | |
import org.junit.runners.model.Statement; | |
import org.springframework.http.HttpEntity; | |
import org.springframework.http.HttpHeaders; | |
import org.springframework.http.HttpMethod; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.http.MediaType; | |
import org.springframework.http.ResponseEntity; | |
import org.springframework.http.client.ClientHttpResponse; | |
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; | |
import org.springframework.security.oauth2.client.test.RestTemplateHolder; | |
import org.springframework.util.LinkedMultiValueMap; | |
import org.springframework.util.MultiValueMap; | |
import org.springframework.web.client.ResponseErrorHandler; | |
import org.springframework.web.client.RestClientException; | |
import org.springframework.web.client.RestOperations; | |
import org.springframework.web.client.RestTemplate; | |
import org.springframework.web.util.UriTemplate; | |
/** | |
* <p> | |
* A rule that prevents integration tests from failing if the server application is not running or not accessible. If | |
* the server is not running in the background all the tests here will simply be skipped because of a violated | |
* assumption (showing as successful). Usage: | |
* </p> | |
* | |
* <pre> | |
* @Rule public static BrokerRunning brokerIsRunning = BrokerRunning.isRunning(); | |
* | |
* @Test public void testSendAndReceive() throws Exception { // ... test using RabbitTemplate etc. } | |
* </pre> | |
* <p> | |
* The rule can be declared as static so that it only has to check once for all tests in the enclosing test case, but | |
* there isn't a lot of overhead in making it non-static. | |
* </p> | |
* | |
* @see Assume | |
* @see AssumptionViolatedException | |
* | |
* @author Dave Syer | |
* | |
*/ | |
public class ServerRunning implements MethodRule, RestTemplateHolder { | |
private static Log logger = LogFactory.getLog(ServerRunning.class); | |
// Static so that we only test once on failure: speeds up test suite | |
private static Map<Integer, Boolean> serverOnline = new HashMap<Integer, Boolean>(); | |
// Static so that we only test once on failure | |
private static Map<Integer, Boolean> serverOffline = new HashMap<Integer, Boolean>(); | |
private final boolean assumeOnline; | |
private static int DEFAULT_PORT = 8080; | |
private static String DEFAULT_HOST = "localhost"; | |
private int port; | |
private String hostName = DEFAULT_HOST; | |
private RestOperations client; | |
/** | |
* @return a new rule that assumes an existing running broker | |
*/ | |
public static ServerRunning isRunning() { | |
return new ServerRunning(true); | |
} | |
/** | |
* @return a new rule that assumes there is no existing broker | |
*/ | |
public static ServerRunning isNotRunning() { | |
return new ServerRunning(false); | |
} | |
private ServerRunning(boolean assumeOnline) { | |
this.assumeOnline = assumeOnline; | |
setPort(DEFAULT_PORT); | |
} | |
/** | |
* @param port the port to set | |
*/ | |
public void setPort(int port) { | |
this.port = port; | |
if (!serverOffline.containsKey(port)) { | |
serverOffline.put(port, true); | |
} | |
if (!serverOnline.containsKey(port)) { | |
serverOnline.put(port, true); | |
} | |
client = createRestTemplate(); | |
} | |
/** | |
* @param hostName the hostName to set | |
*/ | |
public void setHostName(String hostName) { | |
this.hostName = hostName; | |
} | |
public Statement apply(final Statement base, FrameworkMethod method, Object target) { | |
// Check at the beginning, so this can be used as a static field | |
if (assumeOnline) { | |
Assume.assumeTrue(serverOnline.get(port)); | |
} | |
else { | |
Assume.assumeTrue(serverOffline.get(port)); | |
} | |
RestTemplate client = new RestTemplate(); | |
boolean followRedirects = HttpURLConnection.getFollowRedirects(); | |
HttpURLConnection.setFollowRedirects(false); | |
boolean online = false; | |
try { | |
client.getForEntity(new UriTemplate(getUrl("/sparklr2/login.jsp")).toString(), String.class); | |
online = true; | |
logger.info("Basic connectivity test passed"); | |
} | |
catch (RestClientException e) { | |
logger.warn(String.format( | |
"Not executing tests because basic connectivity test failed for hostName=%s, port=%d", hostName, | |
port), e); | |
if (assumeOnline) { | |
Assume.assumeNoException(e); | |
} | |
} | |
finally { | |
HttpURLConnection.setFollowRedirects(followRedirects); | |
if (online) { | |
serverOffline.put(port, false); | |
if (!assumeOnline) { | |
Assume.assumeTrue(serverOffline.get(port)); | |
} | |
} | |
else { | |
serverOnline.put(port, false); | |
} | |
} | |
final RestOperations savedClient = getRestTemplate(); | |
postForStatus(savedClient, "/sparklr2/oauth/uncache_approvals", | |
new LinkedMultiValueMap<String, String>()); | |
return new Statement() { | |
@Override | |
public void evaluate() throws Throwable { | |
try { | |
base.evaluate(); | |
} | |
finally { | |
postForStatus(savedClient, "/sparklr2/oauth/cache_approvals", | |
new LinkedMultiValueMap<String, String>()); | |
} | |
} | |
}; | |
} | |
public String getBaseUrl() { | |
return "http://" + hostName + ":" + port; | |
} | |
public String getUrl(String path) { | |
if (path.startsWith("http")) { | |
return path; | |
} | |
if (!path.startsWith("/")) { | |
path = "/" + path; | |
} | |
return "http://" + hostName + ":" + port + path; | |
} | |
public ResponseEntity<String> postForString(String path, MultiValueMap<String, String> formData) { | |
HttpHeaders headers = new HttpHeaders(); | |
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); | |
return client.exchange(getUrl(path), HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData, | |
headers), String.class); | |
} | |
public ResponseEntity<String> postForString(String path, HttpHeaders headers, MultiValueMap<String, String> formData) { | |
HttpHeaders actualHeaders = new HttpHeaders(); | |
actualHeaders.putAll(headers); | |
actualHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_FORM_URLENCODED)); | |
return client.exchange(getUrl(path), HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData, | |
actualHeaders), String.class); | |
} | |
@SuppressWarnings("rawtypes") | |
public ResponseEntity<Map> postForMap(String path, MultiValueMap<String, String> formData) { | |
return postForMap(path, new HttpHeaders(), formData); | |
} | |
@SuppressWarnings("rawtypes") | |
public ResponseEntity<Map> postForMap(String path, HttpHeaders headers, MultiValueMap<String, String> formData) { | |
if (headers.getContentType() == null) { | |
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); | |
} | |
return client.exchange(getUrl(path), HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData, | |
headers), Map.class); | |
} | |
public ResponseEntity<Void> postForStatus(String path, MultiValueMap<String, String> formData) { | |
return postForStatus(this.client, path, formData); | |
} | |
public ResponseEntity<Void> postForStatus(String path, HttpHeaders headers, MultiValueMap<String, String> formData) { | |
return postForStatus(this.client, path, headers, formData); | |
} | |
private ResponseEntity<Void> postForStatus(RestOperations client, String path, | |
MultiValueMap<String, String> formData) { | |
return postForStatus(client, path, new HttpHeaders(), formData); | |
} | |
private ResponseEntity<Void> postForStatus(RestOperations client, String path, HttpHeaders headers, | |
MultiValueMap<String, String> formData) { | |
HttpHeaders actualHeaders = new HttpHeaders(); | |
actualHeaders.putAll(headers); | |
actualHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); | |
return client.exchange(getUrl(path), HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData, | |
actualHeaders), null); | |
} | |
public ResponseEntity<Void> postForRedirect(String path, HttpHeaders headers, MultiValueMap<String, String> params) { | |
ResponseEntity<Void> exchange = postForStatus(path, headers, params); | |
if (exchange.getStatusCode() != HttpStatus.FOUND) { | |
throw new IllegalStateException("Expected 302 but server returned status code " + exchange.getStatusCode()); | |
} | |
if (exchange.getHeaders().containsKey("Set-Cookie")) { | |
String cookie = exchange.getHeaders().getFirst("Set-Cookie"); | |
headers.set("Cookie", cookie); | |
} | |
String location = exchange.getHeaders().getLocation().toString(); | |
return client.exchange(location, HttpMethod.GET, new HttpEntity<Void>(null, headers), null); | |
} | |
public ResponseEntity<String> getForString(String path) { | |
return getForString(path, new HttpHeaders()); | |
} | |
public ResponseEntity<String> getForString(String path, final HttpHeaders headers) { | |
return client.exchange(getUrl(path), HttpMethod.GET, new HttpEntity<Void>((Void) null, headers), String.class); | |
} | |
public ResponseEntity<String> getForString(String path, final HttpHeaders headers, Map<String, String> uriVariables) { | |
return client.exchange(getUrl(path), HttpMethod.GET, new HttpEntity<Void>((Void) null, headers), String.class, | |
uriVariables); | |
} | |
public ResponseEntity<Void> getForResponse(String path, final HttpHeaders headers, Map<String, String> uriVariables) { | |
HttpEntity<Void> request = new HttpEntity<Void>(null, headers); | |
return client.exchange(getUrl(path), HttpMethod.GET, request, null, uriVariables); | |
} | |
public ResponseEntity<Void> getForResponse(String path, HttpHeaders headers) { | |
return getForResponse(path, headers, Collections.<String, String> emptyMap()); | |
} | |
public HttpStatus getStatusCode(String path, final HttpHeaders headers) { | |
ResponseEntity<Void> response = getForResponse(path, headers); | |
return response.getStatusCode(); | |
} | |
public HttpStatus getStatusCode(String path) { | |
return getStatusCode(getUrl(path), null); | |
} | |
public void setRestTemplate(RestOperations restTemplate) { | |
client = restTemplate; | |
} | |
public RestOperations getRestTemplate() { | |
return client; | |
} | |
public RestOperations createRestTemplate() { | |
RestTemplate client = new RestTemplate(); | |
client.setRequestFactory(new HttpComponentsClientHttpRequestFactory() { | |
@Override | |
public HttpClient getHttpClient() { | |
HttpClient client = super.getHttpClient(); | |
client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, false); | |
client.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES); | |
return client; | |
} | |
}); | |
client.setErrorHandler(new ResponseErrorHandler() { | |
// Pass errors through in response entity for status code analysis | |
public boolean hasError(ClientHttpResponse response) throws IOException { | |
return false; | |
} | |
public void handleError(ClientHttpResponse response) throws IOException { | |
} | |
}); | |
return client; | |
} | |
public UriBuilder buildUri(String url) { | |
return UriBuilder.fromUri(url.startsWith("http:") ? url : getUrl(url)); | |
} | |
public static class UriBuilder { | |
private final String url; | |
private Map<String, String> params = new LinkedHashMap<String, String>(); | |
public UriBuilder(String url) { | |
this.url = url; | |
} | |
public static UriBuilder fromUri(String url) { | |
return new UriBuilder(url); | |
} | |
public UriBuilder queryParam(String key, String value) { | |
params.put(key, value); | |
return this; | |
} | |
public String pattern() { | |
StringBuilder builder = new StringBuilder(); | |
// try { | |
builder.append(url.replace(" ", "+")); | |
if (!params.isEmpty()) { | |
builder.append("?"); | |
boolean first = true; | |
for (String key : params.keySet()) { | |
if (!first) { | |
builder.append("&"); | |
} | |
else { | |
first = false; | |
} | |
String value = params.get(key); | |
if (value.contains("=")) { | |
value = value.replace("=", "%3D"); | |
} | |
builder.append(key + "={" + key + "}"); | |
} | |
} | |
return builder.toString(); | |
} | |
public Map<String, String> params() { | |
return params; | |
} | |
public URI build() { | |
return new UriTemplate(pattern()).expand(params); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.tonr; | |
import java.io.IOException; | |
import java.io.UnsupportedEncodingException; | |
import java.net.HttpURLConnection; | |
import java.net.URI; | |
import java.net.URISyntaxException; | |
import java.net.URLDecoder; | |
import java.util.Arrays; | |
import java.util.HashMap; | |
import java.util.Map; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import org.junit.Assume; | |
import org.junit.internal.AssumptionViolatedException; | |
import org.junit.rules.MethodRule; | |
import org.junit.runners.model.FrameworkMethod; | |
import org.junit.runners.model.Statement; | |
import org.springframework.http.HttpEntity; | |
import org.springframework.http.HttpHeaders; | |
import org.springframework.http.HttpMethod; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.http.MediaType; | |
import org.springframework.http.ResponseEntity; | |
import org.springframework.http.client.ClientHttpRequest; | |
import org.springframework.http.client.ClientHttpResponse; | |
import org.springframework.http.client.SimpleClientHttpRequestFactory; | |
import org.springframework.util.FileCopyUtils; | |
import org.springframework.util.LinkedMultiValueMap; | |
import org.springframework.util.MultiValueMap; | |
import org.springframework.web.client.RequestCallback; | |
import org.springframework.web.client.ResponseErrorHandler; | |
import org.springframework.web.client.ResponseExtractor; | |
import org.springframework.web.client.RestClientException; | |
import org.springframework.web.client.RestTemplate; | |
import org.springframework.web.util.UriTemplate; | |
import org.springframework.web.util.UriUtils; | |
/** | |
* <p> | |
* A rule that prevents integration tests from failing if the server application is not running or not accessible. If | |
* the server is not running in the background all the tests here will simply be skipped because of a violated | |
* assumption (showing as successful). Usage: | |
* </p> | |
* | |
* <pre> | |
* @Rule public static BrokerRunning brokerIsRunning = BrokerRunning.isRunning(); | |
* | |
* @Test public void testSendAndReceive() throws Exception { // ... test using RabbitTemplate etc. } | |
* </pre> | |
* <p> | |
* The rule can be declared as static so that it only has to check once for all tests in the enclosing test case, but | |
* there isn't a lot of overhead in making it non-static. | |
* </p> | |
* | |
* @see Assume | |
* @see AssumptionViolatedException | |
* | |
* @author Dave Syer | |
* | |
*/ | |
public class ServerRunning implements MethodRule { | |
private static Log logger = LogFactory.getLog(ServerRunning.class); | |
// Static so that we only test once on failure: speeds up test suite | |
private static Map<Integer, Boolean> serverOnline = new HashMap<Integer, Boolean>(); | |
// Static so that we only test once on failure | |
private static Map<Integer, Boolean> serverOffline = new HashMap<Integer, Boolean>(); | |
private final boolean assumeOnline; | |
private static int DEFAULT_PORT = 8080; | |
private static String DEFAULT_HOST = "localhost"; | |
private int port; | |
private String hostName = DEFAULT_HOST; | |
private RestTemplate client; | |
/** | |
* @return a new rule that assumes an existing running broker | |
*/ | |
public static ServerRunning isRunning() { | |
return new ServerRunning(true); | |
} | |
/** | |
* @return a new rule that assumes there is no existing broker | |
*/ | |
public static ServerRunning isNotRunning() { | |
return new ServerRunning(false); | |
} | |
private ServerRunning(boolean assumeOnline) { | |
this.assumeOnline = assumeOnline; | |
setPort(DEFAULT_PORT); | |
} | |
/** | |
* @param port the port to set | |
*/ | |
public void setPort(int port) { | |
this.port = port; | |
if (!serverOffline.containsKey(port)) { | |
serverOffline.put(port, true); | |
} | |
if (!serverOnline.containsKey(port)) { | |
serverOnline.put(port, true); | |
} | |
client = getRestTemplate(); | |
} | |
/** | |
* @param hostName the hostName to set | |
*/ | |
public void setHostName(String hostName) { | |
this.hostName = hostName; | |
} | |
public Statement apply(final Statement base, FrameworkMethod method, Object target) { | |
// Check at the beginning, so this can be used as a static field | |
if (assumeOnline) { | |
Assume.assumeTrue(serverOnline.get(port)); | |
} else { | |
Assume.assumeTrue(serverOffline.get(port)); | |
} | |
RestTemplate client = new RestTemplate(); | |
boolean followRedirects = HttpURLConnection.getFollowRedirects(); | |
HttpURLConnection.setFollowRedirects(false); | |
boolean online = false; | |
try { | |
client.getForEntity(new UriTemplate(getUrl("/sparklr2/login.jsp")).toString(), String.class); | |
online = true; | |
logger.info("Basic connectivity test passed"); | |
} catch (RestClientException e) { | |
logger.warn(String.format( | |
"Not executing tests because basic connectivity test failed for hostName=%s, port=%d", hostName, | |
port), e); | |
if (assumeOnline) { | |
Assume.assumeNoException(e); | |
} | |
} finally { | |
HttpURLConnection.setFollowRedirects(followRedirects); | |
if (online) { | |
serverOffline.put(port, false); | |
if (!assumeOnline) { | |
Assume.assumeTrue(serverOffline.get(port)); | |
} | |
} else { | |
serverOnline.put(port, false); | |
} | |
} | |
return new Statement() { | |
@Override | |
public void evaluate() throws Throwable { | |
try { | |
postForStatus("/sparklr2/oauth/uncache_approvals", new LinkedMultiValueMap<String, String>()); | |
base.evaluate(); | |
} finally { | |
postForStatus("/sparklr2/oauth/cache_approvals", new LinkedMultiValueMap<String, String>()); | |
} | |
} | |
}; | |
} | |
public String getBaseUrl() { | |
return "http://" + hostName + ":" + port; | |
} | |
public String getUrl(String path) { | |
if (path.startsWith("http:")) { | |
return path; | |
} | |
if (!path.startsWith("/")) { | |
path = "/" + path; | |
} | |
return "http://" + hostName + ":" + port + path; | |
} | |
public ResponseEntity<String> postForString(String path, MultiValueMap<String, String> formData) { | |
HttpHeaders headers = new HttpHeaders(); | |
headers.setAccept(Arrays.asList(MediaType.APPLICATION_FORM_URLENCODED)); | |
return client.exchange(getUrl(path), HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData, | |
headers), String.class); | |
} | |
public ResponseEntity<Void> postForStatus(String path, MultiValueMap<String, String> formData) { | |
return postForStatus(path, new HttpHeaders(), formData); | |
} | |
public ResponseEntity<Void> postForStatus(String path, HttpHeaders headers, MultiValueMap<String, String> formData) { | |
HttpHeaders actualHeaders = new HttpHeaders(); | |
actualHeaders.putAll(headers); | |
actualHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); | |
return client.exchange(getUrl(path), HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData, | |
actualHeaders), null); | |
} | |
public HttpHeaders postForHeaders(String path, MultiValueMap<String, String> formData) { | |
return postForHeaders(path, formData, null); | |
} | |
public HttpHeaders postForHeaders(String path, MultiValueMap<String, String> formData, final HttpHeaders headers) { | |
RequestCallback requestCallback = new NullRequestCallback(); | |
if (headers != null) { | |
requestCallback = new RequestCallback() { | |
public void doWithRequest(ClientHttpRequest request) throws IOException { | |
request.getHeaders().putAll(headers); | |
} | |
}; | |
} | |
StringBuilder builder = new StringBuilder(getUrl(path)); | |
if (!path.contains("?")) { | |
builder.append("?"); | |
} else { | |
builder.append("&"); | |
} | |
for (String key : formData.keySet()) { | |
for (String value : formData.get(key)) { | |
builder.append(key + "=" + value); | |
builder.append("&"); | |
} | |
} | |
builder.deleteCharAt(builder.length() - 1); | |
return client.execute(builder.toString(), HttpMethod.POST, requestCallback, | |
new ResponseExtractor<HttpHeaders>() { | |
public HttpHeaders extractData(ClientHttpResponse response) throws IOException { | |
return response.getHeaders(); | |
} | |
}); | |
} | |
public ResponseEntity<String> postForString(String path, HttpHeaders headers, MultiValueMap<String, String> formData) { | |
HttpHeaders actualHeaders = new HttpHeaders(); | |
actualHeaders.putAll(headers); | |
headers.setAccept(Arrays.asList(MediaType.APPLICATION_FORM_URLENCODED)); | |
return client.exchange(getUrl(path), HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData, | |
headers), String.class); | |
} | |
public ResponseEntity<String> getForString(String path, final HttpHeaders headers) { | |
return client.exchange(getUrl(path), HttpMethod.GET, new HttpEntity<Void>((Void) null, headers), String.class); | |
} | |
public ResponseEntity<String> getForString(String path) { | |
return getForString(path, new HttpHeaders()); | |
} | |
public String getForRedirect(String path, final HttpHeaders headers) { | |
ResponseEntity<Void> response = client.exchange(getUrl(path), HttpMethod.GET, new HttpEntity<Void>((Void) null, | |
headers), Void.class); | |
URI location = response.getHeaders().getLocation(); | |
try { | |
return URLDecoder.decode(location.toString(), "UTF-8"); | |
} catch (UnsupportedEncodingException e) { | |
throw new IllegalStateException("Could not decode URL", e); | |
} | |
} | |
public HttpStatus getStatusCode(String path, final HttpHeaders headers) { | |
RequestCallback requestCallback = new NullRequestCallback(); | |
if (headers != null) { | |
requestCallback = new RequestCallback() { | |
public void doWithRequest(ClientHttpRequest request) throws IOException { | |
request.getHeaders().putAll(headers); | |
} | |
}; | |
} | |
return client.execute(getUrl(path), HttpMethod.GET, requestCallback, | |
new ResponseExtractor<ResponseEntity<Void>>() { | |
public ResponseEntity<Void> extractData(ClientHttpResponse response) throws IOException { | |
FileCopyUtils.copyToByteArray(response.getBody()); | |
return new ResponseEntity<Void>(response.getStatusCode()); | |
} | |
}).getStatusCode(); | |
} | |
public HttpStatus getStatusCode(String path) { | |
return getStatusCode(getUrl(path), null); | |
} | |
public RestTemplate getRestTemplate() { | |
RestTemplate client = new RestTemplate(); | |
client.setRequestFactory(new SimpleClientHttpRequestFactory() { | |
@Override | |
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException { | |
super.prepareConnection(connection, httpMethod); | |
connection.setInstanceFollowRedirects(false); | |
} | |
}); | |
client.setErrorHandler(new ResponseErrorHandler() { | |
// Pass errors through in response entity for status code analysis | |
public boolean hasError(ClientHttpResponse response) throws IOException { | |
return false; | |
} | |
public void handleError(ClientHttpResponse response) throws IOException { | |
} | |
}); | |
return client; | |
} | |
public UriBuilder buildUri(String url) { | |
return UriBuilder.fromUri(url.startsWith("http:") ? url : getUrl(url)); | |
} | |
private static final class NullRequestCallback implements RequestCallback { | |
public void doWithRequest(ClientHttpRequest request) throws IOException { | |
} | |
} | |
public static class UriBuilder { | |
private final String url; | |
private MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>(); | |
public UriBuilder(String url) { | |
this.url = url; | |
} | |
public static UriBuilder fromUri(String url) { | |
return new UriBuilder(url); | |
} | |
public UriBuilder queryParam(String key, String value) { | |
params.add(key, value); | |
return this; | |
} | |
public URI build() { | |
StringBuilder builder = new StringBuilder(url); | |
try { | |
if (!params.isEmpty()) { | |
builder.append("?"); | |
boolean first = true; | |
for (String key : params.keySet()) { | |
if (!first) { | |
builder.append("&"); | |
} else { | |
first = false; | |
} | |
for (String value : params.get(key)) { | |
builder.append(key + "=" + UriUtils.encodeQueryParam(value, "UTF-8")); | |
} | |
} | |
} | |
return new URI(builder.toString()); | |
} catch (UnsupportedEncodingException ex) { | |
// should not happen, UTF-8 is always supported | |
throw new IllegalStateException(ex); | |
} catch (URISyntaxException ex) { | |
throw new IllegalArgumentException("Could not create URI from [" + builder + "]: " + ex, ex); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
org.apache.commons.logging.simplelog.defaultlog=info | |
org.apache.commons.logging.simplelog.showdatetime=true | |
org.apache.commons.logging.simplelog.dateTimeFormat='sparklr2' HH:mm:ss.SSS | |
org.apache.commons.logging.simplelog.log.org.springframework.web=debug | |
org.apache.commons.logging.simplelog.log.org.springframework.security=debug | |
org.apache.commons.logging.simplelog.log.org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource=info |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
org.apache.commons.logging.simplelog.defaultlog=info | |
org.apache.commons.logging.simplelog.showdatetime=true | |
org.apache.commons.logging.simplelog.dateTimeFormat='tonr2' HH:mm:ss.SSS | |
org.apache.commons.logging.simplelog.log.org.springframework.security=debug | |
#org.apache.commons.logging.simplelog.log.org.springframework.security.oauth=info | |
org.apache.commons.logging.simplelog.log.org.springframework.web=debug |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %> | |
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<link href="<c:url value="/main.css"/>" rel="stylesheet" type="text/css"/> | |
<title>tonr</title> | |
</head> | |
<body> | |
<div id="container"> | |
<ul id="mainlinks"> | |
<li><a href="<c:url value="/index.jsp"/>">home</a></li> | |
<authz:authorize ifNotGranted="ROLE_USER"> | |
<li><a href="<c:url value="/login.jsp"/>">login</a></li> | |
</authz:authorize> | |
<li><a href="<c:url value="/sparklr/photos"/>" class="selected">sparklr pics</a></li> | |
<li><a href="<c:url value="/facebook/info"/>">facebook friends</a></li> | |
</ul> | |
<div id="content"> | |
<h1>Your Sparklr Photos</h1> | |
<ul id="picturelist"> | |
<c:forEach var="sparklrPhotoId" items="${photoIds}"> | |
<li><img src="<c:url value="/sparklr/photos/${sparklrPhotoId}"/>"/></li> | |
</c:forEach> | |
</ul> | |
</div> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sparklrPhotoListURL=http://localhost:8080/sparklr2/photos?format=xml | |
sparklrPhotoURLPattern=http://localhost:8080/sparklr2/photos/%s | |
sparklrTrustedMessageURL=http://localhost:8080/sparklr2/photos/trusted/message | |
accessTokenUri=http://localhost:8080/sparklr2/token | |
userAuthorizationUri=http://localhost:8080/sparklr2/authorize |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8" ?> | |
<Module> | |
<ModulePrefs title="OAuth Contacts" scrolling="true"> | |
<Require feature="opensocial-0.8" /> | |
<Require feature="locked-domain"/> | |
<OAuth> | |
<Service name="google"> | |
<Access url="${gae.application.url}/oauth/access_token" method="GET" /> | |
<Request url="${gae.application.url}/oauth/request_token" method="GET" /> | |
<Authorization url="${gae.application.url}/oauth/confirm_access?callbackURL=http://oauth.gmodules.com/gadgets/oauthcallback" /> | |
</Service> | |
</OAuth> | |
</ModulePrefs> | |
<Content type="html"> | |
<![CDATA[ | |
<!-- shindig oauth popup handling code --> | |
<script src="http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/popup.js"></script> | |
<style> | |
#main { | |
margin: 0px; | |
padding: 0px; | |
font-size: small; | |
} | |
</style> | |
<div id="main" style="display: none"> | |
</div> | |
<div id="approval" style="display: none"> | |
<img src="http://gadget-doc-examples.googlecode.com/svn/trunk/images/new.gif"> | |
<a href="#" id="personalize">Personalize this gadget</a> | |
</div> | |
<div id="waiting" style="display: none"> | |
Please click | |
<a href="#" id="approvaldone">I've approved access</a> | |
once you've approved access to your data. | |
</div> | |
<script type="text/javascript"> | |
// Display UI depending on OAuth access state of the gadget (see <divs> above). | |
// If user hasn't approved access to data, provide a "Personalize this gadget" link | |
// that contains the oauthApprovalUrl returned from makeRequest. | |
// | |
// If the user has opened the popup window but hasn't yet approved access, display | |
// text prompting the user to confirm that s/he approved access to data. The user | |
// may not ever need to click this link, if the gadget is able to automatically | |
// detect when the user has approved access, but showing the link gives users | |
// an option to fetch their data even if the automatic detection fails. | |
// | |
// When the user confirms access, the fetchData() function is invoked again to | |
// obtain and display the user's data. | |
function showOneSection(toshow) { | |
var sections = [ 'main', 'approval', 'waiting' ]; | |
for (var i=0; i < sections.length; ++i) { | |
var s = sections[i]; | |
var el = document.getElementById(s); | |
if (s === toshow) { | |
el.style.display = "block"; | |
} else { | |
el.style.display = "none"; | |
} | |
} | |
} | |
// Process returned JSON feed to display data. | |
function showResults(result) { | |
showOneSection('main'); | |
var titleElement = document.createElement('div'); | |
var nameNode = document.createTextNode("some photos"); | |
titleElement.appendChild(nameNode); | |
document.getElementById("main").appendChild(titleElement); | |
document.getElementById("main").appendChild(document.createElement("br")); | |
list = result.photos; | |
for(var i = 0; i < list.length; i++) { | |
entry = list[i]; | |
var divElement = document.createElement('div'); | |
divElement.setAttribute('class', 'name'); | |
var valueNode = document.createTextNode(entry.name); | |
divElement.appendChild(nameNode); | |
divElement.appendChild(valueNode); | |
document.getElementById("main").appendChild(divElement); | |
} | |
} | |
// Invoke makeRequest() to fetch data from the service provider endpoint. | |
// Depending on the results of makeRequest, decide which version of the UI | |
// to ask showOneSection() to display. If user has approved access to his | |
// or her data, display data. | |
// If the user hasn't approved access yet, response.oauthApprovalUrl contains a | |
// URL that includes a Google-supplied request token. This is presented in the | |
// gadget as a link that the user clicks to begin the approval process. | |
function fetchData() { | |
var params = {}; | |
url = "${gae.application.url}/json/photos"; | |
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON; | |
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.OAUTH; | |
params[gadgets.io.RequestParameters.OAUTH_SERVICE_NAME] = "google"; | |
params[gadgets.io.RequestParameters.OAUTH_USE_TOKEN] = "always"; | |
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET; | |
gadgets.io.makeRequest(url, function (response) { | |
if (response.oauthApprovalUrl) { | |
// Create the popup handler. The onOpen function is called when the user | |
// opens the popup window. The onClose function is called when the popup | |
// window is closed. | |
var popup = shindig.oauth.popup({ | |
destination: response.oauthApprovalUrl, | |
windowOptions: null, | |
onOpen: function() { showOneSection('waiting'); }, | |
onClose: function() { fetchData(); } | |
}); | |
// Use the popup handler to attach onclick handlers to UI elements. The | |
// createOpenerOnClick() function returns an onclick handler to open the | |
// popup window. The createApprovedOnClick function returns an onclick | |
// handler that will close the popup window and attempt to fetch the user's | |
// data again. | |
var personalize = document.getElementById('personalize'); | |
personalize.onclick = popup.createOpenerOnClick(); | |
var approvaldone = document.getElementById('approvaldone'); | |
approvaldone.onclick = popup.createApprovedOnClick(); | |
showOneSection('approval'); | |
} else if (response.data) { | |
showOneSection('main'); | |
showResults(response.data); | |
} else { | |
// The response.oauthError and response.oauthErrorText values may help debug | |
// problems with your gadget. | |
var main = document.getElementById('main'); | |
var err = document.createTextNode('OAuth error: ' + | |
response.oauthError + ': ' + response.oauthErrorText); | |
main.appendChild(err); | |
showOneSection('main'); | |
} | |
}, params); | |
} | |
// Call fetchData() when gadget loads. | |
gadgets.util.registerOnLoadHandler(fetchData); | |
</script> | |
]]> | |
</Content> | |
</Module> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.tonr.mvc; | |
import java.awt.image.BufferedImage; | |
import java.io.InputStream; | |
import java.util.Iterator; | |
import javax.imageio.ImageIO; | |
import javax.imageio.ImageReadParam; | |
import javax.imageio.ImageReader; | |
import javax.imageio.stream.MemoryCacheImageInputStream; | |
import javax.servlet.UnavailableException; | |
import org.springframework.http.HttpHeaders; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.http.MediaType; | |
import org.springframework.http.ResponseEntity; | |
import org.springframework.http.converter.HttpMessageNotReadableException; | |
import org.springframework.security.oauth.examples.tonr.SparklrService; | |
import org.springframework.stereotype.Controller; | |
import org.springframework.ui.Model; | |
import org.springframework.web.bind.annotation.PathVariable; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
/** | |
* @author Ryan Heaton | |
* @author Dave Syer | |
*/ | |
@Controller | |
public class SparklrController { | |
private SparklrService sparklrService; | |
@RequestMapping("/sparklr/photos") | |
public String photos(Model model) throws Exception { | |
model.addAttribute("photoIds", sparklrService.getSparklrPhotoIds()); | |
return "sparklr"; | |
} | |
@RequestMapping("/sparklr/photos/{id}") | |
public ResponseEntity<BufferedImage> photo(@PathVariable String id) throws Exception { | |
InputStream photo = sparklrService.loadSparklrPhoto(id); | |
if (photo == null) { | |
throw new UnavailableException("The requested photo does not exist"); | |
} | |
BufferedImage body; | |
MediaType contentType = MediaType.IMAGE_JPEG; | |
Iterator<ImageReader> imageReaders = ImageIO.getImageReadersByMIMEType(contentType.toString()); | |
if (imageReaders.hasNext()) { | |
ImageReader imageReader = imageReaders.next(); | |
ImageReadParam irp = imageReader.getDefaultReadParam(); | |
imageReader.setInput(new MemoryCacheImageInputStream(photo), true); | |
body = imageReader.read(0, irp); | |
} else { | |
throw new HttpMessageNotReadableException("Could not find javax.imageio.ImageReader for Content-Type [" | |
+ contentType + "]"); | |
} | |
HttpHeaders headers = new HttpHeaders(); | |
headers.setContentType(MediaType.IMAGE_JPEG); | |
return new ResponseEntity<BufferedImage>(body, headers, HttpStatus.OK); | |
} | |
@RequestMapping("/trusted/message") | |
public String trusted(Model model) throws Exception { | |
model.addAttribute("message", this.sparklrService.getTrustedMessage()); | |
return "home"; | |
} | |
public void setSparklrService(SparklrService sparklrService) { | |
this.sparklrService = sparklrService; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.tonr; | |
/** | |
* @author Ryan Heaton | |
*/ | |
public class SparklrException extends Exception { | |
public SparklrException(String message) { | |
super(message); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.tonr; | |
import java.io.InputStream; | |
import java.util.List; | |
/** | |
* @author Ryan Heaton | |
*/ | |
public interface SparklrService { | |
/** | |
* Get the list of Sparklr photo ids for the current user. | |
* | |
* @return The list of photo ids for the current user. | |
*/ | |
List<String> getSparklrPhotoIds() throws SparklrException; | |
/** | |
* Loads the Sparklr photo for the current user. | |
* | |
* @param id the id or the photo. | |
* @return The sparklr photo. | |
*/ | |
InputStream loadSparklrPhoto(String id) throws SparklrException; | |
/** | |
* @return a message | |
*/ | |
String getTrustedMessage(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.tonr.impl; | |
import java.io.ByteArrayInputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.URI; | |
import java.util.ArrayList; | |
import java.util.List; | |
import javax.xml.parsers.ParserConfigurationException; | |
import javax.xml.parsers.SAXParser; | |
import javax.xml.parsers.SAXParserFactory; | |
import org.springframework.security.oauth.examples.tonr.SparklrException; | |
import org.springframework.security.oauth.examples.tonr.SparklrService; | |
import org.springframework.security.oauth2.client.OAuth2RestTemplate; | |
import org.springframework.web.client.RestOperations; | |
import org.xml.sax.Attributes; | |
import org.xml.sax.SAXException; | |
import org.xml.sax.helpers.DefaultHandler; | |
/** | |
* @author Ryan Heaton | |
*/ | |
public class SparklrServiceImpl implements SparklrService { | |
private String sparklrPhotoListURL; | |
private String sparklrTrustedMessageURL; | |
private String sparklrPhotoURLPattern; | |
private RestOperations sparklrRestTemplate; | |
private RestOperations trustedClientRestTemplate; | |
public List<String> getSparklrPhotoIds() throws SparklrException { | |
try { | |
InputStream photosXML = new ByteArrayInputStream(sparklrRestTemplate.getForObject( | |
URI.create(sparklrPhotoListURL), byte[].class)); | |
final List<String> photoIds = new ArrayList<String>(); | |
SAXParserFactory parserFactory = SAXParserFactory.newInstance(); | |
parserFactory.setValidating(false); | |
parserFactory.setXIncludeAware(false); | |
parserFactory.setNamespaceAware(false); | |
SAXParser parser = parserFactory.newSAXParser(); | |
parser.parse(photosXML, new DefaultHandler() { | |
@Override | |
public void startElement(String uri, String localName, String qName, Attributes attributes) | |
throws SAXException { | |
if ("photo".equals(qName)) { | |
photoIds.add(attributes.getValue("id")); | |
} | |
} | |
}); | |
return photoIds; | |
} catch (IOException e) { | |
throw new IllegalStateException(e); | |
} catch (SAXException e) { | |
throw new IllegalStateException(e); | |
} catch (ParserConfigurationException e) { | |
throw new IllegalStateException(e); | |
} | |
} | |
public InputStream loadSparklrPhoto(String id) throws SparklrException { | |
return new ByteArrayInputStream(sparklrRestTemplate.getForObject( | |
URI.create(String.format(sparklrPhotoURLPattern, id)), byte[].class)); | |
} | |
public String getTrustedMessage() { | |
return this.trustedClientRestTemplate.getForObject(URI.create(sparklrTrustedMessageURL), String.class); | |
} | |
public void setSparklrPhotoURLPattern(String sparklrPhotoURLPattern) { | |
this.sparklrPhotoURLPattern = sparklrPhotoURLPattern; | |
} | |
public void setSparklrPhotoListURL(String sparklrPhotoListURL) { | |
this.sparklrPhotoListURL = sparklrPhotoListURL; | |
} | |
public void setSparklrTrustedMessageURL(String sparklrTrustedMessageURL) { | |
this.sparklrTrustedMessageURL = sparklrTrustedMessageURL; | |
} | |
public void setSparklrRestTemplate(OAuth2RestTemplate sparklrRestTemplate) { | |
this.sparklrRestTemplate = sparklrRestTemplate; | |
} | |
public void setTrustedClientRestTemplate(RestOperations trustedClientRestTemplate) { | |
this.trustedClientRestTemplate = trustedClientRestTemplate; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2002-2011 the original author or authors. | |
* | |
* Licensed 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 | |
* | |
* 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.springframework.security.oauth.examples.sparklr.oauth; | |
import java.util.Collection; | |
import java.util.HashSet; | |
import org.springframework.security.core.Authentication; | |
import org.springframework.security.oauth2.provider.AuthorizationRequest; | |
import org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler; | |
/** | |
* @author Dave Syer | |
* | |
*/ | |
public class SparklrUserApprovalHandler extends TokenServicesUserApprovalHandler { | |
private Collection<String> autoApproveClients = new HashSet<String>(); | |
private boolean useTokenServices = true; | |
/** | |
* @param useTokenServices the useTokenServices to set | |
*/ | |
public void setUseTokenServices(boolean useTokenServices) { | |
this.useTokenServices = useTokenServices; | |
} | |
/** | |
* @param autoApproveClients the auto approve clients to set | |
*/ | |
public void setAutoApproveClients(Collection<String> autoApproveClients) { | |
this.autoApproveClients = autoApproveClients; | |
} | |
/** | |
* Allows automatic approval for a white list of clients in the implicit grant case. | |
* | |
* @param authorizationRequest The authorization request. | |
* @param userAuthentication the current user authentication | |
* | |
* @return Whether the specified request has been approved by the current user. | |
*/ | |
public boolean isApproved(AuthorizationRequest authorizationRequest, Authentication userAuthentication) { | |
if (useTokenServices && super.isApproved(authorizationRequest, userAuthentication)) { | |
return true; | |
} | |
if (!userAuthentication.isAuthenticated()) { | |
return false; | |
} | |
return authorizationRequest.isApproved() | |
|| (authorizationRequest.getResponseTypes().contains("token") && autoApproveClients | |
.contains(authorizationRequest.getClientId())); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8" ?> | |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xmlns:sec="http://www.springframework.org/schema/security" | |
xmlns:mvc="http://www.springframework.org/schema/mvc" | |
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd | |
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd | |
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd | |
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> | |
<!-- Just for testing... --> | |
<http pattern="/oauth/cache_approvals" security="none" xmlns="http://www.springframework.org/schema/security" /> | |
<http pattern="/oauth/uncache_approvals" security="none" xmlns="http://www.springframework.org/schema/security" /> | |
<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager" | |
entry-point-ref="oauthAuthenticationEntryPoint" xmlns="http://www.springframework.org/schema/security"> | |
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" /> | |
<anonymous enabled="false" /> | |
<http-basic entry-point-ref="oauthAuthenticationEntryPoint" /> | |
<!-- include this only if you need to authenticate clients via request parameters --> | |
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" /> | |
<access-denied-handler ref="oauthAccessDeniedHandler" /> | |
</http> | |
<!-- The OAuth2 protected resources are separated out into their own block so we can deal with authorization and error handling | |
separately. This isn't mandatory, but it makes it easier to control the behaviour. --> | |
<http pattern="/oauth/(users|clients)/.*" request-matcher="regex" create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint" | |
use-expressions="true" xmlns="http://www.springframework.org/schema/security"> | |
<anonymous enabled="false" /> | |
<intercept-url pattern="/oauth/users/([^/].*?)/tokens/.*" | |
access="oauthClientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or oauthIsClient()) and oauthHasScope('write')" | |
method="DELETE" /> | |
<intercept-url pattern="/oauth/users/.*" | |
access="oauthClientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or oauthIsClient()) and oauthHasScope('read')" | |
method="GET" /> | |
<intercept-url pattern="/oauth/clients/.*" access="oauthClientHasRole('ROLE_CLIENT') and oauthIsClient() and oauthHasScope('read')" | |
method="GET" /> | |
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" /> | |
<access-denied-handler ref="oauthAccessDeniedHandler" /> | |
<expression-handler ref="oauthWebExpressionHandler" /> | |
</http> | |
<!-- The OAuth2 protected resources are separated out into their own block so we can deal with authorization and error handling | |
separately. This isn't mandatory, but it makes it easier to control the behaviour. --> | |
<http pattern="/photos/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint" | |
access-decision-manager-ref="accessDecisionManager" xmlns="http://www.springframework.org/schema/security"> | |
<anonymous enabled="false" /> | |
<intercept-url pattern="/photos" access="ROLE_USER,SCOPE_READ" /> | |
<intercept-url pattern="/photos/trusted/**" access="ROLE_CLIENT,SCOPE_TRUST" /> | |
<intercept-url pattern="/photos/user/**" access="ROLE_USER,SCOPE_TRUST" /> | |
<intercept-url pattern="/photos/**" access="ROLE_USER,SCOPE_READ" /> | |
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" /> | |
<access-denied-handler ref="oauthAccessDeniedHandler" /> | |
</http> | |
<http access-denied-page="/login.jsp?authorization_error=true" disable-url-rewriting="true" | |
xmlns="http://www.springframework.org/schema/security"> | |
<intercept-url pattern="/oauth/**" access="ROLE_USER" /> | |
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" /> | |
<form-login authentication-failure-url="/login.jsp?authentication_error=true" default-target-url="/index.jsp" | |
login-page="/login.jsp" login-processing-url="/login.do" /> | |
<logout logout-success-url="/index.jsp" logout-url="/logout.do" /> | |
<anonymous /> | |
</http> | |
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> | |
<property name="realmName" value="sparklr2" /> | |
</bean> | |
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" /> | |
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter"> | |
<property name="authenticationManager" ref="clientAuthenticationManager" /> | |
</bean> | |
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased" xmlns="http://www.springframework.org/schema/beans"> | |
<constructor-arg> | |
<list> | |
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" /> | |
<bean class="org.springframework.security.access.vote.RoleVoter" /> | |
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" /> | |
</list> | |
</constructor-arg> | |
</bean> | |
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security"> | |
<authentication-provider user-service-ref="clientDetailsUserService" /> | |
</authentication-manager> | |
<authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security"> | |
<authentication-provider> | |
<user-service> | |
<user name="marissa" password="koala" authorities="ROLE_USER" /> | |
<user name="paul" password="emu" authorities="ROLE_USER" /> | |
</user-service> | |
</authentication-provider> | |
</authentication-manager> | |
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"> | |
<constructor-arg ref="clientDetails" /> | |
</bean> | |
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" /> | |
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices"> | |
<property name="tokenStore" ref="tokenStore" /> | |
<property name="supportRefreshToken" value="true" /> | |
</bean> | |
<bean id="userApprovalHandler" class="org.springframework.security.oauth.examples.sparklr.oauth.SparklrUserApprovalHandler"> | |
<property name="autoApproveClients"> | |
<set> | |
<value>my-less-trusted-autoapprove-client</value> | |
</set> | |
</property> | |
<property name="tokenServices" ref="tokenServices" /> | |
</bean> | |
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" | |
user-approval-handler-ref="userApprovalHandler" token-endpoint-url="/token" authorization-endpoint-url="/authorize"> | |
<oauth:authorization-code /> | |
<oauth:implicit /> | |
<oauth:refresh-token /> | |
<oauth:client-credentials /> | |
<oauth:password /> | |
</oauth:authorization-server> | |
<oauth:resource-server id="resourceServerFilter" resource-id="sparklr" token-services-ref="tokenServices" /> | |
<oauth:client-details-service id="clientDetails"> | |
<oauth:client client-id="my-trusted-client" authorized-grant-types="password,authorization_code,refresh_token,implicit" | |
authorities="ROLE_CLIENT, ROLE_TRUSTED_CLIENT" scope="read,write,trust" /> | |
<oauth:client client-id="my-trusted-client-with-secret" authorized-grant-types="password,authorization_code,refresh_token,implicit" | |
secret="somesecret" authorities="ROLE_CLIENT, ROLE_TRUSTED_CLIENT" /> | |
<oauth:client client-id="my-less-trusted-client" authorized-grant-types="authorization_code,implicit" | |
authorities="ROLE_CLIENT" /> | |
<oauth:client client-id="my-less-trusted-autoapprove-client" authorized-grant-types="implicit" | |
authorities="ROLE_CLIENT" /> | |
<oauth:client client-id="my-client-with-registered-redirect" authorized-grant-types="authorization_code,client_credentials" | |
authorities="ROLE_CLIENT" redirect-uri="http://anywhere?key=value" scope="read,trust" /> | |
<oauth:client client-id="my-untrusted-client-with-registered-redirect" authorized-grant-types="authorization_code" | |
authorities="ROLE_CLIENT" redirect-uri="http://anywhere" scope="read" /> | |
<oauth:client client-id="tonr" resource-ids="sparklr" authorized-grant-types="authorization_code,implicit" | |
authorities="ROLE_CLIENT" scope="read,write" secret="secret" /> | |
</oauth:client-details-service> | |
<mvc:annotation-driven /> | |
<mvc:default-servlet-handler /> | |
<sec:global-method-security pre-post-annotations="enabled" proxy-target-class="true"> | |
<!--you could also wire in the expression handler up at the layer of the http filters. See https://jira.springsource.org/browse/SEC-1452 --> | |
<sec:expression-handler ref="oauthExpressionHandler" /> | |
</sec:global-method-security> | |
<oauth:expression-handler id="oauthExpressionHandler" /> | |
<oauth:web-expression-handler id="oauthWebExpressionHandler" /> | |
<!--Basic application beans. --> | |
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> | |
<property name="prefix" value="/WEB-INF/jsp/" /> | |
<property name="suffix" value=".jsp" /> | |
</bean> | |
<bean id="photoController" class="org.springframework.security.oauth.examples.sparklr.mvc.PhotoController"> | |
<property name="photoService" ref="photoServices" /> | |
</bean> | |
<bean id="adminController" class="org.springframework.security.oauth.examples.sparklr.mvc.AdminController"> | |
<property name="tokenServices" ref="tokenServices" /> | |
<property name="userApprovalHandler" ref="userApprovalHandler" /> | |
</bean> | |
<bean id="accessConfirmationController" class="org.springframework.security.oauth.examples.sparklr.mvc.AccessConfirmationController"> | |
<property name="clientDetailsService" ref="clientDetails" /> | |
</bean> | |
<bean id="photoServices" class="org.springframework.security.oauth.examples.sparklr.impl.PhotoServiceImpl"> | |
<property name="photos"> | |
<list> | |
<bean class="org.springframework.security.oauth.examples.sparklr.PhotoInfo"> | |
<property name="id" value="1" /> | |
<property name="name" value="photo1.jpg" /> | |
<property name="userId" value="marissa" /> | |
<property name="resourceURL" value="/org/springframework/security/oauth/examples/sparklr/impl/resources/photo1.jpg" /> | |
</bean> | |
<bean class="org.springframework.security.oauth.examples.sparklr.PhotoInfo"> | |
<property name="id" value="2" /> | |
<property name="name" value="photo2.jpg" /> | |
<property name="userId" value="paul" /> | |
<property name="resourceURL" value="/org/springframework/security/oauth/examples/sparklr/impl/resources/photo2.jpg" /> | |
</bean> | |
<bean class="org.springframework.security.oauth.examples.sparklr.PhotoInfo"> | |
<property name="id" value="3" /> | |
<property name="name" value="photo3.jpg" /> | |
<property name="userId" value="marissa" /> | |
<property name="resourceURL" value="/org/springframework/security/oauth/examples/sparklr/impl/resources/photo3.jpg" /> | |
</bean> | |
<bean class="org.springframework.security.oauth.examples.sparklr.PhotoInfo"> | |
<property name="id" value="4" /> | |
<property name="name" value="photo4.jpg" /> | |
<property name="userId" value="paul" /> | |
<property name="resourceURL" value="/org/springframework/security/oauth/examples/sparklr/impl/resources/photo4.jpg" /> | |
</bean> | |
<bean class="org.springframework.security.oauth.examples.sparklr.PhotoInfo"> | |
<property name="id" value="5" /> | |
<property name="name" value="photo5.jpg" /> | |
<property name="userId" value="marissa" /> | |
<property name="resourceURL" value="/org/springframework/security/oauth/examples/sparklr/impl/resources/photo5.jpg" /> | |
</bean> | |
<bean class="org.springframework.security.oauth.examples.sparklr.PhotoInfo"> | |
<property name="id" value="6" /> | |
<property name="name" value="photo6.jpg" /> | |
<property name="userId" value="paul" /> | |
<property name="resourceURL" value="/org/springframework/security/oauth/examples/sparklr/impl/resources/photo6.jpg" /> | |
</bean> | |
</list> | |
</property> | |
</bean> | |
</beans> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8" ?> | |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:sec="http://www.springframework.org/schema/security" | |
xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xmlns:context="http://www.springframework.org/schema/context" | |
xmlns:aop="http://www.springframework.org/schema/aop" | |
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd | |
http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd | |
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd | |
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd | |
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd | |
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> | |
<http access-denied-page="/login.jsp?authorization_error=true" xmlns="http://www.springframework.org/schema/security"> | |
<intercept-url pattern="/sparklr/**" access="ROLE_USER" /> | |
<intercept-url pattern="/facebook/**" access="ROLE_USER" /> | |
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" /> | |
<form-login authentication-failure-url="/login.jsp?authentication_error=true" default-target-url="/index.jsp" | |
login-page="/login.jsp" login-processing-url="/login.do" /> | |
<logout logout-success-url="/index.jsp" logout-url="/logout.do" /> | |
<anonymous /> | |
<custom-filter ref="oauth2ClientFilter" after="EXCEPTION_TRANSLATION_FILTER" /> | |
</http> | |
<authentication-manager xmlns="http://www.springframework.org/schema/security"> | |
<authentication-provider> | |
<user-service> | |
<user name="marissa" password="wombat" authorities="ROLE_USER" /> | |
<user name="sam" password="kangaroo" authorities="ROLE_USER" /> | |
</user-service> | |
</authentication-provider> | |
</authentication-manager> | |
<!--apply the oauth client context --> | |
<oauth:client id="oauth2ClientFilter" /> | |
<!--define an oauth 2 resource for sparklr --> | |
<oauth:resource id="sparklr" type="authorization_code" client-id="tonr" client-secret="secret" | |
access-token-uri="${accessTokenUri}" user-authorization-uri="${userAuthorizationUri}" scope="read" /> | |
<!--define an oauth 2 resource for trusted client on sparklr --> | |
<oauth:resource id="trusted" type="client_credentials" client-id="my-client-with-registered-redirect" | |
access-token-uri="${accessTokenUri}" scope="trust" /> | |
<!--define an oauth 2 resource for facebook. according to the facebook docs, the 'client-id' is the App ID, and the 'client-secret' | |
is the App Secret --> | |
<oauth:resource id="facebook" type="authorization_code" client-id="233668646673605" client-secret="33b17e044ee6a4fa383f46ec6e28ea1d" | |
authentication-scheme="query" access-token-uri="https://graph.facebook.com/oauth/access_token" user-authorization-uri="https://www.facebook.com/dialog/oauth" | |
token-name="oauth_token" client-authentication-scheme="form" /> | |
<context:property-placeholder location="classpath:/sparklr.properties" /> | |
<mvc:default-servlet-handler /> | |
<mvc:annotation-driven> | |
<mvc:message-converters> | |
<bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter" /> | |
</mvc:message-converters> | |
</mvc:annotation-driven> | |
<bean id="contentViewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> | |
<property name="mediaTypes"> | |
<map> | |
<entry key="json" value="application/json" /> | |
</map> | |
</property> | |
<property name="defaultViews"> | |
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" /> | |
</property> | |
</bean> | |
<!--Basic application beans. --> | |
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> | |
<property name="prefix" value="/WEB-INF/jsp/" /> | |
<property name="suffix" value=".jsp" /> | |
</bean> | |
<bean id="sparklrController" class="org.springframework.security.oauth.examples.tonr.mvc.SparklrController"> | |
<property name="sparklrService" ref="sparklrService" /> | |
</bean> | |
<bean id="facebookController" class="org.springframework.security.oauth.examples.tonr.mvc.FacebookController"> | |
<property name="facebookRestTemplate"> | |
<oauth:rest-template resource="facebook"> | |
<property name="messageConverters"> | |
<list> | |
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"> | |
<property name="supportedMediaTypes"> | |
<list> | |
<bean class="org.springframework.http.MediaType"> | |
<!--facebook sends its json as text/javascript for some reason --> | |
<constructor-arg value="text" /> | |
<constructor-arg value="javascript" /> | |
</bean> | |
<bean class="org.springframework.http.MediaType"> | |
<constructor-arg value="application" /> | |
<constructor-arg value="json" /> | |
</bean> | |
</list> | |
</property> | |
</bean> | |
</list> | |
</property> | |
</oauth:rest-template> | |
</property> | |
</bean> | |
<bean id="sparklrService" class="org.springframework.security.oauth.examples.tonr.impl.SparklrServiceImpl"> | |
<property name="sparklrPhotoListURL" value="${sparklrPhotoListURL}" /> | |
<property name="sparklrTrustedMessageURL" value="${sparklrTrustedMessageURL}" /> | |
<property name="sparklrPhotoURLPattern" value="${sparklrPhotoURLPattern}" /> | |
<property name="sparklrRestTemplate"> | |
<oauth:rest-template resource="sparklr" /> | |
</property> | |
<property name="trustedClientRestTemplate"> | |
<oauth:rest-template resource="trusted" /> | |
</property> | |
</bean> | |
</beans> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* CSS Document */ | |
body { | |
color: white; | |
font-family: 'Trebuchet MS', Helvetica, sans-serif; | |
font-size: 12px; | |
margin: 0 auto; | |
width: 736px; | |
background: url( 'images/bg.gif' ); | |
} | |
#content { | |
background: #3d3d3d; | |
width: 676px; | |
margin-top: 20px; | |
padding: 0 30px 25px 30px; | |
} | |
a { | |
color: lightblue; | |
text-decoration: none; | |
} | |
a:hover { | |
color: red; | |
text-decoration: none; | |
} | |
h1 { | |
background: url( 'images/header.jpg' ); | |
height: 36px; | |
width: 721px; | |
margin: 0 0 1em 0; | |
padding-top: 80px; | |
padding-left: 15px; | |
font-size: 1.8em; | |
font-variant: small-caps; | |
} | |
h2 { | |
font-size: 1.2em; | |
margin-left: -10px; | |
padding-top: 20px; | |
font-weight: bold; | |
letter-spacing: .3px; | |
} | |
.error h2 { | |
color: red; | |
font-size: 1.2em; | |
padding-top: 20px; | |
font-weight: bold; | |
letter-spacing: .3px; | |
} | |
.error p { | |
color: red; | |
} | |
p { | |
letter-spacing: .2px; | |
} | |
label { | |
text-indent: 20px; | |
letter-spacing: .2px; | |
padding: 5px 5px 5px 5px; | |
} | |
#footer { | |
font-size: .8em; | |
margin-top: 1em; | |
} | |
#footer a { | |
color: #333333; | |
font-weight: bold; | |
font-size: 1em | |
} | |
#footer a:hover { | |
color: red; | |
font-weight: bold; | |
font-size: 1em | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<link href="main.css" rel="stylesheet" type="text/css"/> | |
<title>xgallery</title> | |
</head> | |
<body> | |
<div id="container"> | |
<ul id="mainlinks"> | |
<li><a href="#">home</a></li> | |
<li><a href="#">about</a></li> | |
<li><a href="#" class="selected">gallery pics</a></li> | |
<li><a href="http://www.naturaldesigngroup.co.uk">ndg</a></li> | |
<li><a href="http://www.planetphotoshop.com">planetphotoshop</a></li> | |
</ul> | |
<div id="content"> | |
<ul id="picturelist"> | |
<li>a tree<a href="#"><img src="images/autumntree.jpg" alt="a tree"/></a></li> | |
<li>the sea<a href="#"><img src="images/bluesea.jpg" alt="the sea"/></a></li> | |
<li>the forest<a href="#"><img src="images/greenforest.jpg" alt="the forest"/></a></li> | |
<li>a bunch of orange flowers<a href="#"><img src="images/orangeflowers.jpg" alt="a bunch of orange flowers"/></a></li> | |
<li>a turtle in the sea<a href="#"><img src="images/turtleblue.jpg" alt="a turtle in the sea"/></a></li> | |
<li>a garden/jungle thing<a href="#"><img src="images/jungle.jpg" alt="a garden/jungle thing"/></a></li> | |
<li>an oryx antelope in the desert<a href="#"><img src="images/antelope.jpg" alt="an oryx antelope in the desert"/></a></li> | |
<li>mesas in the desert<a href="#"><img src="images/desert.jpg" alt="mesas in the desert"/><span class="main"><!--This theme is free for distriubtion, so long as link to openwebdesing.org and dubaiapartments.biz stay on the theme--> Courtesy <a | |
href="http://www.openwebdesign.org" target="_blank">Open | |
Web Design</a><a href="http://www.dubaiapartments.biz" target="_blank"><img src="spacer.gif" width="5" height="5" border="0"/></a>Thanks | |
to <a href="http://www.dubaiapartments.biz/" target="_blank">Dubai Hotels</a></span></a></li> | |
</ul> | |
</div> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth2.provider; | |
import static org.junit.Assert.assertEquals; | |
import static org.junit.Assert.assertTrue; | |
import java.util.Arrays; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.springframework.http.HttpEntity; | |
import org.springframework.http.HttpHeaders; | |
import org.springframework.http.HttpMethod; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.http.MediaType; | |
import org.springframework.http.ResponseEntity; | |
import org.springframework.security.oauth2.client.test.OAuth2ContextConfiguration; | |
import org.springframework.security.oauth2.client.test.OAuth2ContextSetup; | |
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; | |
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; | |
import org.springframework.security.oauth2.common.OAuth2AccessToken; | |
/** | |
* @author Dave Syer | |
*/ | |
public class TestAdminEndpoints { | |
@Rule | |
public ServerRunning serverRunning = ServerRunning.isRunning(); | |
@Rule | |
public OAuth2ContextSetup context = OAuth2ContextSetup.standard(serverRunning); | |
@Test | |
@OAuth2ContextConfiguration(ResourceOwnerReadOnly.class) | |
public void testListTokensByUser() throws Exception { | |
ResponseEntity<String> result = serverRunning.getForString("/sparklr2/oauth/users/marissa/tokens"); | |
assertEquals(HttpStatus.OK, result.getStatusCode()); | |
// System.err.println(result.getBody()); | |
assertTrue(result.getBody().contains(context.getAccessToken().getValue())); | |
} | |
@Test | |
@OAuth2ContextConfiguration(ResourceOwnerWriteOnly.class) | |
public void testRevokeTokenByUser() throws Exception { | |
OAuth2AccessToken token = context.getAccessToken(); | |
HttpHeaders headers = new HttpHeaders(); | |
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); | |
HttpEntity<?> request = new HttpEntity<Void>(headers); | |
assertEquals( | |
HttpStatus.NO_CONTENT, | |
serverRunning | |
.getRestTemplate() | |
.exchange(serverRunning.getUrl("/sparklr2/oauth/users/{user}/tokens/{token}"), | |
HttpMethod.DELETE, request, Void.class, "marissa", token.getValue()).getStatusCode()); | |
ResponseEntity<String> result = serverRunning.getForString("/sparklr2/oauth/users/marissa/tokens", headers); | |
assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode()); | |
assertTrue(result.getBody().contains("invalid_token")); | |
} | |
@Test | |
@OAuth2ContextConfiguration(ClientCredentialsReadOnly.class) | |
public void testClientListsTokensOfUser() throws Exception { | |
ResponseEntity<String> result = serverRunning.getForString("/sparklr2/oauth/users/marissa/tokens"); | |
assertEquals(HttpStatus.OK, result.getStatusCode()); | |
assertTrue(result.getBody().startsWith("[")); | |
assertTrue(result.getBody().endsWith("]")); | |
assertTrue(result.getBody().length() > 0); | |
} | |
@Test | |
@OAuth2ContextConfiguration(ResourceOwnerReadOnly.class) | |
public void testCannotListTokensOfAnotherUser() throws Exception { | |
assertEquals(HttpStatus.FORBIDDEN, serverRunning.getStatusCode("/sparklr2/oauth/users/foo/tokens")); | |
} | |
@Test | |
@OAuth2ContextConfiguration(ClientCredentialsReadOnly.class) | |
public void testListTokensByClient() throws Exception { | |
ResponseEntity<String> result = serverRunning | |
.getForString("/sparklr2/oauth/clients/my-client-with-registered-redirect/tokens"); | |
assertEquals(HttpStatus.OK, result.getStatusCode()); | |
assertTrue(result.getBody().contains(context.getAccessToken().getValue())); | |
} | |
@Test | |
@OAuth2ContextConfiguration(ResourceOwnerReadOnly.class) | |
public void testUserCannotListTokensOfClient() throws Exception { | |
assertEquals(HttpStatus.FORBIDDEN, | |
serverRunning.getStatusCode("/sparklr2/oauth/clients/my-client-with-registered-redirect/tokens")); | |
} | |
static class ResourceOwnerReadOnly extends ResourceOwnerPasswordResourceDetails { | |
public ResourceOwnerReadOnly(Object target) { | |
setClientId("my-trusted-client"); | |
setId(getClientId()); | |
setScope(Arrays.asList("read")); | |
setUsername("marissa"); | |
setPassword("koala"); | |
TestAdminEndpoints test = (TestAdminEndpoints) target; | |
setAccessTokenUri(test.serverRunning.getUrl("/sparklr2/oauth/token")); | |
} | |
} | |
static class ClientCredentialsReadOnly extends ClientCredentialsResourceDetails { | |
public ClientCredentialsReadOnly(Object target) { | |
setClientId("my-client-with-registered-redirect"); | |
setId(getClientId()); | |
setScope(Arrays.asList("read")); | |
TestAdminEndpoints test = (TestAdminEndpoints) target; | |
setAccessTokenUri(test.serverRunning.getUrl("/sparklr2/oauth/token")); | |
} | |
} | |
static class ResourceOwnerWriteOnly extends ResourceOwnerReadOnly { | |
public ResourceOwnerWriteOnly(Object target) { | |
super(target); | |
setScope(Arrays.asList("write")); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2006-2010 the original author or authors. | |
* | |
* Licensed 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 | |
* | |
* 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.springframework.security.oauth2.provider; | |
import org.junit.Test; | |
import org.springframework.context.support.GenericXmlApplicationContext; | |
import org.springframework.core.io.FileSystemResource; | |
/** | |
* @author Dave Syer | |
* | |
*/ | |
public class TestBootstrap { | |
@Test | |
public void testRootContext() throws Exception { | |
GenericXmlApplicationContext context = new GenericXmlApplicationContext(new FileSystemResource("src/main/webapp/WEB-INF/spring-servlet.xml")); | |
context.close(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2006-2010 the original author or authors. | |
* | |
* Licensed 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 | |
* | |
* 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.springframework.security.oauth.examples.tonr; | |
import org.junit.Test; | |
import org.springframework.context.support.GenericXmlApplicationContext; | |
import org.springframework.core.io.FileSystemResource; | |
/** | |
* @author Dave Syer | |
* | |
*/ | |
public class TestBootstrap { | |
@Test | |
public void testRootContext() throws Exception { | |
GenericXmlApplicationContext context = new GenericXmlApplicationContext(new FileSystemResource("src/main/webapp/WEB-INF/spring-servlet.xml")); | |
context.close(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.tonr; | |
import static org.junit.Assert.assertEquals; | |
import java.util.Arrays; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.springframework.security.oauth2.client.OAuth2RestTemplate; | |
import org.springframework.security.oauth2.client.context.DefaultOAuth2ClientContext; | |
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest; | |
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; | |
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; | |
import org.springframework.security.oauth2.common.OAuth2AccessToken; | |
import org.springframework.web.client.RestTemplate; | |
/** | |
* @author Ryan Heaton | |
* @author Dave Syer | |
*/ | |
public class TestClientCredentialsGrant { | |
@Rule | |
public ServerRunning serverRunning = ServerRunning.isRunning(); | |
@Test | |
public void testConnectDirectlyToResourceServer() throws Exception { | |
ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails(); | |
resource.setAccessTokenUri(serverRunning.getUrl("/sparklr2/oauth/token")); | |
resource.setClientId("my-client-with-registered-redirect"); | |
resource.setId("sparklr"); | |
resource.setScope(Arrays.asList("trust")); | |
ClientCredentialsAccessTokenProvider provider = new ClientCredentialsAccessTokenProvider(); | |
OAuth2AccessToken accessToken = provider.obtainAccessToken(resource, new DefaultAccessTokenRequest()); | |
// TODO: should this work? The client id is different. | |
OAuth2RestTemplate template = new OAuth2RestTemplate(resource, new DefaultOAuth2ClientContext(accessToken)); | |
String result = template.getForObject(serverRunning.getUrl("/sparklr2/photos/trusted/message"), String.class); | |
// System.err.println(result); | |
assertEquals("Hello, Trusted Client", result); | |
} | |
@Test | |
public void testConnectThroughClientApp() throws Exception { | |
// tonr is a trusted client of sparklr for this resource | |
RestTemplate template = new RestTemplate(); | |
String result = template.getForObject(serverRunning.getUrl("/tonr2/trusted/message"), String.class); | |
// System.err.println(result); | |
assertEquals("{\"message\":\"Hello, Trusted Client\"}", result); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth2.provider; | |
import static org.junit.Assert.assertNull; | |
import java.util.Arrays; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.springframework.security.oauth2.client.test.OAuth2ContextConfiguration; | |
import org.springframework.security.oauth2.client.test.OAuth2ContextSetup; | |
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; | |
import org.springframework.security.oauth2.common.OAuth2AccessToken; | |
import org.springframework.security.oauth2.provider.TestClientCredentialsProvider.ClientCredentials; | |
/** | |
* @author Ryan Heaton | |
* @author Dave Syer | |
*/ | |
@OAuth2ContextConfiguration(ClientCredentials.class) | |
public class TestClientCredentialsProvider { | |
@Rule | |
public ServerRunning serverRunning = ServerRunning.isRunning(); | |
@Rule | |
public OAuth2ContextSetup context = OAuth2ContextSetup.standard(serverRunning); | |
/** | |
* tests the basic provider | |
*/ | |
@Test | |
public void testPostForToken() throws Exception { | |
OAuth2AccessToken token = context.getAccessToken(); | |
assertNull(token.getRefreshToken()); | |
} | |
static class ClientCredentials extends ClientCredentialsResourceDetails { | |
public ClientCredentials(Object target) { | |
setClientId("my-client-with-registered-redirect"); | |
setScope(Arrays.asList("read")); | |
setId(getClientId()); | |
TestClientCredentialsProvider test = (TestClientCredentialsProvider) target; | |
setAccessTokenUri(test.serverRunning.getUrl("/sparklr2/oauth/token")); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth2.provider; | |
import static org.junit.Assert.assertEquals; | |
import static org.junit.Assert.assertNotNull; | |
import static org.junit.Assert.fail; | |
import java.util.Arrays; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.http.ResponseEntity; | |
import org.springframework.security.oauth2.client.UserRedirectRequiredException; | |
import org.springframework.security.oauth2.client.test.BeforeOAuth2Context; | |
import org.springframework.security.oauth2.client.test.OAuth2ContextConfiguration; | |
import org.springframework.security.oauth2.client.test.OAuth2ContextSetup; | |
import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitResourceDetails; | |
import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint; | |
import org.springframework.util.LinkedMultiValueMap; | |
import org.springframework.util.MultiValueMap; | |
/** | |
* @author Ryan Heaton | |
* @author Dave Syer | |
*/ | |
public class TestImplicitProvider { | |
@Rule | |
public ServerRunning serverRunning = ServerRunning.isRunning(); | |
@Rule | |
public OAuth2ContextSetup context = OAuth2ContextSetup.standard(serverRunning); | |
private String cookie; | |
@BeforeOAuth2Context | |
public void loginAndExtractCookie() { | |
MultiValueMap<String, String> formData; | |
formData = new LinkedMultiValueMap<String, String>(); | |
formData.add("j_username", "marissa"); | |
formData.add("j_password", "koala"); | |
String location = "/sparklr2/login.do"; | |
ResponseEntity<Void> result = serverRunning.postForStatus(location, formData); | |
assertEquals(HttpStatus.FOUND, result.getStatusCode()); | |
String cookie = result.getHeaders().getFirst("Set-Cookie"); | |
assertNotNull("Expected cookie in " + result.getHeaders(), cookie); | |
this.cookie = cookie; | |
} | |
@Test(expected = UserRedirectRequiredException.class) | |
@OAuth2ContextConfiguration(resource = AutoApproveImplicit.class, initialize = false) | |
public void testRedirectRequiredForAuthentication() throws Exception { | |
context.getAccessToken(); | |
} | |
@Test | |
@OAuth2ContextConfiguration(resource = AutoApproveImplicit.class, initialize = false) | |
public void testPostForAutomaticApprovalToken() throws Exception { | |
context.getAccessTokenRequest().setCookie(cookie); | |
assertNotNull(context.getAccessToken()); | |
} | |
@Test | |
@OAuth2ContextConfiguration(resource = NonAutoApproveImplicit.class, initialize = false) | |
public void testPostForNonAutomaticApprovalToken() throws Exception { | |
context.getAccessTokenRequest().setCookie(cookie); | |
try { | |
assertNotNull(context.getAccessToken()); | |
fail("Expected UserRedirectRequiredException"); | |
} | |
catch (UserRedirectRequiredException e) { | |
// ignore | |
} | |
// add user approval parameter for the second request | |
context.getAccessTokenRequest().add(AuthorizationEndpoint.USER_OAUTH_APPROVAL, "true"); | |
assertNotNull(context.getAccessToken()); | |
} | |
static class AutoApproveImplicit extends ImplicitResourceDetails { | |
public AutoApproveImplicit(Object target) { | |
super(); | |
setClientId("my-less-trusted-autoapprove-client"); | |
setScope(Arrays.asList("read")); | |
setId(getClientId()); | |
setPreEstablishedRedirectUri("http://anywhere"); | |
TestImplicitProvider test = (TestImplicitProvider) target; | |
setAccessTokenUri(test.serverRunning.getUrl("/sparklr2/oauth/authorize")); | |
setUserAuthorizationUri(test.serverRunning.getUrl("/sparklr2/oauth/authorize")); | |
} | |
} | |
static class NonAutoApproveImplicit extends AutoApproveImplicit { | |
public NonAutoApproveImplicit(Object target) { | |
super(target); | |
setClientId("my-less-trusted-client"); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.tonr; | |
import static org.junit.Assert.assertEquals; | |
import static org.junit.Assert.assertFalse; | |
import static org.junit.Assert.assertNotNull; | |
import static org.junit.Assert.assertTrue; | |
import java.util.Arrays; | |
import java.util.Date; | |
import org.junit.Before; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.springframework.security.authentication.TestingAuthenticationToken; | |
import org.springframework.security.core.context.SecurityContextHolder; | |
import org.springframework.security.core.context.SecurityContextImpl; | |
import org.springframework.security.oauth2.client.OAuth2RestTemplate; | |
import org.springframework.security.oauth2.client.context.DefaultOAuth2ClientContext; | |
import org.springframework.security.oauth2.client.token.AccessTokenRequest; | |
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest; | |
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; | |
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; | |
import org.springframework.security.oauth2.common.OAuth2AccessToken; | |
/** | |
* @author Dave Syer | |
*/ | |
public class TestRefreshTokenGrant { | |
@Rule | |
public ServerRunning serverRunning = ServerRunning.isRunning(); | |
private OAuth2AccessToken existingToken; | |
private ResourceOwnerPasswordResourceDetails resource; | |
@Before | |
public void setup() { | |
resource = new ResourceOwnerPasswordResourceDetails(); | |
resource.setAccessTokenUri(serverRunning.getUrl("/sparklr2/oauth/token")); | |
resource.setClientId("my-trusted-client"); | |
resource.setId("sparklr"); | |
resource.setScope(Arrays.asList("trust")); | |
resource.setUsername("marissa"); | |
resource.setPassword("koala"); | |
OAuth2RestTemplate template = new OAuth2RestTemplate(resource); | |
existingToken = template.getAccessToken(); | |
((DefaultOAuth2AccessToken)existingToken).setExpiration(new Date(0L)); | |
SecurityContextImpl securityContext = new SecurityContextImpl(); | |
securityContext.setAuthentication(new TestingAuthenticationToken("marissa", "koala", "ROLE_USER")); | |
SecurityContextHolder.setContext(securityContext); | |
} | |
@Test | |
public void testConnectDirectlyToResourceServer() throws Exception { | |
assertNotNull(existingToken.getRefreshToken()); | |
// It won't be expired on the server, but we can force the client to refresh it | |
assertTrue(existingToken.isExpired()); | |
AccessTokenRequest request = new DefaultAccessTokenRequest(); | |
request.setExistingToken(existingToken); | |
OAuth2RestTemplate template = new OAuth2RestTemplate(resource, new DefaultOAuth2ClientContext(request)); | |
String result = template.getForObject(serverRunning.getUrl("/sparklr2/photos/user/message"), String.class); | |
assertEquals("Hello, Trusted User marissa", result); | |
assertFalse("Tokens match so there was no refresh", existingToken.equals(template.getAccessToken())); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth2.provider; | |
import static org.junit.Assert.assertEquals; | |
import static org.junit.Assert.assertFalse; | |
import static org.junit.Assert.assertNotNull; | |
import java.util.Map; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.springframework.http.HttpHeaders; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.http.ResponseEntity; | |
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; | |
import org.springframework.security.oauth2.common.OAuth2AccessToken; | |
import org.springframework.util.LinkedMultiValueMap; | |
import org.springframework.util.MultiValueMap; | |
/** | |
* @author Ryan Heaton | |
* @author Dave Syer | |
*/ | |
public class TestRefreshTokenSupport { | |
@Rule | |
public ServerRunning serverRunning = ServerRunning.isRunning(); | |
/** | |
* tests a happy-day flow of the refresh token provider. | |
*/ | |
@Test | |
public void testHappyDay() throws Exception { | |
OAuth2AccessToken accessToken = getAccessToken("read", "my-trusted-client"); | |
// now use the refresh token to get a new access token. | |
assertNotNull(accessToken.getRefreshToken()); | |
OAuth2AccessToken newAccessToken = refreshAccessToken(accessToken.getRefreshToken().getValue()); | |
assertFalse(newAccessToken.getValue().equals(accessToken.getValue())); | |
// make sure the new access token can be used. | |
verifyTokenResponse(newAccessToken.getValue(), HttpStatus.OK); | |
// make sure the old access token isn't valid anymore. | |
verifyTokenResponse(accessToken.getValue(), HttpStatus.UNAUTHORIZED); | |
} | |
private void verifyTokenResponse(String accessToken, HttpStatus status) { | |
HttpHeaders headers = new HttpHeaders(); | |
headers.set("Authorization", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, accessToken)); | |
assertEquals(status, serverRunning.getStatusCode("/sparklr2/photos?format=json", headers)); | |
} | |
private OAuth2AccessToken refreshAccessToken(String refreshToken) { | |
MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>(); | |
formData.add("grant_type", "refresh_token"); | |
formData.add("client_id", "my-trusted-client"); | |
formData.add("refresh_token", refreshToken); | |
@SuppressWarnings("rawtypes") | |
ResponseEntity<Map> response = serverRunning.postForMap("/sparklr2/oauth/token", formData); | |
assertEquals(HttpStatus.OK, response.getStatusCode()); | |
assertEquals("no-store", response.getHeaders().getFirst("Cache-Control")); | |
@SuppressWarnings("unchecked") | |
OAuth2AccessToken newAccessToken = DefaultOAuth2AccessToken.valueOf(response.getBody()); | |
return newAccessToken; | |
} | |
private OAuth2AccessToken getAccessToken(String scope, String clientId) throws Exception { | |
MultiValueMap<String, String> formData = getTokenFormData(scope, clientId); | |
@SuppressWarnings("rawtypes") | |
ResponseEntity<Map> response = serverRunning.postForMap("/sparklr2/oauth/token", formData); | |
assertEquals(HttpStatus.OK, response.getStatusCode()); | |
assertEquals("no-store", response.getHeaders().getFirst("Cache-Control")); | |
@SuppressWarnings("unchecked") | |
OAuth2AccessToken accessToken = DefaultOAuth2AccessToken.valueOf(response.getBody()); | |
return accessToken; | |
} | |
private MultiValueMap<String, String> getTokenFormData(String scope, String clientId) { | |
MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>(); | |
formData.add("grant_type", "password"); | |
if (clientId != null) { | |
formData.add("client_id", clientId); | |
} | |
formData.add("scope", scope); | |
formData.add("username", "marissa"); | |
formData.add("password", "koala"); | |
return formData; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth.examples.tonr; | |
import static org.junit.Assert.assertEquals; | |
import java.util.Arrays; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.springframework.security.oauth2.client.OAuth2RestTemplate; | |
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; | |
/** | |
* @author Dave Syer | |
*/ | |
public class TestResourceOwnerPasswordGrant { | |
@Rule | |
public ServerRunning serverRunning = ServerRunning.isRunning(); | |
@Test | |
public void testConnectDirectlyToResourceServer() throws Exception { | |
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails(); | |
resource.setAccessTokenUri(serverRunning.getUrl("/sparklr2/oauth/token")); | |
resource.setClientId("my-trusted-client"); | |
resource.setId("sparklr"); | |
resource.setScope(Arrays.asList("trust")); | |
resource.setUsername("marissa"); | |
resource.setPassword("koala"); | |
OAuth2RestTemplate template = new OAuth2RestTemplate(resource); | |
String result = template.getForObject(serverRunning.getUrl("/sparklr2/photos/user/message"), String.class); | |
// System.err.println(result); | |
assertEquals("Hello, Trusted User marissa", result); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.springframework.security.oauth2.provider; | |
import static org.junit.Assert.assertEquals; | |
import static org.junit.Assert.assertTrue; | |
import static org.junit.Assert.fail; | |
import java.io.IOException; | |
import java.util.Arrays; | |
import java.util.List; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.springframework.http.HttpHeaders; | |
import org.springframework.http.HttpStatus; | |
import org.springframework.http.MediaType; | |
import org.springframework.http.ResponseEntity; | |
import org.springframework.http.client.ClientHttpResponse; | |
import org.springframework.security.oauth2.client.test.BeforeOAuth2Context; | |
import org.springframework.security.oauth2.client.test.OAuth2ContextConfiguration; | |
import org.springframework.security.oauth2.client.test.OAuth2ContextSetup; | |
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; | |
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; | |
import org.springframework.security.oauth2.common.AuthenticationScheme; | |
import org.springframework.security.oauth2.common.OAuth2AccessToken; | |
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; | |
import org.springframework.web.client.DefaultResponseErrorHandler; | |
import org.springframework.web.client.HttpClientErrorException; | |
import org.springframework.web.client.ResponseErrorHandler; | |
import org.springframework.web.client.ResponseExtractor; | |
/** | |
* @author Ryan Heaton | |
* @author Dave Syer | |
*/ | |
public class TestResourceOwnerPasswordProvider { | |
@Rule | |
public ServerRunning serverRunning = ServerRunning.isRunning(); | |
@Rule | |
public OAuth2ContextSetup context = OAuth2ContextSetup.standard(serverRunning); | |
private ClientHttpResponse tokenEndpointResponse; | |
@BeforeOAuth2Context | |
public void setupAccessTokenProvider() { | |
ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider() { | |
private ResponseExtractor<OAuth2AccessToken> extractor = super.getResponseExtractor(); | |
private ResponseErrorHandler errorHandler = super.getResponseErrorHandler(); | |
@Override | |
protected ResponseErrorHandler getResponseErrorHandler() { | |
return new DefaultResponseErrorHandler() { | |
public void handleError(ClientHttpResponse response) throws IOException { | |
response.getHeaders(); | |
response.getStatusCode(); | |
tokenEndpointResponse = response; | |
errorHandler.handleError(response); | |
} | |
}; | |
} | |
@Override | |
protected ResponseExtractor<OAuth2AccessToken> getResponseExtractor() { | |
return new ResponseExtractor<OAuth2AccessToken>() { | |
public OAuth2AccessToken extractData(ClientHttpResponse response) throws IOException { | |
response.getHeaders(); | |
response.getStatusCode(); | |
tokenEndpointResponse = response; | |
return extractor.extractData(response); | |
} | |
}; | |
} | |
}; | |
context.setAccessTokenProvider(accessTokenProvider); | |
} | |
@Test | |
public void testUnauthenticated() throws Exception { | |
// first make sure the resource is actually protected. | |
assertEquals(HttpStatus.UNAUTHORIZED, serverRunning.getStatusCode("/sparklr2/photos?format=json")); | |
} | |
@Test | |
public void testUnauthenticatedErrorMessage() throws Exception { | |
HttpHeaders headers = new HttpHeaders(); | |
ResponseEntity<Void> response = serverRunning.getForResponse("/sparklr2/photos?format=json", headers); | |
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); | |
String authenticate = response.getHeaders().getFirst("WWW-Authenticate"); | |
assertTrue("Wrong header: " + authenticate, authenticate.contains("error=\"unauthorized\"")); | |
} | |
@Test | |
public void testInvalidTokenErrorMessage() throws Exception { | |
HttpHeaders headers = new HttpHeaders(); | |
headers.set("Authorization", "Bearer FOO"); | |
ResponseEntity<Void> response = serverRunning.getForResponse("/sparklr2/photos?format=json", headers); | |
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); | |
String authenticate = response.getHeaders().getFirst("WWW-Authenticate"); | |
assertTrue("Wrong header: " + authenticate, authenticate.contains("error=\"invalid_token\"")); | |
} | |
@Test | |
@OAuth2ContextConfiguration(ResourceOwner.class) | |
public void testTokenObtainedWithHeaderAuthentication() throws Exception { | |
assertEquals(HttpStatus.OK, serverRunning.getStatusCode("/sparklr2/photos?format=json")); | |
} | |
@Test | |
@OAuth2ContextConfiguration(ResourceOwnerQuery.class) | |
public void testTokenObtainedWithQueryAuthentication() throws Exception { | |
assertEquals(HttpStatus.OK, serverRunning.getStatusCode("/sparklr2/photos?format=json")); | |
} | |
@Test | |
@OAuth2ContextConfiguration(resource = ResourceOwnerNoSecretProvided.class, initialize = false) | |
public void testTokenNotGrantedIfSecretNotProvided() throws Exception { | |
try { | |
context.getAccessToken(); | |
} | |
catch (HttpClientErrorException e) { | |
assertEquals(HttpStatus.UNAUTHORIZED, e.getStatusCode()); | |
List<String> values = tokenEndpointResponse.getHeaders().get("WWW-Authenticate"); | |
assertEquals(1, values.size()); | |
assertEquals("Bearer realm=\"sparklr2\", error=\"unauthorized\", error_description=\"Bad credentials\"", values.get(0)); | |
} | |
} | |
@Test | |
@OAuth2ContextConfiguration(ResourceOwnerSecretProvidedInForm.class) | |
public void testSecretProvidedInForm() throws Exception { | |
assertEquals(HttpStatus.OK, serverRunning.getStatusCode("/sparklr2/photos?format=json")); | |
} | |
@Test | |
@OAuth2ContextConfiguration(ResourceOwnerSecretProvided.class) | |
public void testSecretProvidedInHeader() throws Exception { | |
assertEquals(HttpStatus.OK, serverRunning.getStatusCode("/sparklr2/photos?format=json")); | |
} | |
@Test | |
@OAuth2ContextConfiguration(resource = InvalidGrantType.class, initialize = false) | |
public void testInvalidGrantType() throws Exception { | |
// The error comes back as additional information because OAuth2AccessToken is so extensible! | |
try { | |
context.getAccessToken(); | |
} | |
catch (OAuth2Exception e) { | |
assertEquals("invalid_grant", e.getOAuth2ErrorCode()); | |
} | |
assertEquals(HttpStatus.BAD_REQUEST, tokenEndpointResponse.getStatusCode()); | |
List<String> newCookies = tokenEndpointResponse.getHeaders().get("Set-Cookie"); | |
if (newCookies != null && !newCookies.isEmpty()) { | |
fail("No cookies should be set. Found: " + newCookies.get(0) + "."); | |
} | |
} | |
@Test | |
public void testUserMessageIsProtectedResource() throws Exception { | |
assertEquals(HttpStatus.UNAUTHORIZED, serverRunning.getStatusCode("/sparklr2/photos/user/message")); | |
} | |
@Test | |
@OAuth2ContextConfiguration(ResourceOwnerWithTrustedClient.class) | |
public void testClientRoleBasedSecurity() throws Exception { | |
assertEquals(HttpStatus.OK, serverRunning.getStatusCode("/sparklr2/photos/user/message")); | |
} | |
/** | |
* tests a happy-day flow of the native application provider. | |
*/ | |
@Test | |
@OAuth2ContextConfiguration(ResourceOwnerWithTrustedClient.class) | |
public void testUnsupportedMediaType() throws Exception { | |
HttpHeaders headers = new HttpHeaders(); | |
headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML)); | |
// Oddly enough this passes - the payload is a String so the message converter thinks it can handle it | |
// the caller will get a surprise when he finds that the response is not actually XML, but that's a different | |
// story. | |
assertEquals(HttpStatus.OK, serverRunning.getStatusCode("/sparklr2/photos/user/message", headers)); | |
} | |
/** | |
* tests that we get the correct error response if the media type is unacceptable. | |
*/ | |
@Test | |
public void testUnsupportedMediaTypeWithInvalidToken() throws Exception { | |
HttpHeaders headers = new HttpHeaders(); | |
headers.set("Authorization", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, "FOO")); | |
headers.setAccept(Arrays.asList(MediaType.valueOf("text/foo"))); | |
assertEquals(HttpStatus.NOT_ACCEPTABLE, serverRunning.getStatusCode("/sparklr2/photos/user/message", headers)); | |
} | |
static class ResourceOwner extends ResourceOwnerPasswordResourceDetails { | |
public ResourceOwner(Object target) { | |
setClientId("my-trusted-client"); | |
setScope(Arrays.asList("read")); | |
setId(getClientId()); | |
setUsername("marissa"); | |
setPassword("koala"); | |
TestResourceOwnerPasswordProvider test = (TestResourceOwnerPasswordProvider) target; | |
setAccessTokenUri(test.serverRunning.getUrl("/sparklr2/oauth/token")); | |
} | |
} | |
static class ResourceOwnerQuery extends ResourceOwner { | |
public ResourceOwnerQuery(Object target) { | |
super(target); | |
setAuthenticationScheme(AuthenticationScheme.query); | |
} | |
} | |
static class ResourceOwnerNoSecretProvided extends ResourceOwner { | |
public ResourceOwnerNoSecretProvided(Object target) { | |
super(target); | |
setClientId("my-trusted-client-with-secret"); | |
} | |
} | |
static class ResourceOwnerSecretProvided extends ResourceOwner { | |
public ResourceOwnerSecretProvided(Object target) { | |
super(target); | |
setClientId("my-trusted-client-with-secret"); | |
setClientSecret("somesecret"); | |
} | |
} | |
static class ResourceOwnerSecretProvidedInForm extends ResourceOwnerSecretProvided { | |
public ResourceOwnerSecretProvidedInForm(Object target) { | |
super(target); | |
setAuthenticationScheme(AuthenticationScheme.form); | |
} | |
} | |
static class InvalidGrantType extends ResourceOwner { | |
public InvalidGrantType(Object target) { | |
super(target); | |
setClientId("my-untrusted-client-with-registered-redirect"); | |
} | |
} | |
static class ResourceOwnerWithTrustedClient extends ResourceOwner { | |
public ResourceOwnerWithTrustedClient(Object target) { | |
super(target); | |
setClientId("my-trusted-client"); | |
setScope(Arrays.asList("trust")); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="ISO-8859-1"?> | |
<!DOCTYPE web-app PUBLIC | |
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" | |
"http://java.sun.com/dtd/web-app_2_3.dtd"> | |
<web-app> | |
<filter> | |
<filter-name>clientCredentialsTokenEndpointFilter</filter-name> | |
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> | |
<init-param> | |
<param-name>contextAttribute</param-name> | |
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring</param-value> | |
</init-param> | |
</filter> | |
<filter> | |
<filter-name>springSecurityFilterChain</filter-name> | |
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> | |
<init-param> | |
<param-name>contextAttribute</param-name> | |
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring</param-value> | |
</init-param> | |
</filter> | |
<filter> | |
<filter-name>oauth2EndpointUrlFilter</filter-name> | |
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> | |
<init-param> | |
<param-name>contextAttribute</param-name> | |
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring</param-value> | |
</init-param> | |
</filter> | |
<filter-mapping> | |
<filter-name>clientCredentialsTokenEndpointFilter</filter-name> | |
<url-pattern>/token</url-pattern> | |
</filter-mapping> | |
<filter-mapping> | |
<filter-name>oauth2EndpointUrlFilter</filter-name> | |
<url-pattern>/authorize</url-pattern> | |
</filter-mapping> | |
<filter-mapping> | |
<filter-name>oauth2EndpointUrlFilter</filter-name> | |
<url-pattern>/token</url-pattern> | |
</filter-mapping> | |
<filter-mapping> | |
<filter-name>springSecurityFilterChain</filter-name> | |
<url-pattern>/*</url-pattern> | |
</filter-mapping> | |
<servlet> | |
<servlet-name>spring</servlet-name> | |
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> | |
<load-on-startup>1</load-on-startup> | |
</servlet> | |
<servlet-mapping> | |
<servlet-name>spring</servlet-name> | |
<url-pattern>/</url-pattern> | |
</servlet-mapping> | |
</web-app> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="ISO-8859-1"?> | |
<!DOCTYPE web-app PUBLIC | |
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" | |
"http://java.sun.com/dtd/web-app_2_3.dtd"> | |
<web-app> | |
<filter> | |
<filter-name>springSecurityFilterChain</filter-name> | |
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> | |
<init-param> | |
<param-name>contextAttribute</param-name> | |
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring</param-value> | |
</init-param> | |
</filter> | |
<filter-mapping> | |
<filter-name>springSecurityFilterChain</filter-name> | |
<url-pattern>/*</url-pattern> | |
</filter-mapping> | |
<servlet> | |
<servlet-name>spring</servlet-name> | |
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> | |
<load-on-startup>1</load-on-startup> | |
</servlet> | |
<servlet-mapping> | |
<servlet-name>spring</servlet-name> | |
<url-pattern>/</url-pattern> | |
</servlet-mapping> | |
</web-app> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment