package eu.dnetlib.simplesso;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.util.List;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;

import com.google.gson.Gson;

public class SimpleSSOAuthenticator {
	private static final Log log = LogFactory.getLog(SimpleSSOAuthenticator.class); // NOPMD by marko on 11/24/08 5:02 PM

	public static class AuthToken {
		private String payload;
		private String signature;

		public String getSignature() {
			return signature;
		}

		public void setSignature(String signature) {
			this.signature = signature;
		}

		public String getPayload() {
			return payload;
		}

		public void setPayload(String payload) {
			this.payload = payload;
		}
	}

	public static class AuthPayload {
		private String uid;
		private String email;
		private List<String> roles;

		public String getUid() {
			return uid;
		}

		public void setUid(String uid) {
			this.uid = uid;
		}

		public String getEmail() {
			return email;
		}

		public void setEmail(String email) {
			this.email = email;
		}

		public List<String> getRoles() {
			return roles;
		}

		public void setRoles(List<String> roles) {
			this.roles = roles;
		}

	}

	private SimpleSSOCertificate certificate;
	private String algo;

	protected AuthPayload decodeToken(String encodedToken) {
		String clearToken = decodeBase64(encodedToken);

		Gson son = new Gson();
		AuthToken token = son.fromJson(clearToken, AuthToken.class);
		checkSignature(token.getPayload(), token.getSignature());
		AuthPayload payload = son.fromJson(token.getPayload(), AuthPayload.class);

		return payload;
	}

	private void checkSignature(String payload, String sig) {
		try {
			log.info("checking signature " + payload + " with sig: " + sig);

			Signature signature = Signature.getInstance(algo);
			Certificate keypair = certificate.getKeyPair();

			signature.initVerify(keypair);

			signature.update(payload.getBytes());

			if (!signature.verify(rawDecodeBase64(sig)))
				throw new IllegalArgumentException("doesn't validate signature, token forged");
			
			log.info("Signature verified !!!");
		} catch (NoSuchAlgorithmException e) {
			throw new IllegalArgumentException("problem verifying singnature", e);
		} catch (InvalidKeyException e) {
			throw new IllegalArgumentException("problem verifying singnature", e);
		} catch (SignatureException e) {
			throw new IllegalArgumentException("problem verifying singnature", e);
		}
	}

	private byte[] rawDecodeBase64(String input) {
		return Base64.decodeBase64(input.getBytes());
	}

	private String decodeBase64(String input) {
		return new String(Base64.decodeBase64(input.getBytes()));
	}

	public SimpleSSOCertificate getCertificate() {
		return certificate;
	}

	@Required
	public void setCertificate(SimpleSSOCertificate certificate) {
		this.certificate = certificate;
	}

	public String getAlgo() {
		return algo;
	}

	@Required
	public void setAlgo(String algo) {
		this.algo = algo;
	}
}
