package cz.metacentrum.perun.registrar.modules;

import cz.metacentrum.perun.core.api.*;
import cz.metacentrum.perun.core.api.exceptions.InternalErrorException;
import cz.metacentrum.perun.core.api.exceptions.PerunException;
import cz.metacentrum.perun.core.api.exceptions.UserExtSourceExistsException;
import cz.metacentrum.perun.core.impl.Utils;
import cz.metacentrum.perun.registrar.RegistrarModule;
import cz.metacentrum.perun.registrar.model.Application;
import cz.metacentrum.perun.registrar.model.ApplicationFormItemData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.List;

/**
 * Application module for ELIXIR purpose
 * 
 * @author Michal Prochazka
 * 
 * Module which generates userExtSource containing generated DN for ELIXIR CILogon service. 
 * More details are available at https://wiki.geant.org/display/AARC/RCauth.eu-CILogon-like-TTS-pilot 
 */
public class ELIXIRCILogonDNGenerator implements RegistrarModule {

	final static Logger log = LoggerFactory.getLogger(ELIXIRCILogonDNGenerator.class);
	
	final static String ELIXIRSCOPE = "@elixir-europe.org";
	final static String LOGINATTRIBUTE = "urn:perun:user:attribute-def:def:login-namespace:elixir";
	final static String DISPLAYNAMEATTRIBUTE = "urn:perun:user:attribute-def:def:displayName";
	final static String DNPREFIX = "/DC=eu/DC=rcauth/DC=rcauth-clients/O=elixir-europe.org/CN=";
	final static String CADN = "/DC=eu/DC=rcauth/O=Certification Authorities/CN=Research and Collaboration Authentication Pilot G1 CA";

	@Override
	public List<ApplicationFormItemData> createApplication(PerunSession user, Application application, List<ApplicationFormItemData> data) throws PerunException {
		return data;
	}

	/**
	 * All new members will get new userExtSource with generated DN according to the CILogon rules:
	 * echo -n "eppn" | openssl dgst -sha256 -binary | base64 | head -c16
	 * where eppn is eduPersonPrincipalName
	 */
	@Override
	public Application approveApplication(PerunSession session, Application app) throws PerunException {

		if (Application.AppType.INITIAL.equals(app.getType())) {
			
			// get perun from session
			Perun perun = session.getPerun();

			User user = app.getUser();
			// Get user login
			String login = (String) perun.getAttributesManager().getAttribute(session, user, LOGINATTRIBUTE).getValue();
			// Create ELIXIR login from user login and scope
			String elixirLogin = login + ELIXIRSCOPE;
			
			// Get user displayName
			String utfDisplayName = (String) perun.getAttributesManager().getAttribute(session, user, DISPLAYNAMEATTRIBUTE).getValue();
			// Convert to ASCII only
			String displayName = Utils.utftoasci(utfDisplayName);
			
			// Compute hash
			MessageDigest md;
			try {
				md = MessageDigest.getInstance("SHA-256");
			} catch (NoSuchAlgorithmException e) {
				throw new InternalErrorException(e);
			}

			try {
				md.update(elixirLogin.getBytes("UTF-8"));
			} catch (UnsupportedEncodingException e) {
				throw new InternalErrorException(e);
			} 
			
			byte[] digest = md.digest();
			Encoder enc = Base64.getEncoder();
			String hash = enc.encodeToString(digest);
			// Get just first 16 bytes as is described in EU CILogon - RCauth.eu CA requirements
			String CILogonHash = hash.substring(0, 16);
			
			// Generate the DN, it must look like /DC=eu/DC=rcauth/DC=rcauth-clients/O=elixir-europe.org/CN=Michal Prochazka rdkfo3rdkfo3kdo
			String dn = DNPREFIX + displayName + " " + CILogonHash;

			// Store the userExtSource
			ExtSource extSource = perun.getExtSourcesManager().getExtSourceByName(session, CADN);
		
			// TODO This must be run under perunBL, user doesn't have rights to create an extSource
			perun.getExtSourcesManager().checkOrCreateExtSource(session, CADN, ExtSourcesManager.EXTSOURCE_X509);
				
			UserExtSource userExtSource = new UserExtSource(extSource, dn);
			try {
				perun.getUsersManager().addUserExtSource(session, user, userExtSource);
			} catch (UserExtSourceExistsException e) {
				// This can happen, so we can ignore it.
			}
		}

		return app;

	}

	@Override
	public Application rejectApplication(PerunSession session, Application app, String reason) throws PerunException {
		return app;
	}

}

@@ -333,18 +333,18 @@
 ore.
-impl.Utils
+bl.PerunBl
 ;%0Aim
@@ -522,16 +522,63 @@
 emData;%0A
+import org.apache.commons.codec.binary.Base64;%0A
 import o
@@ -770,50 +770,21 @@
 ava.
-util.Base64;%0Aimport java.util.Base64.Encod
+text.Normaliz
 er;%0A
@@ -2343,16 +2343,18 @@
 erun
+Bl
  perun =
  ses
@@ -2349,16 +2349,26 @@
  perun =
+ (PerunBl)
  session
@@ -2477,32 +2477,34 @@
 ttributesManager
+Bl
 ().getAttribute(
@@ -2732,24 +2732,26 @@
 butesManager
+Bl
 ().getAttrib
@@ -2813,29 +2813,26 @@
 %09// 
-Convert to ASCII only
+Remove all accents
 %0A%09%09%09
@@ -2856,38 +2856,116 @@
 e = 
-Utils.utftoasci(utfDisplayName
+Normalizer.normalize(utfDisplayName, Normalizer.Form.NFD).replaceAll(%22%5C%5Cp%7BInCombiningDiacriticalMarks%7D+%22, %22%22
 );%0A%09
@@ -3340,46 +3340,8 @@
 ();%0A
-%09%09%09Encoder enc = Base64.getEncoder();%0A
 %09%09%09S
@@ -3357,20 +3357,27 @@
 h = 
-enc
+Base64
 .encode
-To
+Base64
 Stri
@@ -3800,16 +3800,18 @@
 sManager
+Bl
 ().getEx
@@ -3844,101 +3844,8 @@
 N);%0A
-%09%09%0A%09%09%09// TODO This must be run under perunBL, user doesn't have rights to create an extSource
 %0A%09%09%09
@@ -3870,16 +3870,18 @@
 sManager
+Bl
 ().check
@@ -4025,24 +4025,24 @@
 );%0A%09%09%09try %7B%0A
-
 %09%09%09%09perun.ge
@@ -4054,16 +4054,18 @@
 sManager
+Bl
 ().addUs

Use PerunBl for some calls in Elixir registrar module