package cz.muni.fi.lasaris.sbms.auth;

import java.io.IOException;
import java.security.Principal;
import java.util.Properties;

import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.SecurityContext;

import org.apache.log4j.Logger;
import org.glassfish.jersey.internal.util.Base64;

//http://stackoverflow.com/questions/17068528/authorization-with-rolesalloweddynamicfeature-and-jersey
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
	final static Logger logger = Logger.getLogger(AuthenticationFilter.class);

	private AuthProvider ap;
	
	private Properties props = null; 
	private String appName;
	
	public AuthenticationFilter(String appName, Properties props) {
		super();
		
		this.props = props;
		this.appName = appName;
		
		if (props.getProperty("auth.enable") == null || !Boolean.parseBoolean(props.getProperty("auth.enable"))) {
			logger.debug("Initing allowAll provider for app " + this.appName);
			ap = getAllowAllProvider();
			return;
		}
		try {
			String className = this.props.getProperty("auth.provider");
			if(className != null) {
				Class<?> c = Class.forName(className);
				this.ap = (AuthProvider)c.newInstance();
				this.ap.init(this.props);
				logger.debug("Initing custom auth provider for app " + this.appName);
			} else {
				throw new IllegalArgumentException(appName + ": customAuth is required but no AuthProvider implementation is provided.");
			}
		} catch(Exception ex) {
			logger.error(ex);
			logger.error(appName + ": Unable to load AuthProvider - allowing all users.");
			ap = getAllowAllProvider();
		}
	}
	
	public void filter(final ContainerRequestContext requestContext) throws IOException {
		requestContext.setSecurityContext(new SecurityContext() {
			private Principal user;
			
			{
				String auth = requestContext.getHeaderString("authorization");
				
					final String[] creds = decodeAuth(auth);
					if(ap.authenticate(creds[0], creds[1])) {
						logger.debug(appName + ": Authenticated.");
						user = new Principal() {

							public String getName() {
								return creds[0];
							}  
						};
					} else {
					logger.debug(appName + ": Auth failed.");
					user = null;
				}
				
			}
			
			
			
			public Principal getUserPrincipal() {
				return user;
			}

			public boolean isUserInRole(String role) { 
				if(getUserPrincipal() == null) {
					return false;
				}
				String user = getUserPrincipal().getName();
				if(ap.authorize(user, role)) {
					logger.debug(appName + ": Authorized: " + user + " in " + role);
					return true;  
				} else {
					logger.debug(appName + ": Not authorized: " + user + " in " + role);
					return false;
				}
			}

			public boolean isSecure() {
				return requestContext.getSecurityContext().isSecure();
			}

			public String getAuthenticationScheme() {
				return requestContext.getSecurityContext().getAuthenticationScheme();
			}

		});    
	}

	// https://simplapi.wordpress.com/2013/01/24/jersey-jax-rs-implements-a-http-basic-auth-decoder/	  
	private String[] decodeAuth(String header) {
		if (header == null) {
			return new String[] {"", ""};
		}
		String auth = header.replaceFirst("[B|b]asic ", "");

		//Decode the Base64 into byte[]
		//Base64 decoder = 
		//byte[] decodedBytes = DatatypeConverter.parseBase64Binary(auth);
		byte[] decodedBytes = Base64.decode(auth.getBytes());
		//If the decode fails in any case
		if(decodedBytes == null || decodedBytes.length == 0){
			return null;
		}

		//Now we can convert the byte[] into a splitted array :
		//  - the first one is login,
		//  - the second one password
		return new String(decodedBytes).split(":", 2);
	}
	
	private AuthProvider getAllowAllProvider() {
		return new AuthProvider() {
			
			
			public boolean authenticate(String user, String password) {
				return true;
			}

			public boolean authorize(String user, String role) {
				return true;
			}

			public void init(Properties props) {
			}
			
		};
	}
	
}
