Sunday, July 28, 2013

Permission based authorization with OAuth 10a

OAuth 10a deals with roal based authorization. 
Can we do the permission based authorization with oauth 10a ?

That is can we authorize a URL pattern, can we authorize frequency of request URL 
Or can we authorize particulate client IP to access the resource

The easiest way is to write a custom expression voter as below

public class CExpressionVoter implements AccessDecisionVoter<FilterInvocation> {
    public int vote(Authentication authentication, FilterInvocation fi, Collection<ConfigAttribute> attributes) {
        assert authentication != null;
        assert fi != null;
        assert attributes != null;

        if(authentication.getPrincipal()!=null ) {
        Object principal = authentication.getPrincipal();
        if(principal instanceof UserDetails) {
        UserDetails userDetails = (UserDetails)principal;
        if(userDetails.getUsername().equalsIgnoreCase("anonymousUser")){
        return -1;
        } else {
        Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
          int i = -1;
        for (GrantedAuthority grantedAuthority : authorities) {
String uriAuthorityPattern = grantedAuthority.getAuthority().toLowerCase();
String uriRequest = fi.getRequestUrl().toLowerCase();
String fullRequestUrl = fi.getFullRequestUrl();

String delimiters = "/\\s*|\\?\\s*";
String[] uriPatternArray = uriRequest.split(delimiters);
String uriPattern = null;
if(uriPatternArray !=null & uriPatternArray.length >=2) {
uriPattern = uriPatternArray[1];
}
// This is the sample to check the URL pattern. Likewise you can do other authentications, All the autorities can be loaded to
// user detail object using a custom user detail service, sample given below
if (uriPattern!=null & uriPattern.equalsIgnoreCase(uriAuthorityPattern)){
i = 1; break;
} else {
i = -1;
}
}
        return i;        
        }
       
        if (principal instanceof String) {
        if(((String) principal).equalsIgnoreCase("anonymousUser")){
        return -1;
        }
        }
        }
        return 1;
    }
    public boolean supports(ConfigAttribute attribute) {
    return true;
    }

    public boolean supports(Class<?> clazz) {
        return clazz.isAssignableFrom(FilterInvocation.class);
    }
}

Custom User Detail Service
public class CUserDetailService implements UserDetailsService {
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
ArrayList<String> authority  = new ArrayList<String>();
// Get the values from the database and add as arrayList
UserDetails result = new CustomUserDetails(findById, authority);
return result;
}

}

Custom User Details

public class CustomUserDetails implements UserDetails {
private String userName;
private String password;
private Collection<GrantedAuthority> grantedAuthorities;

public CustomUserDetails(CustomUser user, List<String> authority){
this.grantedAuthorities = new ArrayList<GrantedAuthority>();
for (String p : authority) {
GrantedAuthority ga = new SimpleGrantedAuthority(p);
this.grantedAuthorities.add(ga);
}
}

public Collection<? extends GrantedAuthority> getAuthorities() {
return this.grantedAuthorities;
}

// write inherited methods for UserDetails
}

Custom Secure Resource Filter
// this is a dummy implementation
public class CSecureResourceFilter implements FilterInvocationSecurityMetadataSource {
public Collection<ConfigAttribute> getAllConfigAttributes() {
StringBuilder rolesStringBuilder = new StringBuilder();
rolesStringBuilder.append("USER1");
List<ConfigAttribute> createListFromCommaDelimitedString = SecurityConfig.createListFromCommaDelimitedString(rolesStringBuilder.toString());
return createListFromCommaDelimitedString;
}

public Collection<ConfigAttribute> getAttributes(Object filter) throws IllegalArgumentException {
StringBuilder rolesStringBuilder = new StringBuilder();
rolesStringBuilder.append("USER1");
return SecurityConfig.createListFromCommaDelimitedString(rolesStringBuilder.toString());
}
public boolean supports(Class<?> arg0) {
return true;
}
}


Spring security configuration file

..

<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<beans:constructor-arg name="decisionVoters">
<beans:list>
<beans:bean class="..................CExpressionVoter" />
</beans:list>
</beans:constructor-arg> 
</beans:bean>

<http use-expressions="true" auto-config='true' access-denied-page="/login.jsp" > 
<form-login authentication-failure-url="/login.jsp" default-target-url="/index.jsp" login-page="/login.jsp" login-processing-url="/login.do" />
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="filterSecurityInterceptor" /> 
</http> 

<beans:bean id="customUserDetailService" class=".....CUserDetailService" /> 

<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="customUserDetailService"/> 
</authentication-manager>

<beans:bean id="customSecureResourceFilter" class=".......CSecureResourceFilter"/>


<beans:bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="accessDecisionManager" ref="accessDecisionManager"/>
<beans:property name="securityMetadataSource" ref="customSecureResourceFilter"/>

</beans:bean>