package eu.dnetlib.openaire.aas;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.thrift.TException;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opensaml.DefaultBootstrap;
import org.opensaml.lite.common.SAMLObject;
import org.opensaml.lite.saml2.core.Assertion;
import org.springframework.context.ApplicationContext;

import pl.edu.icm.yadda.aas.audit.user.AssertionSubjectUserIdExtractor;
import pl.edu.icm.yadda.aas.client.authn.ServiceAuthenticatorException;
import pl.edu.icm.yadda.aas.refresher.IRefresher;
import pl.edu.icm.yadda.aas.refresher.RefresherException;
import pl.edu.icm.yadda.service2.user.UserCatalog;
import pl.edu.icm.yadda.service2.user.credential.LoginPasswordCredential;
import pl.edu.icm.yadda.service2.user.model.GroupName;
import pl.edu.icm.yadda.service2.user.model.User;
import pl.edu.icm.yadda.service2.user.model.UserAttributesConstants;
import pl.edu.icm.yadda.spring.utils.SpringUtils;
import eu.dnetlib.enabling.aas.DNetAuthenticateRequest;
import eu.dnetlib.enabling.aas.DNetAuthenticateResponse;
import eu.dnetlib.enabling.aas.DNetAuthorizeRequest;
import eu.dnetlib.enabling.aas.DNetAuthorizeResponse;
import eu.dnetlib.enabling.aas.IAAService;
import eu.dnetlib.enabling.aas.client.ServiceSingleAssertionHolder;
import eu.dnetlib.enabling.aas.client.authn.GenericServiceAuthenticator;
import eu.dnetlib.enabling.aas.client.authn.oblig.DummyObligationsAnalyzer;
import eu.dnetlib.enabling.aas.client.authn.req.PasswordBasedAuthnRequestBuilder;
import eu.dnetlib.enabling.aas.holder.IDataHolder;
import eu.dnetlib.enabling.aas.proxy.SimpleAssertionRefresher;
import eu.dnetlib.enabling.aas.saml.adapter.AdapterFactoryRegistry;
import eu.dnetlib.openaire.thrift.ClaimedDocument;
import eu.dnetlib.openaire.thrift.SecuredConnectorFacade;
import eu.dnetlib.openaire.thrift.UserClaims;
import eu.dnetlib.openaire.thrift.UserInfo;
import eu.dnetlib.openaire.thrift.OpenAireConnector.Iface;

/**
 * Secured connector with AAS2 integration tests.
 * @author mhorst
 *
 */
public class SecuredConnectorIntegrationTest {

	SecuredConnectorFacade securedConnector;
	GenericServiceAuthenticator serviceAuthenticator;
	SimpleAssertionRefresher refresher;
	ServiceSingleAssertionHolder sudoAssertionHolder;
	
	UserCatalog userCatalog = null;
	IAAService aaService = null;
	IDataHolder<SAMLObject[]> sharedDataHolder = null;
	
	String sudoEmail = "some.user@host.eu";
	String sudoPassword = "somesudopass";
	
	@BeforeClass
	public static void initClass() throws Exception {
//		required by openSAML
		DefaultBootstrap.bootstrap();
//		required by adapters model
		AdapterFactoryRegistry.bootstrap();
	}
	
	@SuppressWarnings("unchecked")
	@Before
	public void init() throws Exception {
		userCatalog = (UserCatalog) SpringUtils.getSpringContext(
		"classpath:/eu/dnetlib/openaire/aas/ldap-context.xml").getBeansOfType(
				UserCatalog.class).values().iterator().next();
		ApplicationContext aasContext = SpringUtils.getSpringContext(
		"classpath:/eu/dnetlib/openaire/aas/aas2-client-context.xml");
		aaService = (IAAService) aasContext.getBean("client");
		sharedDataHolder = (IDataHolder<SAMLObject[]>) aasContext.getBean("SharedDataHolder");
		
//		initializing connector
		securedConnector = new SecuredConnectorFacade();
		securedConnector.setAaService(aaService);
		securedConnector.setSamlDataHolder(sharedDataHolder);
		sudoAssertionHolder = new ServiceSingleAssertionHolder();
		securedConnector.setSudoAssertionHolder(sudoAssertionHolder);
		refresher = new SimpleAssertionRefresher();
		refresher.setAaService(aaService);
		refresher.setSamlDataHolder(sharedDataHolder);
		refresher.setIdExtractor(new AssertionSubjectUserIdExtractor());
		securedConnector.setRefresher(refresher);
		serviceAuthenticator = new GenericServiceAuthenticator();
		serviceAuthenticator.setAuthnService(aaService);
		serviceAuthenticator.setAssertionHolder(sudoAssertionHolder);
		serviceAuthenticator.setInputSamlDataHolder(sharedDataHolder);
		serviceAuthenticator.setObligationsAnalyzer(new DummyObligationsAnalyzer());
		serviceAuthenticator.setReauthnScheduler(null);
		PasswordBasedAuthnRequestBuilder requestBuilder = new PasswordBasedAuthnRequestBuilder();
		requestBuilder.setLogin(sudoEmail);
		requestBuilder.setPassword(sudoPassword);
		serviceAuthenticator.setRequestBuilder(requestBuilder);
		securedConnector.setServiceAuthenticator(serviceAuthenticator);
		
//		initializing mockery
		Mockery connectorMockery = new Mockery();
		final Iface mockedConnector = connectorMockery.mock(Iface.class);
		connectorMockery.checking(new Expectations(){{
			allowing(mockedConnector).getClaimedDocuments(
					with(any(String.class)),
					with(any(String.class)));
			will(returnValue(new ArrayList<ClaimedDocument>()));
			
			allowing(mockedConnector).getAllClaimedDocuments(
					with(any(String.class)),
					with(any(String.class)),
					with(any(String.class)));
			will(returnValue(new ArrayList<UserClaims>()));
			
			allowing(mockedConnector).claimDocuments(
					with(any(UserInfo.class)), 
					with(any(List.class)), 
					with(any(String.class)));
		}});
		securedConnector.setConnector(mockedConnector);
	}
	
	@Test
	public void testBootstrapSudoAuthenticationOfNonExistingSudoer() throws Exception {
//		authenticating sudoer
		try {
			securedConnector.afterPropertiesSet();
			fail("exception should be thrown when authenticating non-existing sudoer");
		} catch(ServiceAuthenticatorException e) {
//			ok
//			TODO shouldn't be authn info propagated in response?
		}
	}
	
	@Test
	public void testBootstrapSudoAuthentication() throws Exception {
//		registering sudo user
		String sudoId = "someUser_" + System.currentTimeMillis();
		String sudoName = "John Smith";
		boolean wasCreated = false;
		try {
//			1) registering new sudoer user in LDAP
			User user = new User();
			user.setId(sudoId);
			Map<String, String> attributes = new HashMap<String, String>();
			attributes.put(UserAttributesConstants.EMAIL, sudoEmail);
			attributes.put(UserAttributesConstants.FULL_NAME, sudoName);
			user.setAttributes(attributes);
			userCatalog.addUser(user);
			wasCreated = true;
//			1b) setting password
			LoginPasswordCredential passCred = new LoginPasswordCredential();
			passCred.setUserId(sudoId);
			passCred.setPassword(sudoPassword);
			userCatalog.addCredential(passCred);
//			1c) assigning to sudoers group
			userCatalog.assignUser(sudoId, new GroupName(null,"sudoers"));

//			authenticating sudoer
			securedConnector.afterPropertiesSet();
			
//			checking assertion
			assertNotNull(sudoAssertionHolder.getAllAssertions());
			assertEquals(1, sudoAssertionHolder.getAllAssertions().size());
			assertEquals(sudoEmail, new AssertionSubjectUserIdExtractor().extractId(
					sudoAssertionHolder.getAllAssertions().iterator().next()));
			
		} finally {
			if (wasCreated) {
				userCatalog.deleteUser(sudoId, null);
			}
		}
	}
	
	@Test
	public void testAuthorizeAccessToOwner() throws Exception {
//		no need to authenticate sudoer, owner-based authorization is performed locally
		String ownerEmail = "some.guy@host.eu";
		securedConnector.claimDocuments(
				new UserInfo(ownerEmail, "John", "Smith"), 
				null, ownerEmail);

		List<ClaimedDocument> result = securedConnector.getClaimedDocuments(
				ownerEmail, ownerEmail);
		assertNotNull(result);
		assertTrue(result.isEmpty());
	}
	
	@Test
	public void testAuthorizeAccessForUnregisteredUser() throws Exception {
//		registering sudo user
		String sudoId = "someUser_" + System.currentTimeMillis();
		String sudoName = "John Smith";
		boolean wasCreated = false;
		try {
//			1) registering new sudoer user in LDAP
			User user = new User();
			user.setId(sudoId);
			Map<String, String> attributes = new HashMap<String, String>();
			attributes.put(UserAttributesConstants.EMAIL, sudoEmail);
			attributes.put(UserAttributesConstants.FULL_NAME, sudoName);
			user.setAttributes(attributes);
			userCatalog.addUser(user);
			wasCreated = true;
//			1b) setting password
			LoginPasswordCredential passCred = new LoginPasswordCredential();
			passCred.setUserId(sudoId);
			passCred.setPassword(sudoPassword);
			userCatalog.addCredential(passCred);
//			1c) assigning to sudoers group
			userCatalog.assignUser(sudoId, new GroupName(null,"sudoers"));

//			authenticating sudoer
			securedConnector.afterPropertiesSet();
			
//			checking assertion
			assertNotNull(sudoAssertionHolder.getAllAssertions());
			assertEquals(1, sudoAssertionHolder.getAllAssertions().size());
			assertEquals(sudoEmail, new AssertionSubjectUserIdExtractor().extractId(
					sudoAssertionHolder.getAllAssertions().iterator().next()));

//			authorizing non existing user access to all 3 secured methods
			String unregisteredEmail = "some.unregistered.guy@host.eu";
			try {
				securedConnector.claimDocuments(
					new UserInfo("john.smith@host.eu", "John", "Smith"), 
					null, unregisteredEmail);
				fail("exception should be thrown when accessing user data for unregistered user!");
			} catch (TException e) {
//				ok
			}
			try {
				securedConnector.getClaimedDocuments("john.smith@host.eu", 
						unregisteredEmail);
				fail("exception should be thrown when accessing user data for unregistered user!");
			} catch (TException e) {
//				ok
			}
			try {
				securedConnector.getAllClaimedDocuments(null, null, unregisteredEmail); 
				fail("exception should be thrown when accessing user data for unregistered user!");
			} catch (TException e) {
//				ok
			}
			
		} finally {
			if (wasCreated) {
				userCatalog.deleteUser(sudoId, null);
			}
		}
	}
	
	@Test
	public void testAuthorizeAccessForAdminMultipleTimes() throws Exception {
		String userId = "someUser_" + System.currentTimeMillis();
//		registering sudo user
		String sudoId = "someSudo_" + System.currentTimeMillis();
		String sudoName = "John Smith";
		boolean wasAdminCreated = false;
		boolean wasUserCreated = false;
		try {
//			1) registering new sudoer user in LDAP
			User user = new User();
			user.setId(sudoId);
			Map<String, String> attributes = new HashMap<String, String>();
			attributes.put(UserAttributesConstants.EMAIL, sudoEmail);
			attributes.put(UserAttributesConstants.FULL_NAME, sudoName);
			user.setAttributes(attributes);
			userCatalog.addUser(user);
			wasAdminCreated = true;
//			1b) setting password
			LoginPasswordCredential passCred = new LoginPasswordCredential();
			passCred.setUserId(sudoId);
			passCred.setPassword(sudoPassword);
			userCatalog.addCredential(passCred);
//			1c) assigning to sudoers group
			userCatalog.assignUser(sudoId, new GroupName(null,"sudoers"));

//			authenticating sudoer
			securedConnector.afterPropertiesSet();
			
//			checking assertion
			assertNotNull(sudoAssertionHolder.getAllAssertions());
			assertEquals(1, sudoAssertionHolder.getAllAssertions().size());
			assertEquals(sudoEmail, new AssertionSubjectUserIdExtractor().extractId(
					sudoAssertionHolder.getAllAssertions().iterator().next()));

//			registering admin user
			String userEmail = "admin@host.eu";
			String userName = "unknown";
			user = new User();
			user.setId(userId);
			attributes = new HashMap<String, String>();
			attributes.put(UserAttributesConstants.EMAIL, userEmail);
			attributes.put(UserAttributesConstants.FULL_NAME, userName);
			user.setAttributes(attributes);
			userCatalog.addUser(user);
			wasUserCreated = true;
			userCatalog.assignUser(userId, new GroupName(null, "Super Administrator"));
			
//			authorizing all 3 operations
			securedConnector.claimDocuments(
				new UserInfo("john.smith@host.eu", "John", "Smith"), 
				null, userEmail);
			List<ClaimedDocument> docs = securedConnector.getClaimedDocuments(
					"john.smith@host.eu", userEmail);
			assertNotNull(docs);
			List<UserClaims> claims = securedConnector.getAllClaimedDocuments(
					null, null, userEmail); 
			assertNotNull(claims);
			
//			second try, checking whether already stored assertion was used
//			no authn should be called
			securedConnector.setAaService(new IAAService() {
				@Override
				public void start() {
					aaService.start();
				}
				@Override
				public void notify(String subscriptionId, String topic, String isId,
						String message) {
					aaService.notify(subscriptionId, topic, isId, message);
				}
				@Override
				public String identify() {
					return aaService.identify();
				}
				@Override
				public DNetAuthorizeResponse authorize(DNetAuthorizeRequest request) {
					return aaService.authorize(request);
				}
				@Override
				public DNetAuthenticateResponse authenticate(DNetAuthenticateRequest request) {
					throw new RuntimeException("authn should not be called!");
				}
			});

			securedConnector.claimDocuments(
					new UserInfo("john.smith@host.eu", "John", "Smith"), 
					null, userEmail);
			docs = securedConnector.getClaimedDocuments(
					"john.smith@host.eu", userEmail);
			assertNotNull(docs);
			claims = securedConnector.getAllClaimedDocuments(
					null, null, userEmail); 
			assertNotNull(claims);
			
		} finally {
			try {
				if (wasAdminCreated) {
					userCatalog.deleteUser(sudoId, null);
				}	
			} catch(Exception e) {
				e.printStackTrace();
			}
			if (wasUserCreated) {
				userCatalog.deleteUser(userId, null);
			}
		}
	}

	@Test
	public void testAuthorizeAccessForNonAdminUser() throws Exception {
		String userId = "someUser_" + System.currentTimeMillis();
//		registering sudo user
		String sudoId = "someSudo_" + System.currentTimeMillis();
		String sudoName = "John Smith";
		boolean wasAdminCreated = false;
		boolean wasUserCreated = false;
		try {
//			1) registering new sudoer user in LDAP
			User user = new User();
			user.setId(sudoId);
			Map<String, String> attributes = new HashMap<String, String>();
			attributes.put(UserAttributesConstants.EMAIL, sudoEmail);
			attributes.put(UserAttributesConstants.FULL_NAME, sudoName);
			user.setAttributes(attributes);
			userCatalog.addUser(user);
			wasAdminCreated = true;
//			1b) setting password
			LoginPasswordCredential passCred = new LoginPasswordCredential();
			passCred.setUserId(sudoId);
			passCred.setPassword(sudoPassword);
			userCatalog.addCredential(passCred);
//			1c) assigning to sudoers group
			userCatalog.assignUser(sudoId, new GroupName(null,"sudoers"));

//			authenticating sudoer
			securedConnector.afterPropertiesSet();
			
//			checking assertion
			assertNotNull(sudoAssertionHolder.getAllAssertions());
			assertEquals(1, sudoAssertionHolder.getAllAssertions().size());
			assertEquals(sudoEmail, new AssertionSubjectUserIdExtractor().extractId(
					sudoAssertionHolder.getAllAssertions().iterator().next()));

//			registering admin user
			String userEmail = "casual@host.eu";
			String userName = "unknown";
			user = new User();
			user.setId(userId);
			attributes = new HashMap<String, String>();
			attributes.put(UserAttributesConstants.EMAIL, userEmail);
			attributes.put(UserAttributesConstants.FULL_NAME, userName);
			user.setAttributes(attributes);
			userCatalog.addUser(user);
			wasUserCreated = true;
			
//			authorizing all 3 operations
			try {
				securedConnector.claimDocuments(
					new UserInfo("john.smith@host.eu", "John", "Smith"), 
					null, userEmail);
				fail("exception should be thrown when accessing user data for unregistered user!");
			} catch (TException e) {
//				ok
			}
			try {
				securedConnector.getClaimedDocuments("john.smith@host.eu", 
						userEmail);
				fail("exception should be thrown when accessing user data for unregistered user!");
			} catch (TException e) {
//				ok
			}
			try {
				securedConnector.getAllClaimedDocuments(null, null, userEmail); 
				fail("exception should be thrown when accessing user data for unregistered user!");
			} catch (TException e) {
//				ok
			} 
			
		} finally {
			try {
				if (wasAdminCreated) {
					userCatalog.deleteUser(sudoId, null);
				}	
			} catch(Exception e) {
				e.printStackTrace();
			}
			if (wasUserCreated) {
				userCatalog.deleteUser(userId, null);
			}
		}
	}
	
	/**
	 * Testing user assertion refreshing.
	 * Policy have to be defined so the user assertion has the following parameters:
	 * aas.assertion.default.ttl=PT2S, aas.assertion.default.refreshes-count=5
	 * aas.assertion.sudo.default.ttl=PT2S, aas.assertion.sudo.default.refreshes-count=5
	 * in such scenario sudo assertion will expire as well.
	 */
	@Test
	public void testAdminAuthzWhenUserAssertionExpiredAndPermanentlyExpired() throws Exception {
		long userTimeWindowMillis = 2000;
		int userRefrCount = 5;
		
		String userId = "someUser_" + System.currentTimeMillis();
//		registering sudo user
		String sudoId = "someSudo_" + System.currentTimeMillis();
		String sudoName = "John Smith";
		boolean wasAdminCreated = false;
		boolean wasUserCreated = false;
		try {
//			1) registering new sudoer user in LDAP
			User user = new User();
			user.setId(sudoId);
			Map<String, String> attributes = new HashMap<String, String>();
			attributes.put(UserAttributesConstants.EMAIL, sudoEmail);
			attributes.put(UserAttributesConstants.FULL_NAME, sudoName);
			user.setAttributes(attributes);
			userCatalog.addUser(user);
			wasAdminCreated = true;
//			1b) setting password
			LoginPasswordCredential passCred = new LoginPasswordCredential();
			passCred.setUserId(sudoId);
			passCred.setPassword(sudoPassword);
			userCatalog.addCredential(passCred);
//			1c) assigning to sudoers group
			userCatalog.assignUser(sudoId, new GroupName(null,"sudoers"));

//			authenticating sudoer
			securedConnector.afterPropertiesSet();
			
//			checking assertion
			assertNotNull(sudoAssertionHolder.getAllAssertions());
			assertEquals(1, sudoAssertionHolder.getAllAssertions().size());
			assertEquals(sudoEmail, new AssertionSubjectUserIdExtractor().extractId(
					sudoAssertionHolder.getAllAssertions().iterator().next()));

//			registering admin user
			String userEmail = "admin@host.eu";
			String userName = "unknown";
			user = new User();
			user.setId(userId);
			attributes = new HashMap<String, String>();
			attributes.put(UserAttributesConstants.EMAIL, userEmail);
			attributes.put(UserAttributesConstants.FULL_NAME, userName);
			user.setAttributes(attributes);
			userCatalog.addUser(user);
			wasUserCreated = true;
			userCatalog.assignUser(userId, new GroupName(null, "Super Administrator"));
			
//			first call, triggering creating assertion for user
			List<ClaimedDocument> docs = securedConnector.getClaimedDocuments(
					"john.smith@host.eu", userEmail);
			assertNotNull(docs);
			
			final AtomicInteger refrCount = new AtomicInteger(0);
			final AtomicInteger reauthnCount = new AtomicInteger(0);
//			authn in refresh mode should be called
			securedConnector.setRefresher(new IRefresher<Assertion>() {
				@Override
				public Assertion refresh(Assertion arg0) throws RefresherException {
					refrCount.set(refrCount.incrementAndGet());
					return refresher.refresh(arg0);
				}
			});
			securedConnector.setAaService(new IAAService() {
				@Override
				public void start() {
					aaService.start();
				}
				@Override
				public void notify(String subscriptionId, String topic, String isId,
						String message) {
					aaService.notify(subscriptionId, topic, isId, message);
				}
				@Override
				public String identify() {
					return aaService.identify();
				}
				@Override
				public DNetAuthorizeResponse authorize(DNetAuthorizeRequest request) {
					return aaService.authorize(request);
				}
				@Override
				public DNetAuthenticateResponse authenticate(DNetAuthenticateRequest request) {
					reauthnCount.set(reauthnCount.incrementAndGet());
					return aaService.authenticate(request);
				}
			});
//			wait single time-window to pass
			Thread.sleep(userTimeWindowMillis);
//			second call, user assertion should be refreshed
			docs = securedConnector.getClaimedDocuments(
					"john.smith@host.eu", userEmail);
			assertNotNull(docs);
			assertEquals(0, reauthnCount.get());
			assertEquals(1, refrCount.get());
//			resetting values
			refrCount.set(0);
		
//			wait for permanent expiration
			Thread.sleep(userRefrCount*userTimeWindowMillis);
			docs = securedConnector.getClaimedDocuments(
					"john.smith@host.eu", userEmail);
			assertNotNull(docs);
//			two reauthn required: user and sudo
			assertEquals(2, reauthnCount.get());
			assertEquals(0, refrCount.get());
			
		} finally {
			try {
				if (wasAdminCreated) {
					userCatalog.deleteUser(sudoId, null);
				}	
			} catch(Exception e) {
				e.printStackTrace();
			}
			if (wasUserCreated) {
				userCatalog.deleteUser(userId, null);
			}
		}
	}

	/**
	 * Testing sudo user assertion refreshing.
	 * Policy have to be defined so the sudo assertion has the following parameters:
	 * aas.assertion.sudo.default.ttl=PT2S, aas.assertion.sudo.default.refreshes-count=5
	 * and the user assertion has to expire after first time frame, it shouldn't be refreshable:
	 * aas.assertion.default.ttl=PT2S, aas.assertion.default.refreshes-count=0
	 */
	@Test
	public void testAdminAuthzWhenSudoAssertionExpiredAndPermanentlyExpired() throws Exception {
		long sudoTimeWindowMillis = 2000;
		int sudoRefrCount = 5;
		
		String userId = "someUser_" + System.currentTimeMillis();
//		registering sudo user
		String sudoId = "someSudo_" + System.currentTimeMillis();
		String sudoName = "John Smith";
		boolean wasAdminCreated = false;
		boolean wasUserCreated = false;
		try {
//			1) registering new sudoer user in LDAP
			User user = new User();
			user.setId(sudoId);
			Map<String, String> attributes = new HashMap<String, String>();
			attributes.put(UserAttributesConstants.EMAIL, sudoEmail);
			attributes.put(UserAttributesConstants.FULL_NAME, sudoName);
			user.setAttributes(attributes);
			userCatalog.addUser(user);
			wasAdminCreated = true;
//			1b) setting password
			LoginPasswordCredential passCred = new LoginPasswordCredential();
			passCred.setUserId(sudoId);
			passCred.setPassword(sudoPassword);
			userCatalog.addCredential(passCred);
//			1c) assigning to sudoers group
			userCatalog.assignUser(sudoId, new GroupName(null,"sudoers"));

//			authenticating sudoer
			securedConnector.afterPropertiesSet();
			
//			checking assertion
			assertNotNull(sudoAssertionHolder.getAllAssertions());
			assertEquals(1, sudoAssertionHolder.getAllAssertions().size());
			assertEquals(sudoEmail, new AssertionSubjectUserIdExtractor().extractId(
					sudoAssertionHolder.getAllAssertions().iterator().next()));

//			registering admin user
			String userEmail = "admin@host.eu";
			String userName = "unknown";
			user = new User();
			user.setId(userId);
			attributes = new HashMap<String, String>();
			attributes.put(UserAttributesConstants.EMAIL, userEmail);
			attributes.put(UserAttributesConstants.FULL_NAME, userName);
			user.setAttributes(attributes);
			userCatalog.addUser(user);
			wasUserCreated = true;
			userCatalog.assignUser(userId, new GroupName(null, "Super Administrator"));
			
//			first call, triggering creating assertion for user
			List<ClaimedDocument> docs = securedConnector.getClaimedDocuments(
					"john.smith@host.eu", userEmail);
			assertNotNull(docs);
			
			final Assertion initialSudoAssertion = sudoAssertionHolder.getAssertion();
			assertNotNull(initialSudoAssertion);
			
			final AtomicInteger refrCount = new AtomicInteger(0);
			final AtomicInteger reauthnCount = new AtomicInteger(0);
//			authn in refresh mode should be called
			securedConnector.setRefresher(new IRefresher<Assertion>() {
				@Override
				public Assertion refresh(Assertion arg0) throws RefresherException {
					refrCount.set(refrCount.incrementAndGet());
					return refresher.refresh(arg0);
				}
			});
			serviceAuthenticator.setAuthnService(new IAAService() {
				@Override
				public void start() {
					aaService.start();
				}
				@Override
				public void notify(String subscriptionId, String topic, String isId,
						String message) {
					aaService.notify(subscriptionId, topic, isId, message);
				}
				@Override
				public String identify() {
					return aaService.identify();
				}
				@Override
				public DNetAuthorizeResponse authorize(DNetAuthorizeRequest request) {
					return aaService.authorize(request);
				}
				@Override
				public DNetAuthenticateResponse authenticate(DNetAuthenticateRequest request) {
					reauthnCount.set(reauthnCount.incrementAndGet());
					return aaService.authenticate(request);
				}
			});
//			wait single time-window to pass
			Thread.sleep(sudoTimeWindowMillis);
//			second call, user assertion should be refreshed
			docs = securedConnector.getClaimedDocuments(
					"john.smith@host.eu", userEmail);
			assertNotNull(docs);
			assertEquals(0, reauthnCount.get());
			assertEquals(1, refrCount.get());
//			checking refreshed assertion
			Assertion refreshedSudoAssertion = sudoAssertionHolder.getAssertion();
			assertEquals(initialSudoAssertion.getID(), refreshedSudoAssertion.getID());
			assertTrue(refreshedSudoAssertion.getConditions().getNotBefore().isAfter(
					initialSudoAssertion.getConditions().getNotBefore().getMillis()));
//			resetting values
			refrCount.set(0);
		
//			wait for permanent expiration
			Thread.sleep(sudoRefrCount*sudoTimeWindowMillis);
			docs = securedConnector.getClaimedDocuments(
					"john.smith@host.eu", userEmail);
			assertNotNull(docs);
			assertEquals(1, reauthnCount.get());
			assertEquals(0, refrCount.get());
//			checking reauthenticated assertion
			Assertion reauthenticatedSudoAssertion = sudoAssertionHolder.getAssertion();
			assertFalse(reauthenticatedSudoAssertion.getID().equals(
					refreshedSudoAssertion.getID()));
			assertTrue(reauthenticatedSudoAssertion.getConditions().getNotBefore().isAfter(
					refreshedSudoAssertion.getConditions().getNotBefore().getMillis()));
			
		} finally {
			try {
				if (wasAdminCreated) {
					userCatalog.deleteUser(sudoId, null);
				}	
			} catch(Exception e) {
				e.printStackTrace();
			}
			if (wasUserCreated) {
				userCatalog.deleteUser(userId, null);
			}
		}
	}
	
}
