Acegi Security: How do i add another GrantedAuthority to Authentication to anonymous user - spring-security

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)

Related

Is it possible to load granted authorities and other user information AFTER authentication succeeds when using a UserDetailsService?

I have been able to get a Spring Security based application up and running, and it has been satisfying all my requirements until now.
I do have 1 doubt regarding how UserDetailsService is used in Spring Security. I have a custom 'UserDetailsService' implementation, which goes like this -
public class CustomUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<UserDetailsDto> userDetailsByEmail = // Load userDetailsDto from database
if (!userDetailsByEmail.isPresent()) {
throw new UsernameNotFoundException("Username does not exists");
}
UserDetailsDto userDetailsDto = userDetailsByEmail.get();
List<Role> roles = roleService.listByEmail(username);
List<ModulePermission> modulePermissions = modulePermissionService.listByUserId(userDetailsDto.getId());
UserType userType = userTypeService.getByUserId(userDetailsDto.getId());
return new LoggedInUser(userDetailsDto, roles, modulePermissions, userType);
}
}
The class LoggedInUser is an extension of Spring Security's org.springframework.security.core.userdetails.User class, which goes like this -
public class LoggedInUser extends User {
private static final long serialVersionUID = -1L;
private Long userId;
private boolean firstLogin;
private UserType userType;
private List<ModulePermission> modulePermissions;
private String firstName;
private String lastName;
private String contactNo;
public LoggedInUser(UserDetailsDto userDetailsDto, List<Role> roles, List<ModulePermission> modulePermissions,
UserType userType) {
super(userDetailsDto.getEmail(), userDetailsDto.getPassword(), userDetailsDto.getEnabledStatus().getValue(),
userDetailsDto.getAccountNonExpiredStatus().getValue(), true,
userDetailsDto.getAccountNonLockedStatus().getValue(),
roles.stream().map(role -> new SimpleGrantedAuthority(role.getId())).collect(Collectors.toList()));
this.modulePermissions = modulePermissions;
this.userType = userType;
this.userId = userDetailsDto.getId();
this.firstLogin = userDetailsDto.getIsFirstLoginStatus().getValue();
this.firstName = userDetailsDto.getFirstName();
this.lastName = userDetailsDto.getLastName();
this.contactNo = userDetailsDto.getContactNo();
}
public List<ModulePermission> getModulePermissions() {
return Collections.unmodifiableList(modulePermissions);
}
public UserType getUserType() {
return userType;
}
public Long getUserId() {
return userId;
}
public boolean isFirstLogin() {
return firstLogin;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getContactNo() {
return contactNo;
}
public void setFirstLogin(boolean firstLogin) {
this.firstLogin = firstLogin;
}
}
Now, to configure Spring Security to use my CustomUserDetailsService, I do the following in security configuration -
#Bean
public UserDetailsService customUserDetailsService() {
return new CustomUserDetailsService();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher(SuperAdminConstant.UrlConstant.ANT_MATCHER_PATH)
.userDetailsService(customUserDetailsService())
.formLogin(// further configuration)
}
And this works without any problems.
But notice that in CustomUserDetailsService, several database queries are executed even before the user has been authenticated successfully (This is because Spring Security has created a DaoAuthenticationProvider, which loads a UserDetails implementation (in my case, LoggedInUser), and perform various checks on that object AFTER it has been retrieved from a UserDetailsService (in my case , CustomUserDetailsService)).
Consider that a user has entered the correct username, but a wrong password. In that case, the high-level authentication flow would be -
CustomUserDetailsService would be called
First query is executed to verify username and load user details (UsernameNotFoundException is not thrown as username is correct)
Second query is executed to retrieve the roles
Third query is executed to retrieve module permissions
Fourth query is executed to retrieve user types
DaoAuthenticationProvider checks the password, finds it to be incorrect, and throws a BadCredentialsException.
So as can be seen, total 4 queries are executed EVEN BEFORE authentication process has completed, out of which only 1 is essential at this stage (the first query to verify username).
One solution to this problem can be to eliminate the use of UserDetailsService altogeather, and use a custom AuthenticationProvider instead.
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
Authentication authenticate(Authentication authentication)
throws AuthenticationException {
// Customize the authentication logic here, and retrieve
// user information only if everything is correct.
}
}
But going by this approach also means that I have to duplicate code and functionality provided by DaoAuthenticationProvider and AbstractUserDetailsAuthenticationProvider, which includes checking user account status flags manually (accountNonExpired, accountNonLocked etc.), and throwing exceptions.
So I was wondering weather it is possible to perform authentication logic in such a way that user information is retrieved only AFTER authentication succeeds, and most of the authentication logic provided by Spring Security can be resused.
Any ideas will be deeply appreciated.
You can write implementation of AuthenticationSuccessHandler:
#Component
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
LoggedInUser loggedInUser = (LoggedInUser)authentication.getPrincipal();
List<Role> roles = roleService.listByEmail(username);
List<ModulePermission> modulePermissions = modulePermissionService.listByUserId(userDetailsDto.getId());
UserType userType = userTypeService.getByUserId(userDetailsDto.getId());
// Set roles after authentication succeeds
loggedInUser.setRoles(roles);
loggedInUser.setModulePermissions(modulePermissions);
loggedInUser.setUserType(userType);
}
}
After the authentication succeeds, you can obtain logged in user from security context and set additional properties.

SpringSecurity WithSecurityContext MockMvc OAuth2 always unauthorised

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.

SpringSecurityService: Log other user out?

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"
]
}

Grails Spring Security get user by id

I'm writing Grails application with Spring Security plugin.
I have enabled queries generated by GORM into console and I have noticed that every request Security query the database about users, selecting them by it's username.
My intention is load user's by it's ID's, not usernames for increase performance.
I am aware that there is a possibility to overwrite UserDetailsService method loadUserByUsername(String username), but this method is used both for refreshing user's credential during the session and in login form, where in fact I want to authenticate user by it's username.
I have three questions:
How to load user by id? Should I inject user ID instead of username in GrailsUser (implementation of UserDetails) instead of regular username and use long selectById = Long.valueOf(String username)?
How to create different user provider for refreshing session to grab user by ID and different for user login (when I want to grab user by it's username/email)?
There is possibility to fetch user credentials not every request, but every X seconds?
Finally i managed to solve this problem. The queries are generated by:
springSecurityService.getCurrentUser()
Unfortunatelly, this method fetches User model class by username (from Principal object) and maps it into database field, at most configured by:
grails.plugin.springsecurity.userLookup.usernamePropertyName
as mentioned in documentation.
I have tried
grails.plugin.springsecurity.userLookup.usernamePropertyName = 'id'
but i received class-cast exception from String to Long.
Workaround is simple - create own Principle with username field typed as Long.
See PrincipalProxy in my solution:
package com.selly.util.security
import java.security.Principal;
import grails.plugin.springsecurity.userdetails.GrailsUser
import org.springframework.security.core.Authentication
import org.springframework.security.core.GrantedAuthority
class AppMetadataAuthenticationToken implements Authentication, Principal {
private boolean authenticated
private GrailsUser userDetails
private Principal principal
public AppMetadataAuthenticationToken(GrailsUser userDetails) {
this.userDetails = userDetails
this.principal = new PrincipalProxy(userDetails)
}
public GrailsUser getUser() {
return userDetails
}
public String getUsername() {
return userDetails.getUsername()
}
#Override
public String getName() {
return userDetails.getUsername()
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return userDetails.getAuthorities()
}
#Override
public Object getCredentials() {
return userDetails.password
}
#Override
public Object getDetails() {
return getUser()
}
#Override
public Object getPrincipal() {
return principal
}
#Override
public boolean isAuthenticated() {
return authenticated
}
#Override
public void setAuthenticated(boolean authenticated) throws IllegalArgumentException {
this.authenticated = authenticated
}
static class PrincipalProxy implements Principal {
GrailsUser grailsUser
Long username
public PrincipalProxy(GrailsUser grailsUser) {
this.grailsUser = grailsUser
this.username = grailsUser.id
}
#Override
public String getName() {
return grailsUser.id
}
}
}
To return this Token, just register your own AuthenticationProvider:
package com.selly.util.security;
import grails.plugin.springsecurity.SpringSecurityService
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
public class AppUsernamePasswordAuthenticationProvider extends DaoAuthenticationProvider implements AuthenticationProvider {
SpringSecurityService springSecurityService
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
def token = (UsernamePasswordAuthenticationToken) authentication
def user = userDetailsService.loadUserByUsername(authentication.principal)
if(!user)
throw new UsernameNotFoundException("Cannot find user", authentication.principal)
if(!passwordEncoder.isPasswordValid(user.password, authentication.credentials, null))
throw new BadCredentialsException("Invalid password")
return new AppMetadataAuthenticationToken(user)
}
#Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
package com.selly.util.security;
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
public class AppMetadataAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
// TODO Auto-generated method stub
return authentication;
}
#Override
public boolean supports(Class<?> authentication) {
// TODO Auto-generated method stub
return AppMetadataAuthenticationToken.class.isAssignableFrom(authentication);
}
}
Register it in resources.groovy
appUsernamePasswordAuthenticationProvider(AppUsernamePasswordAuthenticationProvider) {
userDetailsService = ref('userDetailsService')
passwordEncoder = ref('passwordEncoder')
userCache = ref('userCache')
saltSource = ref('saltSource')
preAuthenticationChecks = ref('preAuthenticationChecks')
postAuthenticationChecks = ref('postAuthenticationChecks')
springSecurityService = ref('springSecurityService')
}
And in Config.groovy:
grails.plugin.springsecurity.providerNames = [
'appMetadataAuthenticationProvider',
'appUsernamePasswordAuthenticationProvider',
// 'daoAuthenticationProvider',
// 'anonymousAuthenticationProvider',
// 'rememberMeAuthenticationProvider'
]
Now all works perfectly:
Hibernate: select this_.id as id13_0_, this_.account_expired as account2_13_0_, this_.account_locked as account3_13_0_, this_.enabled as enabled13_0_, this_."password" as password5_13_0_, this_.password_expired as password6_13_0_, this_.username as username13_0_, this_.workspace as workspace13_0_ from users this_ where (**this_.id=?**) limit ?
Instead of using getCurrentUser() you can also getPrincipal() and cast to your previously populated object with more data than Principal interface offers.

ASP.NET MVC Creating a impersonated user

I have a MVC app where I have a User class and the user can also impersonate another user(Admin users only).
So I have this code below that authenticates the request and instantiates my version of a User class.
It then tries to get the impersonated user from the Session object but Session is not available in this method in the global.asax.
Hope this makes sense.
How else could I do this?
My question I guess is at what point in the global.asax methods do you get access to Session object for each request?
protected void Application_OnAuthenticateRequest(object sender, EventArgs e)
{
IMylesterService service = ObjectFactory.GetInstance<IMylesterService>();
if (Context.User != null)
{
if (Context.User.Identity.IsAuthenticated)
{
User user = service.GetUser(Context.User.Identity.Name);
if (user == null)
throw new ApplicationException("Context.user.Identity.name is not a recognized user");
User impersonatedUser = (User)this.Session["ImpersonatedUser"];
if (impersonatedUser == null)
user.ImpersonatedUser = user;
else
user.ImpersonatedUser = impersonatedUser;
System.Threading.Thread.CurrentPrincipal = Context.User = user;
return;
}
}
User guest = service.GetGuestUser();
guest.ImpersonatedUser = guest;
System.Threading.Thread.CurrentPrincipal = Context.User = guest;
}
Try creating an authorization filter:
public class CustomAuthorizationFilter : AuthorizeAttribute
{
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
// perform your authorization here
// full access to HttpContext and session
}
}
You can then apply this attribute to your controllers. Ideally you'd have a base controller that all other controllers inherit from and you could apply the attribute at the class level on that controller. Then all of your requests would be authorized and apply the impersonation as you have coded above.
Session will not be available during AuthenticateRequest: What you will need to do is tag the required information to the Identity.userData; so for example if you where using forms authentication do the following:
void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.User != null)
{
if (Context.User.Identity.IsAuthenticated)
{
// retrieve the value
var id = (FormsIdentity)Context.User.Identity;
var myvalue = id.Ticket.UserData; // "Here you are"
}
}
}
For sign in using forms you will need to write a custom cookie:
MVC -> class FormsAuthenticationService : IFormsAuthenticationService
public static void SetAuthenticationCookie(HttpContextBase context, FormsAuthenticationTicket ticket)
{
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName)
{
Value = FormsAuthentication.Encrypt(ticket),
Secure = FormsAuthentication.RequireSSL,
Domain = FormsAuthentication.CookieDomain,
HttpOnly = true,
Expires = DateTime.Now.AddMinutes(15)
};
if (!context.Request.IsSecureConnection && FormsAuthentication.RequireSSL)
{
throw new HttpException("Ticket requires SSL.");
}
context.Response.Cookies.Add(cookie);
}
public static FormsAuthenticationTicket CreateTicket(HttpContextBase context, string emailAddress, string userData, bool persist)
{
return new FormsAuthenticationTicket(1, emailAddress, DateTime.Now, DateTime.Now.AddMinutes(15), persist, userData, FormsAuthentication.FormsCookiePath);
}
Finally in SignIn you would now create the required ticket by calling CreateTicket(...), and then you would write it out by SetAuthenticationCookie(...).
public void SignIn(string userName, string password)
{
if(CheckUserValid(userName,password, out string email))
{
var ticket = CreateTicket(email, "Here you are", true);
SetAuthenticationCookie(HttpContext.Current.Base(), ticket);
}
}
I have never used it, but assume that session state is assigned during the AcquireRequestState:
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(context_AcquireRequestState);
}
I had this same issue of needing to access session in global.asax and finally solved it by moving my code into the AcquireRequestState handler, which happens after the authentication is passed.
protected void Application_AcquireRequestState(Object sender, EventArgs e)
{
if (Request.IsAuthenticated && Context.Session != null)
{
// access Context.Session
}
}
This fires a lot and the current context does not always have a valid session object, hence the check.
EDIT: Had to add the check for IsAuthenticated too -- was getting a null error when logged out. Works great now.

Resources