/*
 * 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$
 *
 */
// https://issues.jboss.org/browse/TEIID-2471
// https://issues.jboss.org/browse/TEIID-2771
// https://issues.jboss.org/browse/TEIID-2507
// https://issues.jboss.org/browse/TEIID-2771
// https://issues.redhat.com/browse/TEIID-3663
package org.teiid.jdbc;

import static org.junit.jupiter.api.Assertions.*;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Arrays;

import javax.security.auth.login.LoginException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.teiid.core.util.UnitTestUtil;
import org.teiid.jboss.JBossSecurityHelper;
import org.teiid.metadata.Column;
import org.teiid.metadata.MetadataFactory;
import org.teiid.metadata.Table;
import org.teiid.metadata.Table.Type;
import org.teiid.runtime.EmbeddedConfiguration;
import org.teiid.runtime.EmbeddedServer;
import org.teiid.runtime.HardCodedExecutionFactory;
import org.teiid.security.Credentials;
import org.teiid.security.SecurityHelper;
import org.teiid.security.TeiidLoginContext;
import org.teiid.translator.TranslatorException;
import org.teiid.translator.TypeFacility;
import org.wildfly.security.auth.realm.SimpleMapBackedSecurityRealm;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.auth.server.SecurityIdentity;

@SuppressWarnings("nls")
public class TestRowBasedSecurity {

	private EmbeddedServer es;

	@AfterEach public void tearDown() {
		es.stop();
	}

	@Test public void testSecurity() throws Exception {
		es = new EmbeddedServer();
		EmbeddedConfiguration ec = new EmbeddedConfiguration();
		ec.setSecurityHelper(new JBossSecurityHelper() {

			private SecurityDomain domain = SecurityDomain.builder()
					.addRealm("Simple", new SimpleMapBackedSecurityRealm()).build()
					.setDefaultRealmName("Simple")
					.build();

			@Override
			public SecurityDomain getSecurityDomainContext(String securityDomain) {
				return domain;
			}

			@Override
			public TeiidLoginContext authenticate(String securityDomain, String baseUserName, Credentials credentials, String applicationName) throws LoginException {
				return new TeiidLoginContext(baseUserName+"@"+securityDomain, createAdHocIdentity(securityDomain, baseUserName, null), securityDomain);
			}
		});
		es.start(ec);
		HardCodedExecutionFactory hcef = new HardCodedExecutionFactory() {
			@Override
            public void getMetadata(MetadataFactory metadataFactory, Object conn) throws TranslatorException {
				Table t = metadataFactory.addTable("x");
				Column col = metadataFactory.addColumn("col", TypeFacility.RUNTIME_NAMES.STRING, t);
				metadataFactory.addColumn("col2", TypeFacility.RUNTIME_NAMES.STRING, t);
				metadataFactory.addPermission("y", t, null, null, Boolean.TRUE, null, null, null, "col = 'a'", null);
				metadataFactory.addColumnPermission("y", col, null, null, null, null, "null", null);

				t = metadataFactory.addTable("y");
				col = metadataFactory.addColumn("col", TypeFacility.RUNTIME_NAMES.STRING, t);
				metadataFactory.addColumn("col2", TypeFacility.RUNTIME_NAMES.STRING, t);
				metadataFactory.addPermission("z", t, null, null, null, null, null, null, "col = 'e'", null);

				Table v = metadataFactory.addTable("v");
				metadataFactory.addPermission("y", v, null, null, Boolean.TRUE, null, null, null, null, null);
				col = metadataFactory.addColumn("col", TypeFacility.RUNTIME_NAMES.STRING, v);
				metadataFactory.addColumn("col2", TypeFacility.RUNTIME_NAMES.STRING, v);
				v.setTableType(Type.View);
				v.setVirtual(true);
				v.setSelectTransformation("/*+ cache(scope:session) */ select col, col2 from y");
			}
			@Override
			public boolean isSourceRequiredForMetadata() {
				return false;
			}
		};
		hcef.addData("SELECT x.col, x.col2 FROM x", Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("c", "d")));
		hcef.addData("SELECT y.col, y.col2 FROM y", Arrays.asList(Arrays.asList("e", "f"), Arrays.asList("h", "g")));

		es.addTranslator("hc", hcef);
		es.deployVDB(new FileInputStream(UnitTestUtil.getTestDataFile("roles-vdb.xml")));

		SecurityHelper sh = ec.getSecurityHelper();
		SecurityIdentity si = sh.createAdHocIdentity("dv-security", "test", Arrays.asList("myrole"));
		si.runAs(() -> {
			try {
				Connection c = es.getDriver().connect("jdbc:datavirtuality:z;PassthroughAuthentication=true", null);
				Statement s = c.createStatement();
				ResultSet rs = s.executeQuery("select * from x");
				rs.next();
				assertEquals(null, rs.getString(1)); //masking
				assertEquals("b", rs.getString(2));
				assertFalse(rs.next()); //row filter
				rs.close();

				s = c.createStatement();
				rs = s.executeQuery("select lookup('myschema.x', 'col', 'col2', 'b')");
				rs.next();
				assertEquals(null, rs.getString(1)); //global scoped

				s = c.createStatement();
				rs = s.executeQuery("select count(col2) from v where col is not null");
				rs.next();
				assertEquals(1, rs.getInt(1));
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		});

		//different session with different roles
		Connection c = es.getDriver().connect("jdbc:datavirtuality:z", null);
		Statement s = c.createStatement();
		ResultSet rs = s.executeQuery("select count(col2) from v where col is not null");
		rs.next();
		assertEquals(2, rs.getInt(1));
	}
	
}
