Commit ac126b9b authored by Matija Obreza's avatar Matija Obreza

OAuth2 working with JdbcTokenStore

parent b1d41b00
......@@ -40,7 +40,7 @@
<aspectj.version>1.7.2</aspectj.version>
<spring.framework.version>3.2.4.RELEASE</spring.framework.version>
<spring.security.version>3.1.4.RELEASE</spring.security.version>
<spring.security.oauth2.version>1.0.0.RELEASE</spring.security.oauth2.version>
<spring.security.oauth2.version>1.0.5.RELEASE</spring.security.oauth2.version>
<spring.data.core.version>1.5.1.RELEASE</spring.data.core.version>
<spring.data.jpa.version>1.3.2.RELEASE</spring.data.jpa.version>
......
/**
* Copyright 2013 Global Crop Diversity Trust
*
* 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.genesys2.server.listener.sample;
import org.genesys2.server.model.oauth.OAuthClientDetails;
import org.genesys2.server.service.OAuth2ClientDetailsService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CreateClientDetailsListener implements InitializingBean {
@Autowired
private OAuth2ClientDetailsService clientDetailsService;
public static final String DEFAULT_CLIENT_DETAILS_ID = "CropHub";
@Override
public void afterPropertiesSet() throws Exception {
if (!clientDetailsService.exists(DEFAULT_CLIENT_DETAILS_ID)) {
createClientDetails();
}
}
private void createClientDetails() {
OAuthClientDetails clientDetails = new OAuthClientDetails();
clientDetails.setClientId(DEFAULT_CLIENT_DETAILS_ID);
clientDetails.setClientSecret("0xcafebabe");
clientDetails.setScope("read,write");
clientDetails.setAuthorizedGrantTypes("authorization_code");
clientDetails.setAuthorities("ROLE_USER");
clientDetailsService.addClientDetails(clientDetails);
}
}
/**
* Copyright 2013 Global Crop Diversity Trust
*
* 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.genesys2.server.listener.sample;
import java.util.HashSet;
import java.util.Set;
import org.genesys2.server.listener.RunAsAdminListener;
import org.genesys2.server.model.UserRole;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CreateUserListener extends RunAsAdminListener {
@Autowired
private UserService userService;
@Override
public void init() throws Exception {
_logger.info("Checking for user account");
if (!userService.exists("user@example.com")) {
User user = new User();
user.setEmail("user@example.com");
user.setPassword("user");
user.setName("Sample User");
Set<UserRole> userRoles = new HashSet<UserRole>();
userRoles.add(UserRole.USER);
user.setRoles(userRoles);
// user.setUserGroups(userGroupService.getUserGroupList());
userService.addUser(user);
_logger.info("Sample user has been added successfully");
}
}
}
/**
* Copyright 2013 Global Crop Diversity Trust
*
* 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.genesys2.server.service;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationService;
public interface OAuth2ClientDetailsService extends ClientDetailsService, ClientRegistrationService{
boolean exists(String clientId);
}
package org.genesys2.server.service.impl;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.model.oauth.OAuthClientDetails;
import org.genesys2.server.persistence.domain.OAuthClientDetailsPersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.ClientAlreadyExistsException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationService;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.util.Assert;
public class JdbcClientDetailsService implements ClientDetailsService, ClientRegistrationService {
private static final Log logger = LogFactory.getLog(JdbcClientDetailsService.class);
@Autowired
private OAuthClientDetailsPersistence clientDetailsPersistence;
private PasswordEncoder passwordEncoder = NoOpPasswordEncoder.getInstance();
public JdbcClientDetailsService(DataSource dataSource) {
Assert.notNull(dataSource, "DataSource required");
}
/**
* @param passwordEncoder the password encoder to set
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
ClientDetails details;
try {
details = clientDetailsPersistence.findOne(clientId);
}
catch (EmptyResultDataAccessException e) {
throw new NoSuchClientException("No client with requested id: " + clientId);
}
return details;
}
public void addClientDetails(ClientDetails clientDetails) throws ClientAlreadyExistsException {
try {
clientDetailsPersistence.save((OAuthClientDetails)clientDetails);
}
catch (DuplicateKeyException e) {
throw new ClientAlreadyExistsException("Client already exists: " + clientDetails.getClientId(), e);
}
}
public void updateClientDetails(ClientDetails clientDetails) throws NoSuchClientException {
if(clientDetailsPersistence.exists(clientDetails.getClientId())) {
clientDetailsPersistence.save((OAuthClientDetails) clientDetails);
}else{
throw new NoSuchClientException("No client found with id = " + clientDetails.getClientId());
}
}
public void updateClientSecret(String clientId, String secret) throws NoSuchClientException {
if(clientDetailsPersistence.exists(clientId)) {
OAuthClientDetails details = clientDetailsPersistence.findOne(clientId);
details.setClientSecret(passwordEncoder.encode(secret));
clientDetailsPersistence.save(details);
}else{
throw new NoSuchClientException("No client found with id = " + clientId);
}
}
public void removeClientDetails(String clientId) throws NoSuchClientException {
if(clientDetailsPersistence.exists(clientId)) {
clientDetailsPersistence.delete(clientId);
}else{
throw new NoSuchClientException("No client found with id = " + clientId);
}
}
public List<ClientDetails> listClientDetails() {
return new ArrayList<ClientDetails>(clientDetailsPersistence.findAll());
}
public boolean isExists(String id) {
return clientDetailsPersistence.exists(id);
}
}
/**
* Copyright 2013 Global Crop Diversity Trust
*
* 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.genesys2.server.service.impl;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.model.oauth.OAuthClientDetails;
import org.genesys2.server.persistence.domain.OAuthClientDetailsPersistence;
import org.genesys2.server.service.OAuth2ClientDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.ClientAlreadyExistsException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("clientDetails")
@Transactional
public class OAuth2ClientDetailsServiceImpl implements OAuth2ClientDetailsService {
private static final Log logger = LogFactory.getLog(OAuth2ClientDetailsServiceImpl.class);
@Autowired
private OAuthClientDetailsPersistence clientDetailsPersistence;
private PasswordEncoder passwordEncoder = NoOpPasswordEncoder.getInstance();
public OAuth2ClientDetailsServiceImpl() {
}
/**
* @param passwordEncoder
* the password encoder to set
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
logger.info("loadClientByClientId: " + clientId);
ClientDetails details;
try {
details = clientDetailsPersistence.findOne(clientId);
} catch (EmptyResultDataAccessException e) {
throw new NoSuchClientException("No client with requested id: " + clientId);
}
return details;
}
public void addClientDetails(ClientDetails clientDetails) throws ClientAlreadyExistsException {
try {
clientDetailsPersistence.save((OAuthClientDetails) clientDetails);
} catch (DuplicateKeyException e) {
throw new ClientAlreadyExistsException("Client already exists: " + clientDetails.getClientId(), e);
}
}
public void updateClientDetails(ClientDetails clientDetails) throws NoSuchClientException {
if (clientDetailsPersistence.exists(clientDetails.getClientId())) {
clientDetailsPersistence.save((OAuthClientDetails) clientDetails);
} else {
throw new NoSuchClientException("No client found with id = " + clientDetails.getClientId());
}
}
public void updateClientSecret(String clientId, String secret) throws NoSuchClientException {
if (clientDetailsPersistence.exists(clientId)) {
OAuthClientDetails details = clientDetailsPersistence.findOne(clientId);
details.setClientSecret(passwordEncoder.encode(secret));
clientDetailsPersistence.save(details);
} else {
throw new NoSuchClientException("No client found with id = " + clientId);
}
}
public void removeClientDetails(String clientId) throws NoSuchClientException {
logger.info("removeClientDetails: " + clientId);
if (clientDetailsPersistence.exists(clientId)) {
clientDetailsPersistence.delete(clientId);
} else {
throw new NoSuchClientException("No client found with id = " + clientId);
}
}
public List<ClientDetails> listClientDetails() {
return new ArrayList<ClientDetails>(clientDetailsPersistence.findAll());
}
public boolean exists(String id) {
logger.info("isExist: " + id);
return clientDetailsPersistence.exists(id);
}
}
/**
* Copyright 2013 Global Crop Diversity Trust
*
* 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.genesys2.server.servlet.controller;
import java.util.Map;
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.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
@Controller
@SessionAttributes("authorizationRequest")
public class ConfirmationController extends BaseController {
@Autowired
protected ClientDetailsService clientDetailsService;
@RequestMapping("/oauth/confirm_access")
public String getAccessConfirmation(ModelMap model) throws Exception {
AuthorizationRequest clientAuth = (AuthorizationRequest) model.remove("authorizationRequest");
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
model.put("auth_request", clientAuth);
model.put("client", client);
return "/oauth/confirm";
}
@RequestMapping(value = "/oauth/oob", params = { "code" })
public String showCode(ModelMap model, @RequestParam(value = "code") String code) throws Exception {
model.addAttribute("code", code);
return "/oauth/showcode";
}
@RequestMapping(value = "/oauth/oob", params = { "error" })
public String showError(ModelMap model, @RequestParam("error") String error) throws Exception {
model.addAttribute("error", error);
return "/oauth/showerror";
}
@RequestMapping("/oauth/error")
@ResponseBody
public Map<String, Object> handleError(Map<String, Object> model) throws Exception {
model.put("message", "There was a problem with the OAuth2 protocol");
return model;
}
}
package org.genesys2.server.servlet.controller.rest;
import org.genesys2.server.ServiceEndpoints;
import org.genesys2.server.exception.AuthorizationException;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value = "/api/v0")
public class UserController {
@Autowired
protected UserService userService;
/**
* Returns logged in user's profile
*
* @return - user's profile
*/
@RequestMapping(value = ServiceEndpoints.ME, method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
Object getProfile() throws AuthorizationException {
String authenticationName = SecurityContextHolder.getContext().getAuthentication().getName();
System.err.println(">>>" + authenticationName);
User user = userService.getUserByEmail(authenticationName);
return user;
}
}
\ No newline at end of file
......@@ -234,3 +234,15 @@ blurp.admin-no-blurp-here=No blurp here.
blurp.blurp-title=Blurp title
blurp.blurp-body=Blurp contents
blurp.update-blurp=Save blurp
oauth2.confirm-request=Confirm access
oauth2.confirm-client=You, <b>{0}</b>, hereby authorize <b>{1}</b> to access your protected resources.
oauth2.button-approve=Yes, allow access
oauth2.button-deny=No, deny access
oauth2.authorization-code=Authorization code
oauth2.authorization-code-instructions=Copy this authorization code:
oauth2.access-denied=Access denied
oauth2.access-denied-text=You have denied access to your resources.
\ No newline at end of file
<?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"
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/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<bean id="clientDetails" class="org.genesys2.server.service.impl.JdbcClientDetailsService">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<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.JdbcTokenStore">
<constructor-arg ref="dataSource" />
</bean>
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" >
<oauth:authorization-code />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" resource-id="crophub_oauth_server" token-services-ref="tokenServices" />
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
</beans>
<?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"
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/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<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.JdbcTokenStore">
<constructor-arg ref="dataSource" />
</bean>
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<sec:authentication-manager id="clientAuthenticationManager">
<sec:authentication-provider user-service-ref="clientDetailsUserService" />
</sec:authentication-manager>
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:authorization-code />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" resource-id="crophub_oauth_server" token-services-ref="tokenServices" />
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
<sec:http pattern="/oauth/token" authentication-manager-ref="clientAuthenticationManager" create-session="stateless" xmlns="http://www.springframework.org/schema/security">
<sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<sec:anonymous enabled="false" />
<sec:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request parameters -->
<sec:custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
</sec: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. -->
<sec:http pattern="/oauth/(users|clients)/.*" authentication-manager-ref="clientAuthenticationManager" request-matcher="regex" create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint" use-expressions="true" xmlns="http://www.springframework.org/schema/security">
<sec:anonymous enabled="false" />
<sec:intercept-url pattern="/oauth/users/([^/].*?)/tokens/.*" access="#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('write')" method="DELETE" />
<sec:intercept-url pattern="/oauth/users/.*" access="#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('read')" method="GET" />
<sec:intercept-url pattern="/oauth/clients/.*" access="#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')" method="GET" />
<sec:intercept-url pattern="/**" access="denyAll()" />
<sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
<sec:expression-handler ref="oauthWebExpressionHandler" />
</sec:http>
<sec:http pattern="/api/v0/.*" authentication-manager-ref="clientAuthenticationManager" request-matcher="regex" create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint" use-expressions="true" xmlns="http://www.springframework.org/schema/security">
<sec:anonymous enabled="false" />
<sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
<sec:expression-handler ref="oauthWebExpressionHandler" />
</sec:http>
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="genesys2" />
</bean>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="genesys2/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>
</beans>
......@@ -14,48 +14,49 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi=