I have problem with j_spring_security_switch_user, as I only can switch between users with the role ROLE_SWITCH_USER.
Can I change it so it can switch to users with the ROLE_USER from a user with the role ROLE_SWITCH_USER?
I got it fixed by:
Create file MySwichUserFilter.groovy:
class MySwichUserFilter extends SwitchUserFilter {
protected Authentication attemptSwitchUser(HttpServletRequest request) throws AuthenticationException {
Authentication switchTo = super.attemptSwitchUser(request);
SecurityContextHolder.getContext().getAuthentication();
return switchTo;
}
}
Correct the resources.groovy
beans = {
...
switchUserProcessingFilter(MySwichUserFilter){
userDetailsService = ref('userDetailsService')
switchUserUrl = "/j_spring_security_switch_user"
exitUserUrl = "/j_spring_security_exit_user"
targetUrl = conf.successHandler.defaultTargetUrl
}
...
}
Related
I use spring-security-oauth2-authorization-server (v. 0.2.0) for implement my authorization-server.
I would like user roles to be in the token, is it possible to add them? like?
Thanks
I solved the problem by adding the following bean
#Bean
OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
return context -> {
if (context.getTokenType() == OAuth2TokenType.ACCESS_TOKEN.getValue()) {
Authentication principal = context.getPrincipal();
Set<String> authorities = principal.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
context.getClaims().claim("user-authorities", authorities);
}
};
}
It should be == check with ACCESS_TOKEN not the value.
#Bean
OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
return context -> {
if (context.getTokenType() == OAuth2TokenType.ACCESS_TOKEN) {
Authentication principal = context.getPrincipal();
Set<String> authorities = principal.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
context.getClaims().claim("user-authorities", authorities);
}
};
}
I have followed the following links to try and test OAuth2 #PreAuthorise(hasAnyRole('ADMIN', 'TEST') for example but I can't any of the tests to pass or even authenticate.
When I try to access the end point with admin (or any role) it will never authenticate properly. Am I missing something obvious, it seems I have everything just as it is in the examples. I have also tried another alternative to the WithSecurityContext Factory with OAuth Specific Authentication and still no luck. Any help would be appreciated.
https://stackoverflow.com/a/31679649/2594130
and
http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test
My Controller I'm testing
#RestController
#RequestMapping("/bookmark/")
public class GroupBookmarkController {
#Autowired
BookmarkService bookmarkService;
/**
* Get list of all bookmarks
*/
#RequestMapping(value = "{groupId}", method = RequestMethod.GET)
#PreAuthorize("hasAnyRole(['ADMIN', 'USER'])")
public ResponseEntity<List<Bookmark>> listAllGroupBookmarks(#PathVariable("groupId") String groupId) throws BookmarkNotFoundException {
List<Bookmark> bookmarks = bookmarkService.findAllBookmarksByGroupId(groupId);
return new ResponseEntity<>(bookmarks, HttpStatus.OK);
}
...
}
My Test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = BookmarkServiceApplication.class)
#WebAppConfiguration
public class BookmarkServiceApplicationTests {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void loadData() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.apply(springSecurity())
.alwaysDo(print())
.build();
}
#Test
#WithMockCustomUser(username = "test")
public void getBookmarkAuthorised() throws Exception {
mockMvc.perform(get("/bookmark/nvjdbngkjlsdfngkjlfdsnlkgsd"))
.andExpect(status().is(HttpStatus.SC_OK));
// always 401 here
}
}
My BookmarkServiceApplication
#SpringBootApplication
#EnableResourceServer
public class BookmarkServiceApplication {
public static void main(String[] args) {
SpringApplication.run(BookmarkServiceApplication.class, args);
}
}
My WithSecurityContextFactory
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {
#Override
public SecurityContext createSecurityContext(WithMockCustomUser customUser) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
UserDetails principal = new User(customUser.username(), "password", true, true, true, true, grantedAuthorities);
Authentication authentication = new UsernamePasswordAuthenticationToken(
principal, principal.getPassword(), principal.getAuthorities());
context.setAuthentication(authentication);
return context;
}
}
My WithSecurityContext Annotation
#Retention(RetentionPolicy.RUNTIME)
#WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public #interface WithMockCustomUser {
String username() default "user";
String name() default "Test User";
}
As per #RobWinch 's reply
Hi #RobWinch I've tried you suggestion with the stateless flag, this helped with part of the answer. However in your reply to this question [Spring OAuth and Boot Integration Test] (https://stackoverflow.com/a/31679649/2594130) you mention
You no longer need to worry about running in stateless mode or not
Why is it that I need to still add the stateless false, is this a bug or are we using it slightly differently?
The other thing I needed to do to get this to work was adding OAuth2Request and OAuth2Authentication to the WithSecurityContextFactory as you can see in the following
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockOAuthUser> {
#Override
public SecurityContext createSecurityContext(WithMockOAuthUser withClient) {
// Get the username
String username = withClient.username();
if (username == null) {
throw new IllegalArgumentException("Username cannot be null");
}
// Get the user roles
List<GrantedAuthority> authorities = new ArrayList<>();
for (String role : withClient.roles()) {
if (role.startsWith("ROLE_")) {
throw new IllegalArgumentException("roles cannot start with ROLE_ Got " + role);
}
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
}
// Get the client id
String clientId = withClient.clientId();
// get the oauth scopes
String[] scopes = withClient.scope();
Set<String> scopeCollection = Sets.newSet(scopes);
// Create the UsernamePasswordAuthenticationToken
User principal = new User(username, withClient.password(), true, true, true, true, authorities);
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(),
principal.getAuthorities());
// Create the authorization request and OAuth2Authentication object
OAuth2Request authRequest = new OAuth2Request(null, clientId, null, true, scopeCollection, null, null, null,
null);
OAuth2Authentication oAuth = new OAuth2Authentication(authRequest, authentication);
// Add the OAuth2Authentication object to the security context
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(oAuth);
return context;
}
}
The problem is that OAuth2AuthenticationProcessingFilter will clear the SecurityContext if it is marked as stateless. To workaround this configure it to allow the state to be populated externally (i.e. stateless = false).
to add some more infos how to set stateless to false:
in your ResourceServerConfigurerAdapter do the following:
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.stateless(false);
}
which worked for me.
I want to redirect a user upon a successful login based on his roles. I am following this example but the method never executes(print does not happen).
My resources.groovy:
beans = {
authenticationSuccessHandler(UrlRedirectEventListener)
{
def conf = SpringSecurityUtils.securityConfig
requestCache = ref('requestCache')
defaultTargetUrl = conf.successHandler.defaultTargetUrl
alwaysUseDefaultTargetUrl = conf.successHandler.alwaysUseDefault
targetUrlParameter = conf.successHandler.targetUrlParameter
useReferer = conf.successHandler.useReferer
redirectStrategy = ref('redirectStrategy')
defaultUrl= "/"
adminUrl = "/admin/"
userUrl = "/user/"
}
}
And my UrlRedirectEventListener
class UrlRedirectEventListener
extends SavedRequestAwareAuthenticationSuccessHandler
{
#Override
protected String determineTargetUrl(HttpServletRequest request,
HttpServletResponse response) {
boolean hasBoth = SpringSecurityUtils.ifAllGranted("ROLE_USER,ROLE_ADMIN");
boolean hasUser = SpringSecurityUtils.ifAnyGranted("ROLE_USER");
boolean hasAdmin = SpringSecurityUtils.ifAnyGranted("ROLE_ADMIN");
println("hasBoth:"+hasBoth+ " hasUser:"+hasUser+ " hasAdmin:"+hasAdmin)
if( hasBoth ){
return defaultLoginUrl;
}else if ( hasUser ){
return userUrl ;
}else if ( hasAdmin ){
return adminUrl ;
}else{
return super.determineTargetUrl(request, response);
}
}
private String defaultLoginUrl;
private String mmrLoginUrl;
private String pubApiLoginUrl;
...setters for urls
}
What am I missing?
I am using Grails 2.0.4 and Spring Security Plugin 2 RC2.0
Thanks!
Edit:
I also have this controller to test that my bean is created, which shows up correctly.
class TestController {
AuthenticationSuccessHandler authenticationSuccessHandler
def index()
{
render( authenticationSuccessHandler )
}
}
After looking at the source, I figured it out. The problem was that SavedRequestAwareAuthenticationSuccessHandler's method onAuthenticationSuccess would never reach the overridden method determineTargetUrl with the simple login scheme I was using( the default that is generated by the plugin ). I had to actually override onAuthenticationSuccess instead and put my logic there.
The source in question:
SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler
My updated UrlRedirectEventListener, with most copy-pasted from SavedRequestAwareAuthenticationSuccessHandler:
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
def targetUrl = savedRequest.getRedirectUrl();
if( targetUrl != null && targetUrl != "" && targetUrl.endsWith("/myApp/") ) // I only want to differently handle "/". If a user hits /user/, or any other url, attempt to send him there. Spring Security will deny him access based on his privileges if necessary.
targetUrl = this.determineTargetUrl( request, response ) // This is the method defined above. Only thing changed there is I removed the `super` call
clearAuthenticationAttributes(request);
logger.info("Redirecting to Url: " + targetUrl);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
Is there any way, with springSecurityService, to log someone else out?
(For example, if a regular user leaves for the day and forgets to logout, and the manager wants to log their account out.)
I have done in my application where , A admin User can logged-out forcefully any user from the list of all users currently logged-in into the system.
I get all users those are currently logged-in into the system and send to the jsp where list of all logged-in users are shown to the Admin user.
#PreAuthorize("hasRole('Currently_Logged-In_Users')")
#RequestMapping(value = "/getLoggedInUsers", method = RequestMethod.POST)
#ResponseBody
public Map<String, List<?>> getLoggedInUsers(Map<String, Object> map ,Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String userName = auth.getName();
List<Object> principals = sessionRegistry.getAllPrincipals();
List<UserInfo> usersInfoList = new ArrayList<UserInfo>();
for (Object principal: principals) {
if (principal instanceof UserInfo) {
if(!((UserInfo) principal).getUsername().equals(userName)){
for(SessionInformation sess :sessionRegistry.getAllSessions(principal, false)){
if(!sess.isExpired()){
usersInfoList.add((UserInfo) sess.getPrincipal());
}
}
}
}
}
Map<String, List<?>> loggedInUserMap = new HashMap<String, List<?>>();
loggedInUserMap.put("loggenInUsers",
usersInfoList);
return loggedInUserMap;
}
Now Admin user can select any user or multiple user by clicking on check box against the users. and call forced Logged-out action.
#PreAuthorize("hasRole('Currently_Logged-In_Users')")
#RequestMapping(value = "/logoutSelectedUsers", method = RequestMethod.POST)
#ResponseBody
public Map<String, String> forcedLoggedOut(#RequestParam("userList[]")ArrayList<String> userList ,Model model ,HttpServletRequest request ) {
Map<String,String> map= new HashMap<String,String>();
try{
String successMessage =null;
List<String> userCodeList = new ArrayList<String>();
for(String userCode :userList ){
userCodeList.add(userCode);
}
List<Object> principals = sessionRegistry.getAllPrincipals();
for (Object principal: principals) {
if (principal instanceof UserInfo) {
if(userCodeList.contains(((UserInfo) principal).getUsername())){
for(SessionInformation sess :sessionRegistry.getAllSessions(principal, false)){
sess.expireNow();
successMessage = "msg.loggedOutSuccessfully";
}
}
}
}
map.put("successmsg", successMessage);
}
catch(Exception e){
map.put("failmsg", "msg.notLoggedOut");
logger.error(e.toString(),e);
}
return map;
}
The springSecurityService itself does not have this capability.
However, nothing is stopping you from creating your own ServletFilter to track session ids and security principals and expose a controller and pages to invalidate the associated session with a login.
Here's how I do it.
Edit: The example below uses the webxml plugin. You can also edit web.xml directly. See this answer for setting the timeout.
// src/groovy/com/example/ExpiringSessionEventListener.groovy:
package com.example
import grails.util.Holders
import javax.servlet.http.HttpSessionListener
import javax.servlet.http.HttpSessionEvent
import org.springframework.security.core.context.SecurityContext
public class ExpiringSessionEventListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent event) {
// Do some logging
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
SecurityContext securityContext = event.session.getAttribute("SPRING_SECURITY_CONTEXT")
if (securityContext) {
UserService userService = Holders.applicationContext.getBean("userService")
String userName = securityContext.authentication.principal.username
userService.userLoggedOut(userName, event.session.id, Boolean.TRUE)
}
}
}
// grails-app/services/com/example/UserService.groovy:
package com.example
import grails.plugin.springsecurity.annotation.Secured
class UserService {
#Secured(["ROLE_USER"])
def userLoggedOut(String userName, String sessionId, Boolean expired) {
User user = User.findByUsername(userName)
if (expired) {
// Do user cleanup stuff after expired session
} else {
// Do user cleanup stuff after clicking the logout button
}
}
}
Edit:
// grails-app/conf/WebXmlConfig.groovy:
webxml {
sessionConfig.sessionTimeout = 10 // minutes
listener.add = true
listener.classNames = [
"com.example.ExpiringSessionEventListener"
]
}
i give users special URL with access key in it. users accessing the public page via this special url should be able to see some additional data as compared to simple anonymous user.
i want to give some additional role to anonymous user based on parameters provided in request so i can do something like this in my template:
<#sec.authorize ifAnyGranted="ROLE_ADMIN, ROLE_USER, ROLE_INVITED_VISITOR">
...some additional stuff for invited user to see
</#sec.authorize>
currently i'm implementing Spring's OncePerRequestfilter:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
if (null != request.getParameter("accessKey")) {
if(isValid(request.getParameter("accessKey"))) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
//how do i add additional roles to authenticated (potentially anonymous) user?
}
}
}
Why not just create a wrapper class that delegates to the original, but adds on a couple of extra GrantedAuthorities:
public class AuthenticationWrapper implements Authentication
{
private Authentication original;
private GrantedAuthority[] extraRoles;
public AuthenticationWrapper( Authentication original, GrantedAuthority[] extraRoles )
{
this.original = original;
this.extraRoles = extraRoles;
}
public GrantedAuthority[] getAuthorities()
{
GrantedAuthority[] originalRoles = original.getAuthorities();
GrantedAuthority[] roles = new GrantedAuthority[originalRoles.length + extraRoles.length];
System.arraycopy( originalRoles, 0, roles, 0, originalRoles.length );
System.arraycopy( extraRoles, 0, roles, originalRoles.length, extraRoles.length );
return roles;
}
public String getName() { return original.getName(); }
public Object getCredentials() { return original.getCredentials(); }
public Object getDetails() { return original.getDetails(); }
public Object getPrincipal() { return original.getPrincipal(); }
public boolean isAuthenticated() { return original.isAuthenticated(); }
public void setAuthenticated( boolean isAuthenticated ) throws IllegalArgumentException
{
original.setAuthenticated( isAuthenticated );
}
}
and then do this in your filter:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
GrantedAuthority extraRoles = new GrantedAuthority[2];
extraRoles[0] = new GrantedAuthorityImpl( "Role X" );
extraRoles[1] = new GrantedAuthorityImpl( "Role Y" );
AuthenticationWrapper wrapper = new AuthenticationWrapper( auth, extraRoles );
SecurityContextHolder.getContext().setAuthentication( wrapper );
The Authentication is now replaced by your version with the extra roles. NB You may have to handle the case where the Authentication has not yet been authenticated and so its getAuthorities() returns null. (The wrapper implementation currently assumes that it will always get a non-null array from its wrapped Authentication)