/*
 * JBoss, Home of Professional Open Source.
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.  Some portions may be licensed
 * to Red Hat, Inc. under one or more contributor license agreements.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 * Modified by
 * @author  Data Virtuality GmbH
 * @date    $Date$
 *  
 */

package org.teiid.jboss;

import java.security.Principal;
import java.security.Provider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentSkipListMap;

import java.util.function.Supplier;
import javax.security.auth.login.LoginException;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.teiid.adminapi.impl.SessionMetadata;
import org.teiid.adminapi.impl.VDBMetaData;
import org.teiid.client.security.InvalidSessionException;
import org.teiid.deployers.VDBRepository;
import org.teiid.dqp.service.SessionServiceException;
import org.teiid.net.socket.AuthenticationType;
import org.teiid.security.Credentials;
import org.teiid.security.SecurityHelper;
import org.teiid.security.TeiidLoginContext;
import org.teiid.services.SessionServiceImpl;
import org.wildfly.security.auth.realm.SimpleMapBackedSecurityRealm;
import org.wildfly.security.auth.realm.SimpleRealmEntry;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.auth.server.SecurityIdentity;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.evidence.Evidence;
import org.wildfly.security.permission.PermissionVerifier;
import static org.junit.jupiter.api.Assertions.*;

@SuppressWarnings("nls")
public class TestJBossSecurityHelper {

    private JBossSecurityHelper buildSecurityHelper(final String securityDomain, String username)
            throws Exception {
	    SimpleMapBackedSecurityRealm realm = new SimpleMapBackedSecurityRealm();
		List<Credential> credentials = new ArrayList<>();
		credentials.add(new Credential() {
		    @Override
		    public boolean canVerify(Evidence evidence) {
			    return true;
		    }

		    @Override
		    public boolean canVerify(Class<? extends Evidence> evidenceClass, String algorithmName) {
			    return true;
		    }

		    @Override
		    public boolean verify(Evidence evidence) {
				return true;
			}

		    @Override
		    public boolean verify(Supplier<Provider[]> providerSupplier, Evidence evidence) {
				return true;
			}

			@Override
		    public Credential clone() {
				return null;
			}
	    });
	    SimpleRealmEntry entry = new SimpleRealmEntry(credentials);
		Map<String, SimpleRealmEntry> realms = new HashMap<>();
		realms.put(username, entry);
		realm.setIdentityMap(realms);
	    SecurityDomain domain = SecurityDomain.builder()
			    .addRealm(securityDomain, realm).build()
			    .setDefaultRealmName(securityDomain)
			    .setPermissionMapper((permissionMappable, roles) -> PermissionVerifier.ALL)
			    .build();
    	
    	JBossSecurityHelper sh = new JBossSecurityHelper() {
			@Override
		    public SecurityDomain getSecurityDomainContext(String securityDomain) {
			    return domain;
			}
		};

    	return sh;
    }

	@Test
    public void testAuthenticate() throws Exception {
    	Credentials credentials = new Credentials("pass1".toCharArray());

        String domain = "testFile";
        JBossSecurityHelper ms = buildSecurityHelper(domain, "user1");

		TeiidLoginContext c = ms.authenticate(domain, "user1", credentials, null); //$NON-NLS-1$
        assertEquals("user1@testFile", c.getUserName()); //$NON-NLS-1$
    }

	@Test
    public void testPassThrough() throws Exception {
        String domain = "passthrough";
		SecurityHelper ms = buildSecurityHelper(domain, "user1");

		Principal p = Mockito.mock(Principal.class);
		Mockito.when(p.getName()).thenReturn("alreadylogged"); //$NON-NLS-1$
		SecurityIdentity si = ms.createAdHocIdentity(domain, p, null);
		si.runAs(() -> {

		SessionServiceImpl ss = new SessionServiceImpl();
		ss.setSecurityHelper(ms);

			TeiidLoginContext c;
			try {
				c = ss.passThroughLogin(domain, "user1");
			} catch (LoginException e) {
				throw new RuntimeException(e);
			}

        assertEquals("alreadylogged@passthrough", c.getUserName()); //$NON-NLS-1$
		});
    }
    
	public void validateSession(boolean securityEnabled) throws Exception {
		SessionServiceImpl jss = new SessionServiceImpl() {
		@Override
		protected VDBMetaData getActiveVDB(String vdbName, String vdbVersion)
				throws SessionServiceException {
			return Mockito.mock(VDBMetaData.class);
		}
		};
		VDBRepository repo = Mockito.mock(VDBRepository.class);
		Mockito.when(repo.getVdbRepo()).thenReturn(new ConcurrentSkipListMap<>());
		jss.setVDBRepository(repo);
		JBossSecurityHelper securityHelper = buildSecurityHelper("somedomain", "steve");
		jss.setSecurityHelper(securityHelper);
		jss.setSecurityDomain("somedomain");
		
		try {
			jss.validateSession(String.valueOf(1));
			fail("exception expected"); //$NON-NLS-1$
		} catch (InvalidSessionException e) {
			
		}
		
		SessionMetadata info = jss.createSession("x", "1", AuthenticationType.USERPASSWORD, "steve",  new Credentials("pass1".toCharArray()), "foo", new Properties()); //$NON-NLS-1$ //$NON-NLS-2$
		if (securityEnabled) {
			SecurityIdentity si = info.getSubject();
			assertEquals("steve", si.getPrincipal().getName());
		}
		
		String id1 = info.getSessionId();
		jss.validateSession(id1);
		
		assertEquals(1, jss.getActiveSessionsCount());
		assertEquals(0, jss.getSessionsLoggedInToVDB("a", 1).size()); //$NON-NLS-1$ 
		
		jss.closeSession(id1);
		
		try {
			jss.validateSession(id1);
			fail("exception expected"); //$NON-NLS-1$
		} catch (InvalidSessionException e) {
			
		}
		
		try {
			jss.closeSession(id1);
			fail("exception expected"); //$NON-NLS-1$
		} catch (InvalidSessionException e) {
			
		}
	}
	
	@Test public void testvalidateSession() throws Exception{
		validateSession(true);
	}

	@Test public void testvalidateSession2() throws Exception {
		validateSession(false);
	}    
}
