diff --git a/apps/shrine-app/src/main/scala/net/shrine/wiring/AuthStrategy.scala b/apps/shrine-app/src/main/scala/net/shrine/wiring/AuthStrategy.scala index ed62f9786..d805ade75 100644 --- a/apps/shrine-app/src/main/scala/net/shrine/wiring/AuthStrategy.scala +++ b/apps/shrine-app/src/main/scala/net/shrine/wiring/AuthStrategy.scala @@ -1,67 +1,35 @@ package net.shrine.wiring import com.typesafe.config.Config import net.shrine.authentication.{AuthenticationType, Authenticator, PmAuthenticator} -import net.shrine.authorization.{AuthorizationType, StewardQueryAuthorizationService, QueryAuthorizationService, AllowsAllAuthorizationService} -import net.shrine.qep.AllowsAllAuthenticator +import net.shrine.authorization.{AllowsAllAuthorizationService, AuthorizationType, QueryAuthorizationService, StewardQueryAuthorizationService} import net.shrine.client.Poster -import net.shrine.hms.authorization.HmsDataStewardAuthorizationService import net.shrine.hms.authentication.EcommonsPmAuthenticator -import net.shrine.hms.authorization.JerseySheriffClient - +import net.shrine.hms.authorization.HmsDataStewardAuthorizationService +import net.shrine.qep.AllowsAllAuthenticator /** * @author clint * @since Jul 1, 2014 */ object AuthStrategy { import AuthenticationType._ import AuthorizationType._ def determineAuthenticator(authType: AuthenticationType, pmPoster: Poster): Authenticator = authType match { case NoAuthentication => AllowsAllAuthenticator case Pm => PmAuthenticator(pmPoster) case Ecommons => EcommonsPmAuthenticator(pmPoster) - case _ => throw new Exception(s"Disallowed authentication type '$authType'") + case _ => throw new IllegalArgumentException(s"Unknown authentication type '$authType'") } - def determineQueryAuthorizationService(qepConfig:Config,authType: AuthorizationType, shrineConfigBall: ShrineConfig, authenticator: Authenticator): QueryAuthorizationService = { + def determineQueryAuthorizationService(qepConfig:Config,authType: AuthorizationType, authenticator: Authenticator): QueryAuthorizationService = { authType match { - case ShrineSteward => makeShrineStewardAuthorizationService(qepConfig) - case HmsSteward => makeHmsStewardAuthorizationService(qepConfig,shrineConfigBall, authenticator) + case ShrineSteward => StewardQueryAuthorizationService(qepConfig.getConfig("shrineSteward")) + case HmsSteward => HmsDataStewardAuthorizationService(qepConfig,authenticator) case NoAuthorization => AllowsAllAuthorizationService - case _ => throw new Exception(s"Disallowed authorization type '$authType'") + case _ => throw new IllegalArgumentException(s"Unknown authorization type '$authType'") } } - - private def makeShrineStewardAuthorizationService(qepConfig:Config): QueryAuthorizationService = { - val stewardConfig: Config = qepConfig.getConfig("shrineSteward") - StewardQueryAuthorizationService(stewardConfig) - } - - private def makeHmsStewardAuthorizationService(qepConfig:Config,shrineConfigBall: ShrineConfig, authenticator: => Authenticator): QueryAuthorizationService = { - //todo put all this in JerseySheriffClient's apply - //NB: Fail fast here, since on the fully-meshed HMS deployment, all nodes are expected to be - //query entry points - - val queryEntryPointConfig = shrineConfigBall.queryEntryPointConfig.get - - val sheriffEndpointOption = queryEntryPointConfig.sheriffEndpoint - val sheriffCredentialsOption = queryEntryPointConfig.sheriffCredentials - - //NB: Fail fast, HMS nodes need to use The Sheriff - require(sheriffEndpointOption.isDefined, "Sheriff endpoint must be defined in shrine.conf") - //NB: Fail fast, HMS nodes need to use The Sheriff - require(sheriffCredentialsOption.isDefined, "Sheriff credentials must be defined in shrine.conf") - - val sheriffUrl = sheriffEndpointOption.get.url.toString - - val sheriffUsername = sheriffCredentialsOption.get.username - val sheriffPassword = sheriffCredentialsOption.get.password - - val sheriffClient = JerseySheriffClient(sheriffUrl, sheriffUsername, sheriffPassword) - - HmsDataStewardAuthorizationService(sheriffClient, authenticator) - } } \ No newline at end of file diff --git a/apps/shrine-app/src/main/scala/net/shrine/wiring/ShrineOrchestrator.scala b/apps/shrine-app/src/main/scala/net/shrine/wiring/ShrineOrchestrator.scala index 86b7656db..25a6e46fb 100644 --- a/apps/shrine-app/src/main/scala/net/shrine/wiring/ShrineOrchestrator.scala +++ b/apps/shrine-app/src/main/scala/net/shrine/wiring/ShrineOrchestrator.scala @@ -1,363 +1,363 @@ package net.shrine.wiring import javax.sql.DataSource import com.typesafe.config.{Config, ConfigFactory} import net.shrine.adapter.dao.squeryl.tables.{Tables => AdapterTables} import net.shrine.adapter.dao.squeryl.{SquerylAdapterDao, SquerylI2b2AdminDao} import net.shrine.adapter.dao.{AdapterDao, I2b2AdminDao} import net.shrine.adapter.service.{AdapterConfig, AdapterRequestHandler, AdapterResource, AdapterService, I2b2AdminResource, I2b2AdminService} import net.shrine.adapter.translators.{ExpressionTranslator, QueryDefinitionTranslator} import net.shrine.adapter.{Adapter, AdapterMap, DeleteQueryAdapter, FlagQueryAdapter, ReadInstanceResultsAdapter, ReadPreviousQueriesAdapter, ReadQueryDefinitionAdapter, ReadQueryResultAdapter, ReadTranslatedQueryDefinitionAdapter, RenameQueryAdapter, RunQueryAdapter, UnFlagQueryAdapter} import net.shrine.authentication.Authenticator import net.shrine.authorization.QueryAuthorizationService import net.shrine.broadcaster.dao.HubDao import net.shrine.broadcaster.dao.squeryl.SquerylHubDao import net.shrine.broadcaster.service.{BroadcasterMultiplexerResource, BroadcasterMultiplexerService} import net.shrine.broadcaster.{AdapterClientBroadcaster, BroadcastAndAggregationService, BroadcasterClient, InJvmBroadcasterClient, NodeHandle, PosterBroadcasterClient, SigningBroadcastAndAggregationService} import net.shrine.client.{EndpointConfig, HttpClient, JerseyHttpClient, OntClient, Poster, PosterOntClient} import net.shrine.config.ConfigExtensions import net.shrine.config.mappings.{AdapterMappings, AdapterMappingsSource, ClasspathFormatDetectingAdapterMappingsSource} import net.shrine.crypto.{DefaultSignerVerifier, KeyStoreCertCollection, KeyStoreDescriptorParser, TrustParam} import net.shrine.dao.squeryl.{DataSourceSquerylInitializer, SquerylDbAdapterSelecter, SquerylInitializer} import net.shrine.happy.{HappyShrineResource, HappyShrineService} import net.shrine.log.Loggable import net.shrine.ont.data.{OntClientOntologyMetadata, OntologyMetadata} import net.shrine.protocol.{HiveCredentials, ResultOutputTypes, NodeId, RequestType, ResultOutputType} import net.shrine.qep.dao.AuditDao import net.shrine.qep.dao.squeryl.SquerylAuditDao import net.shrine.qep.dao.squeryl.tables.{Tables => HubTables} import net.shrine.qep.{QepConfig, I2b2BroadcastResource, I2b2QepService, QepService, ShrineResource} import net.shrine.status.StatusJaxrs import org.squeryl.internals.DatabaseAdapter /** * @author clint * @since Jan 14, 2014 * * Application wiring for Shrine, in the base, non-HMS case. All vals are protecetd, so they may be accessed, * in subclasses without ballooning this class's public API, and lazy, to work around init-order surprises when * overriding vals declared inline. See * * https://stackoverflow.com/questions/15762650/scala-override-val-in-class-inheritance * * among other links mentioning val overrides, early initializers, etc. -Clint */ object ShrineOrchestrator extends ShrineJaxrsResources with Loggable { import NodeHandleSource.makeNodeHandles override def resources: Iterable[AnyRef] = { Seq(happyResource,statusJaxrs) ++ shrineResource ++ i2b2BroadcastResource ++ adapterResource ++ i2b2AdminResource ++ broadcasterMultiplexerResource } //Load config from file on the classpath called "shrine.conf" lazy val config: Config = ConfigFactory.load("shrine") protected lazy val shrineConfigurationBall: ShrineConfig = ShrineConfig(config) val shrineConfig = config.getConfig("shrine") protected lazy val nodeId: NodeId = NodeId(shrineConfig.getString("humanReadableNodeName")) //TODO: Don't assume keystore lives on the filesystem, could come from classpath, etc protected lazy val keyStoreDescriptor = shrineConfig.getConfigured("keystore",KeyStoreDescriptorParser(_)) protected lazy val shrineCertCollection: KeyStoreCertCollection = KeyStoreCertCollection.fromFile(keyStoreDescriptor) protected lazy val keystoreTrustParam: TrustParam = TrustParam.SomeKeyStore(shrineCertCollection) protected lazy val signerVerifier: DefaultSignerVerifier = new DefaultSignerVerifier(shrineCertCollection) protected lazy val dataSource: DataSource = Jndi("java:comp/env/jdbc/shrineDB").get protected lazy val squerylAdapter: DatabaseAdapter = SquerylDbAdapterSelecter.determineAdapter(shrineConfig.getString("shrineDatabaseType")) protected lazy val squerylInitializer: SquerylInitializer = new DataSourceSquerylInitializer(dataSource, squerylAdapter) private def makePoster = poster(shrineCertCollection) _ private lazy val pmEndpoint: EndpointConfig = shrineConfig.getConfigured("pmEndpoint", EndpointConfig(_)) protected lazy val pmPoster: Poster = makePoster(pmEndpoint) private lazy val ontEndpoint: EndpointConfig = shrineConfig.getConfigured("ontEndpoint", EndpointConfig(_)) protected lazy val ontPoster: Poster = makePoster(ontEndpoint) protected lazy val breakdownTypes: Set[ResultOutputType] = shrineConfig.getOptionConfigured("breakdownResultOutputTypes", ResultOutputTypes.fromConfig).getOrElse(Set.empty) protected lazy val hubDao: HubDao = new SquerylHubDao(squerylInitializer, new net.shrine.broadcaster.dao.squeryl.tables.Tables) lazy val crcHiveCredentials = shrineConfig.getConfigured("hiveCredentials", HiveCredentials(_, HiveCredentials.CRC)) //todo move as much of this block as possible to the adapter project, and get rid of this multi-assignment of one thing protected lazy val ( adapterService: Option[AdapterService], i2b2AdminService: Option[I2b2AdminService], adapterDao: Option[AdapterDao], adapterMappings: Option[AdapterMappings] ) = adapterComponentsToTuple(shrineConfig.getOptionConfiguredIf("adapter", AdapterConfig(_)).map { adapterConfig => //todo unwind adapterConfig and just have an adapter val crcEndpoint: EndpointConfig = adapterConfig.crcEndpoint val crcPoster: Poster = makePoster(crcEndpoint) val squerylAdapterTables: AdapterTables = new AdapterTables val adapterDao: AdapterDao = new SquerylAdapterDao(squerylInitializer, squerylAdapterTables)(breakdownTypes) //NB: Is i2b2HiveCredentials.projectId the right project id to use? val i2b2AdminDao: I2b2AdminDao = new SquerylI2b2AdminDao(crcHiveCredentials.projectId, squerylInitializer, squerylAdapterTables) val adapterMappingsSource: AdapterMappingsSource = ClasspathFormatDetectingAdapterMappingsSource(adapterConfig.adapterMappingsFileName) //NB: Fail fast val adapterMappings: AdapterMappings = adapterMappingsSource.load.get val expressionTranslator: ExpressionTranslator = ExpressionTranslator(adapterMappings) val queryDefinitionTranslator: QueryDefinitionTranslator = new QueryDefinitionTranslator(expressionTranslator) val doObfuscation = adapterConfig.setSizeObfuscation val runQueryAdapter = new RunQueryAdapter( crcPoster, adapterDao, crcHiveCredentials, queryDefinitionTranslator, adapterConfig.adapterLockoutAttemptsThreshold, doObfuscation, adapterConfig.immediatelyRunIncomingQueries, breakdownTypes, collectAdapterAudit = adapterConfig.collectAdapterAudit ) val readInstanceResultsAdapter: Adapter = new ReadInstanceResultsAdapter( crcPoster, crcHiveCredentials, adapterDao, doObfuscation, breakdownTypes, collectAdapterAudit = adapterConfig.collectAdapterAudit ) val readQueryResultAdapter: Adapter = new ReadQueryResultAdapter( crcPoster, crcHiveCredentials, adapterDao, doObfuscation, breakdownTypes, collectAdapterAudit = adapterConfig.collectAdapterAudit ) val readPreviousQueriesAdapter: Adapter = new ReadPreviousQueriesAdapter(adapterDao) val deleteQueryAdapter: Adapter = new DeleteQueryAdapter(adapterDao) val renameQueryAdapter: Adapter = new RenameQueryAdapter(adapterDao) val readQueryDefinitionAdapter: Adapter = new ReadQueryDefinitionAdapter(adapterDao) val readTranslatedQueryDefinitionAdapter: Adapter = new ReadTranslatedQueryDefinitionAdapter(nodeId, queryDefinitionTranslator) val flagQueryAdapter: Adapter = new FlagQueryAdapter(adapterDao) val unFlagQueryAdapter: Adapter = new UnFlagQueryAdapter(adapterDao) val adapterMap = AdapterMap(Map( RequestType.QueryDefinitionRequest -> runQueryAdapter, RequestType.GetRequestXml -> readQueryDefinitionAdapter, RequestType.UserRequest -> readPreviousQueriesAdapter, RequestType.InstanceRequest -> readInstanceResultsAdapter, RequestType.MasterDeleteRequest -> deleteQueryAdapter, RequestType.MasterRenameRequest -> renameQueryAdapter, RequestType.GetQueryResult -> readQueryResultAdapter, RequestType.ReadTranslatedQueryDefinitionRequest -> readTranslatedQueryDefinitionAdapter, RequestType.FlagQueryRequest -> flagQueryAdapter, RequestType.UnFlagQueryRequest -> unFlagQueryAdapter)) AdapterComponents( new AdapterService(nodeId, signerVerifier, adapterConfig.maxSignatureAge, adapterMap), new I2b2AdminService(adapterDao, i2b2AdminDao, pmPoster, runQueryAdapter), adapterDao, adapterMappings) }) val shouldQuerySelf = "hub.shouldQuerySelf" lazy val localAdapterServiceOption: Option[AdapterRequestHandler] = if(shrineConfig.getOption(shouldQuerySelf,_.getBoolean).getOrElse(false)) { //todo give this a default value (of false) in the reference.conf for the Hub, and make it part of the Hub's apply(config) require(adapterService.isDefined, s"Self-querying requested because shrine.$shouldQuerySelf is true, but this node is not configured to have an adapter") adapterService } else None //todo eventually make this just another downstream node accessed via loopback val hubConfig = shrineConfig.getConfig("hub") //todo use an empty Set instead of Option[Set] private lazy val broadcastDestinations: Option[Set[NodeHandle]] = { if(hubConfig.getBoolean("create")) { Some(makeNodeHandles(hubConfig, keystoreTrustParam, nodeId, localAdapterServiceOption, breakdownTypes)) } else None } protected lazy val qepConfig = shrineConfig.getConfig("queryEntryPoint") lazy val queryEntryPointConfig: QepConfig = shrineConfig.getConfigured("queryEntryPoint", QepConfig(_)) protected lazy val queryEntryPointComponents:Option[QueryEntryPointComponents] = if(qepConfig.getBoolean("create")) { val broadcasterClient: BroadcasterClient = { //todo don't bother with a distinction between local and remote QEPs. Just use loopback. if (qepConfig.getOptionConfigured("broadcasterServiceEndpoint", EndpointConfig(_)).isEmpty) { //If broadcaster is local, we need a hub config require(broadcastDestinations.isDefined, s"The QEP's config implied a local hub (no broadcasterServiceEndpoint), but either no downstream nodes were configured, the hub was not configured, or the hub's configuration specified not to create it.") val broadcaster: AdapterClientBroadcaster = AdapterClientBroadcaster(broadcastDestinations.get, hubDao) InJvmBroadcasterClient(broadcaster) } else { //if broadcaster is remote, we need an endpoint //todo Just have an endpoint always, use loopback for local. require(queryEntryPointConfig.broadcasterServiceEndpoint.isDefined, "Non-local broadcaster requested, but no URL for the remote broadcaster is specified") PosterBroadcasterClient(makePoster(queryEntryPointConfig.broadcasterServiceEndpoint.get), breakdownTypes) } } val commonName: String = shrineCertCollection.myCommonName.getOrElse { val hostname = java.net.InetAddress.getLocalHost.getHostName warn(s"No common name available from ${shrineCertCollection.descriptor}. Using $hostname instead.") hostname } val broadcastService: BroadcastAndAggregationService = SigningBroadcastAndAggregationService(broadcasterClient, signerVerifier, queryEntryPointConfig.signingCertStrategy) val auditDao: AuditDao = new SquerylAuditDao(squerylInitializer, new HubTables) val authenticationType = queryEntryPointConfig.authenticationType - val authorizationType = queryEntryPointConfig.authorizationType + val authorizationType = queryEntryPointConfig.authorizationType //todo next target val authenticator: Authenticator = AuthStrategy.determineAuthenticator(authenticationType, pmPoster) - val authorizationService: QueryAuthorizationService = AuthStrategy.determineQueryAuthorizationService(qepConfig,authorizationType, shrineConfigurationBall, authenticator) + val authorizationService: QueryAuthorizationService = AuthStrategy.determineQueryAuthorizationService(qepConfig,authorizationType,authenticator) debug(s"authorizationService set to $authorizationService") Some(QueryEntryPointComponents( QepService( commonName, auditDao, authenticator, authorizationService, queryEntryPointConfig.includeAggregateResults, broadcastService, queryEntryPointConfig.maxQueryWaitTime, breakdownTypes, queryEntryPointConfig.collectQepAudit ), I2b2QepService( commonName, auditDao, authenticator, authorizationService, queryEntryPointConfig.includeAggregateResults, broadcastService, queryEntryPointConfig.maxQueryWaitTime, breakdownTypes, queryEntryPointConfig.collectQepAudit ), auditDao)) } else { None } private lazy val broadcasterOption = unpackHubComponents { for { hubConfig <- shrineConfigurationBall.hubConfig } yield { require(broadcastDestinations.isDefined, "This node is configured to be a hub, but no downstream nodes are defined") HubComponents(AdapterClientBroadcaster(broadcastDestinations.get, hubDao)) } } protected lazy val broadcasterMultiplexerService = { for { broadcaster <- broadcasterOption hubConfig <- shrineConfigurationBall.hubConfig } yield { BroadcasterMultiplexerService(broadcaster, hubConfig.maxQueryWaitTime) } } protected lazy val pmUrlString: String = pmEndpoint.url.toString protected lazy val ontologyMetadata: OntologyMetadata = { import scala.concurrent.duration._ //TODO: XXX: Un-hard-code max wait time param val ontClient: OntClient = new PosterOntClient(shrineConfig.getConfigured("hiveCredentials", HiveCredentials(_, HiveCredentials.ONT)), 1.minute, ontPoster) new OntClientOntologyMetadata(ontClient) } //TODO: Don't assume we're an adapter with an AdapterMappings (don't call .get) protected lazy val happyService: HappyShrineService = { new HappyShrineService( config = config, keystoreDescriptor = keyStoreDescriptor, shrineConfigObject = shrineConfigurationBall, certCollection = shrineCertCollection, signer = signerVerifier, pmPoster = pmPoster, ontologyMetadata = ontologyMetadata, adapterMappings = adapterMappings, auditDaoOption = queryEntryPointComponents.map(_.auditDao), adapterDaoOption = adapterDao, broadcasterOption = broadcasterOption, adapterOption = adapterService ) } protected lazy val happyResource: HappyShrineResource = new HappyShrineResource(happyService) protected lazy val statusJaxrs: StatusJaxrs = StatusJaxrs(config) protected lazy val shrineResource: Option[ShrineResource] = queryEntryPointComponents.map(x => ShrineResource(x.shrineService)) protected lazy val i2b2BroadcastResource: Option[I2b2BroadcastResource] = queryEntryPointComponents.map(x => new I2b2BroadcastResource(x.i2b2Service,breakdownTypes)) protected lazy val adapterResource: Option[AdapterResource] = adapterService.map(AdapterResource(_)) protected lazy val i2b2AdminResource: Option[I2b2AdminResource] = i2b2AdminService.map(I2b2AdminResource(_, breakdownTypes)) protected lazy val broadcasterMultiplexerResource: Option[BroadcasterMultiplexerResource] = broadcasterMultiplexerService.map(BroadcasterMultiplexerResource(_)) def makeHttpClient(keystoreCertCollection: KeyStoreCertCollection, endpoint: EndpointConfig): HttpClient = { import TrustParam.{AcceptAllCerts, SomeKeyStore} val trustParam = if (endpoint.acceptAllCerts) AcceptAllCerts else SomeKeyStore(keystoreCertCollection) JerseyHttpClient(trustParam, endpoint.timeout) } //todo here's the Adapter. Move to the adapter package. private final case class AdapterComponents(adapterService: AdapterService, i2b2AdminService: I2b2AdminService, adapterDao: AdapterDao, adapterMappings: AdapterMappings) //todo here's the QEP. Move to the QEP package. case class QueryEntryPointComponents(shrineService: QepService, i2b2Service: I2b2QepService, auditDao: AuditDao) //todo here's the Hub. Move to the hub package private final case class HubComponents(broadcaster: AdapterClientBroadcaster) //todo get rid of this private def adapterComponentsToTuple(option: Option[AdapterComponents]): (Option[AdapterService], Option[I2b2AdminService], Option[AdapterDao], Option[AdapterMappings]) = option match { case None => (None, None, None, None) case Some(AdapterComponents(a, b, c, d)) => (Option(a), Option(b), Option(c), Option(d)) } //todo get rid of this private def unpackHubComponents(option: Option[HubComponents]): Option[AdapterClientBroadcaster] = option.map(_.broadcaster) def poster(keystoreCertCollection: KeyStoreCertCollection)(endpoint: EndpointConfig): Poster = { val httpClient = makeHttpClient(keystoreCertCollection, endpoint) Poster(endpoint.url.toString, httpClient) } } diff --git a/apps/shrine-app/src/test/scala/net/shrine/wiring/AuthStrategyTest.scala b/apps/shrine-app/src/test/scala/net/shrine/wiring/AuthStrategyTest.scala index daa7278f9..39f064c98 100644 --- a/apps/shrine-app/src/test/scala/net/shrine/wiring/AuthStrategyTest.scala +++ b/apps/shrine-app/src/test/scala/net/shrine/wiring/AuthStrategyTest.scala @@ -1,86 +1,55 @@ package net.shrine.wiring -import java.net.URL - -import net.shrine.authentication.{AuthenticationType, Authenticator, PmAuthenticator} +import net.shrine.authentication.{AuthenticationType, PmAuthenticator} import net.shrine.authorization.{AllowsAllAuthorizationService, AuthorizationType} -import net.shrine.client.{EndpointConfig, Poster} -import net.shrine.crypto.SigningCertStrategy +import net.shrine.client.Poster import net.shrine.hms.authentication.EcommonsPmAuthenticator -import net.shrine.protocol.CredentialConfig -import net.shrine.qep.{AllowsAllAuthenticator, QepConfig} +import net.shrine.qep.AllowsAllAuthenticator import net.shrine.util.ShouldMatchersForJUnit import org.junit.Test /** * @author clint * @since Jul 2, 2014 */ final class AuthStrategyTest extends ShouldMatchersForJUnit { private[this] val pmPoster = Poster("http://example.com", null) @Test def testDefaultDetermineAuthenticator(): Unit = { import AuthenticationType._ intercept[Exception] { AuthStrategy.determineAuthenticator(null, pmPoster) } { val authenticator = AuthStrategy.determineAuthenticator(Pm, pmPoster).asInstanceOf[PmAuthenticator] authenticator.pmPoster should be(pmPoster) } { val authenticator = AuthStrategy.determineAuthenticator(Ecommons, pmPoster).asInstanceOf[EcommonsPmAuthenticator] authenticator.pmPoster should be(pmPoster) } { val authenticator = AuthStrategy.determineAuthenticator(NoAuthentication, pmPoster) authenticator should be(AllowsAllAuthenticator) } } @Test def testDefaultDetermineAuthorizationService(): Unit = { import AuthorizationType._ { - val authService = AuthStrategy.determineQueryAuthorizationService(null,NoAuthorization, null, null) + val authService = AuthStrategy.determineQueryAuthorizationService(qepConfig = null,authType = NoAuthorization, authenticator = null) authService should be(AllowsAllAuthorizationService) } - - { - val authenticationType = AuthenticationType.Ecommons - val authorizationType = HmsSteward - - import scala.concurrent.duration._ - - val sheriffUrl = "http://example.com/sheriff" - val sheriffCredentials = CredentialConfig(None, "u", "p") - - val shrineConfig = ShrineConfig( - None, //hub config - Some(QepConfig( - authenticationType, - authorizationType, - Some(EndpointConfig(new URL(sheriffUrl), acceptAllCerts = false, 42.minutes)), - Some(sheriffCredentials), - includeAggregateResults = false, - 1.minute, - None, - SigningCertStrategy.Attach, - collectQepAudit = false)), - null //adapterStatusQuery - ) //breakdown types - - val authenticator: Authenticator = AllowsAllAuthenticator - } } } \ No newline at end of file diff --git a/apps/shrine-app/src/test/scala/net/shrine/wiring/ShrineConfigTest.scala b/apps/shrine-app/src/test/scala/net/shrine/wiring/ShrineConfigTest.scala index 7a65fe7a1..53f5e0f7f 100644 --- a/apps/shrine-app/src/test/scala/net/shrine/wiring/ShrineConfigTest.scala +++ b/apps/shrine-app/src/test/scala/net/shrine/wiring/ShrineConfigTest.scala @@ -1,73 +1,67 @@ package net.shrine.wiring import com.typesafe.config.ConfigFactory import net.shrine.authentication.AuthenticationType import net.shrine.authorization.AuthorizationType import net.shrine.broadcaster.NodeListParserTest import net.shrine.client.EndpointConfigTest import net.shrine.crypto.SigningCertStrategy import net.shrine.util.ShouldMatchersForJUnit import org.junit.Test /** * @author clint * @since Feb 6, 2013 */ final class ShrineConfigTest extends ShouldMatchersForJUnit { private def shrineConfig(baseFileName: String, loadBreakdownsFile: Boolean = true) = { val baseConfig = ConfigFactory.load(baseFileName) val breakdownConfig = ConfigFactory.load("breakdowns") val config = if(loadBreakdownsFile) baseConfig.withFallback(breakdownConfig) else baseConfig ShrineConfig(config) } import scala.concurrent.duration._ @Test def testApply() { import NodeListParserTest.node import EndpointConfigTest.endpoint val conf = shrineConfig("shrine") conf.queryEntryPointConfig.get.authenticationType should be(AuthenticationType.Ecommons) conf.queryEntryPointConfig.get.authorizationType should be(AuthorizationType.HmsSteward) - - conf.queryEntryPointConfig.get.sheriffEndpoint.get should equal(endpoint("http://localhost:8080/shrine-hms-authorization/queryAuthorization")) - - conf.queryEntryPointConfig.get.sheriffCredentials.get.domain should be(None) - conf.queryEntryPointConfig.get.sheriffCredentials.get.username should be("sheriffUsername") - conf.queryEntryPointConfig.get.sheriffCredentials.get.password should be("sheriffPassword") conf.queryEntryPointConfig.get.broadcasterIsLocal should be(false) conf.queryEntryPointConfig.get.broadcasterServiceEndpoint.get should equal(endpoint("http://example.com/shrine/rest/broadcaster/broadcast")) conf.queryEntryPointConfig.get.maxQueryWaitTime should equal(5.minutes) conf.queryEntryPointConfig.get.signingCertStrategy should equal(SigningCertStrategy.Attach) conf.queryEntryPointConfig.get.includeAggregateResults should equal(false) conf.adapterStatusQuery should equal("""\\SHRINE\SHRINE\Diagnoses\Mental Illness\Disorders usually diagnosed in infancy, childhood, or adolescence\Pervasive developmental disorders\Infantile autism, current or active state\""") } @Test def testApplyOptionalFields() { val conf = shrineConfig("shrine-no-optional-configs", loadBreakdownsFile = false) conf.hubConfig should be(None) conf.queryEntryPointConfig should be(None) } @Test def testApplySomeOptionalFields() { val conf = shrineConfig("shrine-some-optional-props") conf.queryEntryPointConfig.get.authenticationType should be(AuthenticationType.Pm) conf.queryEntryPointConfig.get.authorizationType should be(AuthorizationType.NoAuthorization) conf.queryEntryPointConfig.get.signingCertStrategy should be(SigningCertStrategy.DontAttach) } } \ No newline at end of file diff --git a/commons/config/src/main/scala/net/shrine/config/Keys.scala b/commons/config/src/main/scala/net/shrine/config/Keys.scala index 4d7a5a962..00d9dd6a5 100644 --- a/commons/config/src/main/scala/net/shrine/config/Keys.scala +++ b/commons/config/src/main/scala/net/shrine/config/Keys.scala @@ -1,37 +1,35 @@ package net.shrine.config /** * @author clint * @since Jan 17, 2014 * * Keys for Shrine */ //todo distribute to where they are used once the rest of config is cleaned up object Keys { val crcEndpoint = "crcEndpoint" - val sheriffEndpoint = "sheriffEndpoint" - val sheriffCredentials = "sheriffCredentials" val shrineSteward = "shrineSteward" val ontProjectId = "ontProjectId" val crcProjectId = "crcProjectId" val setSizeObfuscation = "setSizeObfuscation" val isAdapter = "isAdapter" val isBroadcaster = "isBroadcaster" val includeAggregateResults = "includeAggregateResults" val adapterLockoutAttemptsThreshold = "adapterLockoutAttemptsThreshold" val maxQueryWaitTime = "maxQueryWaitTime" val networkStatusQuery = "networkStatusQuery" val adapterMappingsFileName = "adapterMappingsFileName" val adapterMappingsFileType = "adapterMappingsFileType" val downstreamNodes = "downstreamNodes" val maxSignatureAge = "maxSignatureAge" val adapter = "adapter" val hub = "hub" val queryEntryPoint = "queryEntryPoint" //todo remove once it's not used anymore val broadcasterIsLocal = "broadcasterIsLocal" val broadcasterServiceEndpoint = "broadcasterServiceEndpoint" val immediatelyRunIncomingQueries = "immediatelyRunIncomingQueries" val authenticationType = "authenticationType" val authorizationType = "authorizationType" val attachSigningCert = "attachSigningCert" } diff --git a/hms-support/hms-core/src/main/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationService.scala b/hms-support/hms-core/src/main/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationService.scala index 9d48903c0..3a8de442d 100644 --- a/hms-support/hms-core/src/main/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationService.scala +++ b/hms-support/hms-core/src/main/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationService.scala @@ -1,73 +1,89 @@ package net.shrine.hms.authorization +import java.net.URL + +import com.typesafe.config.Config import net.shrine.authentication.{AuthenticationResult, Authenticator} import net.shrine.authorization.{AuthorizationResult, QueryAuthorizationService} +import net.shrine.client.EndpointConfig import net.shrine.log.Loggable import net.shrine.problem.{AbstractProblem, ProblemSources} -import net.shrine.protocol.{AuthenticationInfo, ErrorResponse, ReadApprovedQueryTopicsRequest, ReadApprovedQueryTopicsResponse, RunQueryRequest} +import net.shrine.protocol.{CredentialConfig, AuthenticationInfo, ErrorResponse, ReadApprovedQueryTopicsRequest, ReadApprovedQueryTopicsResponse, RunQueryRequest} +import net.shrine.config.ConfigExtensions /** * @author Bill Simons * @since 1/30/12 * @see http://cbmi.med.harvard.edu * @see http://chip.org *

* NOTICE: This software comes with NO guarantees whatsoever and is * licensed as Lgpl Open Source * @see http://www.gnu.org/licenses/lgpl.html */ final case class HmsDataStewardAuthorizationService( - sheriffClient: SheriffClient, - authenticator: Authenticator) extends QueryAuthorizationService with Loggable { + sheriffClient: SheriffClient, + authenticator: Authenticator + ) extends QueryAuthorizationService with Loggable { import net.shrine.hms.authorization.HmsDataStewardAuthorizationService._ override def readApprovedEntries(request: ReadApprovedQueryTopicsRequest): Either[ErrorResponse, ReadApprovedQueryTopicsResponse] = { val authn = request.authn authenticate(authn) match { case None => Left(ErrorResponse(HMSNotAuthenticatedProblem(authn))) case Some(ecommonsUsername) => val topics = sheriffClient.getApprovedEntries(ecommonsUsername) Right(ReadApprovedQueryTopicsResponse(topics)) } } override def authorizeRunQueryRequest(request: RunQueryRequest): AuthorizationResult = { val authn = request.authn if (request.topicId.isEmpty) { AuthorizationResult.NotAuthorized(s"HMS queries require a topic id; couldn't authenticate user ${toDomainAndUser(authn)}") } else { authenticate(authn) match { case None => AuthorizationResult.NotAuthorized(s"Requested topic is not approved; couldn't authenticate user ${toDomainAndUser(authn)}") case Some(ecommonsUsername) => sheriffClient.isAuthorized(ecommonsUsername, request.topicId.get, request.queryDefinition.toI2b2String) } } } private def authenticate(authn: AuthenticationInfo): Option[String] = { val authenticationResult = authenticator.authenticate(authn) identifyEcommonsUsername(authenticationResult) } } object HmsDataStewardAuthorizationService { + + def apply(config:Config,authenticator: Authenticator):HmsDataStewardAuthorizationService = { + val endpointUrl = config.getString("sheriffEndpoint"+EndpointConfig.Keys.url) + val credentials = config.getConfigured("sheriffCredentials", CredentialConfig(_)) + + val sheriffClient = JerseySheriffClient(endpointUrl, credentials.username, credentials.password) + + HmsDataStewardAuthorizationService(sheriffClient, authenticator) + } + private def toDomainAndUser(authn: AuthenticationInfo): String = s"${authn.domain}:${authn.username}" def identifyEcommonsUsername(authenticationResult: AuthenticationResult): Option[String] = authenticationResult match { case AuthenticationResult.Authenticated(_, ecommonsUsername) => Option(ecommonsUsername) case _ => None } } case class HMSNotAuthenticatedProblem(authn: AuthenticationInfo) extends AbstractProblem(ProblemSources.Qep){ override val summary = s"Can not authenticate ${authn.domain}:${authn.username}." override val description = s"Can not authenticate ${authn.domain}:${authn.username}." } \ No newline at end of file diff --git a/hms-support/hms-core/src/test/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationServiceTest.scala b/hms-support/hms-core/src/test/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationServiceTest.scala index 9cc66ca07..359ab5e95 100644 --- a/hms-support/hms-core/src/test/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationServiceTest.scala +++ b/hms-support/hms-core/src/test/scala/net/shrine/hms/authorization/HmsDataStewardAuthorizationServiceTest.scala @@ -1,165 +1,158 @@ package net.shrine.hms.authorization -import net.shrine.authorization.AuthorizationResult.{NotAuthorized, Authorized} +import net.shrine.authentication.{AuthenticationResult, Authenticator} +import net.shrine.authorization.AuthorizationResult.{Authorized, NotAuthorized} +import net.shrine.protocol.{ApprovedTopic, AuthenticationInfo, Credential, ErrorResponse, ReadApprovedQueryTopicsRequest, ReadApprovedQueryTopicsResponse, RunQueryRequest} +import net.shrine.protocol.query.{QueryDefinition, Term} import net.shrine.util.ShouldMatchersForJUnit import org.junit.Test -import net.shrine.protocol.ApprovedTopic -import org.scalatest.mock.EasyMockSugar -import net.shrine.authentication.AuthenticationResult -import net.shrine.authentication.Authenticator -import net.shrine.protocol.AuthenticationInfo -import net.shrine.protocol.Credential -import net.shrine.protocol.ReadApprovedQueryTopicsRequest -import net.shrine.protocol.ErrorResponse -import net.shrine.protocol.RunQueryRequest -import net.shrine.protocol.query.QueryDefinition -import net.shrine.protocol.query.Term -import net.shrine.protocol.ReadApprovedQueryTopicsResponse /** * @author Bill Simons * @since 1/30/12 * @see http://cbmi.med.harvard.edu * @see http://chip.org *

* NOTICE: This software comes with NO guarantees whatsoever and is * licensed as Lgpl Open Source * @see http://www.gnu.org/licenses/lgpl.html */ final class HmsDataStewardAuthorizationServiceTest extends ShouldMatchersForJUnit { @Test - def testIdentifyEcommonsUsername: Unit = { - import HmsDataStewardAuthorizationService.identifyEcommonsUsername + def testIdentifyEcommonsUsername(): Unit = { import AuthenticationResult._ + import HmsDataStewardAuthorizationService.identifyEcommonsUsername identifyEcommonsUsername(NotAuthenticated("", "", "")) should be(None) val ecommonsId = "foo" identifyEcommonsUsername(Authenticated("", ecommonsId)) should be(Some(ecommonsId)) } import HmsDataStewardAuthorizationServiceTest._ + import scala.concurrent.duration._ - private val authn = AuthenticationInfo("d", "u", Credential("p", false)) + private val authn = AuthenticationInfo("d", "u", Credential("p", isToken = false)) @Test - def testReadApprovedEntriesNotAuthenticated { - val service = HmsDataStewardAuthorizationService(null, NeverAuthenticatesAuthenticator) + def testReadApprovedEntriesNotAuthenticated() { + val service = new HmsDataStewardAuthorizationService(null, NeverAuthenticatesAuthenticator) val result = service.readApprovedEntries(ReadApprovedQueryTopicsRequest("projectId", 0.minutes, authn, authn.username)) val Left(errorResponse: ErrorResponse) = result + //noinspection ScalaUnnecessaryParentheses errorResponse.errorMessage should not be (null) } @Test - def testReadApprovedEntriesAuthenticated { + def testReadApprovedEntriesAuthenticated() { val topic = ApprovedTopic(123L, "blarg") val ecommonsUsername = "abc123" val mockSheriffClient = MockSheriffClient(topics = Seq(topic)) val service = HmsDataStewardAuthorizationService(mockSheriffClient, AlwaysAuthenticatesAuthenticator(ecommonsUsername)) val result = service.readApprovedEntries(ReadApprovedQueryTopicsRequest("projectId", 0.minutes, authn, authn.username)) val Right(ReadApprovedQueryTopicsResponse(Seq(actualTopic))) = result actualTopic should equal(topic) mockSheriffClient.Params.user should be(null) mockSheriffClient.Params.topicId should be(null) mockSheriffClient.Params.queryText should be(null) mockSheriffClient.Params.ecommonsUsername should be(ecommonsUsername) } @Test - def testAuthorizeRunQueryRequestNotAuthenticated { - val service = HmsDataStewardAuthorizationService(null, NeverAuthenticatesAuthenticator) + def testAuthorizeRunQueryRequestNotAuthenticated() { + val service = new HmsDataStewardAuthorizationService(null, NeverAuthenticatesAuthenticator) def doTest(topicId: Option[String],topicName:Option[String]): Unit = { val result = service.authorizeRunQueryRequest(RunQueryRequest("projectId", 0.minutes, authn, topicId, topicName, Set.empty, QueryDefinition("foo", Term("foo")))) result.isAuthorized should be(false) } doTest(None,None) doTest(Some("topicId"),Some("Topic Name")) } @Test - def testAuthorizeRunQueryRequestAuthenticated { + def testAuthorizeRunQueryRequestAuthenticated() { def doTest(isAuthorized: Boolean, topicId: Option[String], topicName:Option[String]): Unit = { val ecommonsUsername = "abc123" val queryDef = QueryDefinition("foo", Term("foo")) val mockSheriffClient = MockSheriffClient(authorized = isAuthorized) val service = HmsDataStewardAuthorizationService(mockSheriffClient, AlwaysAuthenticatesAuthenticator(ecommonsUsername)) val result = service.authorizeRunQueryRequest(RunQueryRequest("projectId", 0.minutes, authn, topicId, topicName, Set.empty, queryDef)) val expectedIsAuthorized = isAuthorized && topicId.isDefined result.isAuthorized should be(expectedIsAuthorized) if (topicId.isDefined) { mockSheriffClient.Params.user should equal(ecommonsUsername) mockSheriffClient.Params.topicId should equal(topicId.get) mockSheriffClient.Params.queryText should equal(queryDef.toI2b2String) mockSheriffClient.Params.ecommonsUsername should be(null) } else { mockSheriffClient.Params.user should be(null) mockSheriffClient.Params.topicId should be(null) mockSheriffClient.Params.queryText should be(null) mockSheriffClient.Params.ecommonsUsername should be(null) } } - doTest(true, Some("topic123"), Some("Topic Name")) - doTest(false, Some("topic123"), Some("Topic Name")) - doTest(false, Some("topic123"), None) - doTest(true, None, None) - doTest(false, None, None) + doTest(isAuthorized = true, Some("topic123"), Some("Topic Name")) + doTest(isAuthorized = false, Some("topic123"), Some("Topic Name")) + doTest(isAuthorized = false, Some("topic123"), None) + doTest(isAuthorized = true, None, None) + doTest(isAuthorized = false, None, None) } } object HmsDataStewardAuthorizationServiceTest { object NeverAuthenticatesAuthenticator extends Authenticator { override def authenticate(authn: AuthenticationInfo) = AuthenticationResult.NotAuthenticated(authn.domain, authn.username, "foo") } final case class AlwaysAuthenticatesAuthenticator(ecommonsUsername: String) extends Authenticator { override def authenticate(authn: AuthenticationInfo) = AuthenticationResult.Authenticated(authn.domain, ecommonsUsername) } final case class MockSheriffClient(authorized: Boolean = false, topics: Seq[ApprovedTopic] = Nil) extends SheriffClient { object Params { var ecommonsUsername: String = _ var user: String = _ var topicId: String = _ var queryText: String = _ } override def getApprovedEntries(ecommonsUsername: String): Seq[ApprovedTopic] = { Params.ecommonsUsername = ecommonsUsername topics } override def isAuthorized(user: String, topicId: String, queryText: String) = { Params.user = user Params.topicId = topicId Params.queryText = queryText if(authorized) Authorized(Some((topicId,"Mock Topic"))) else NotAuthorized("Mock authorization failure") } } } \ No newline at end of file diff --git a/qep/service/src/main/scala/net/shrine/qep/QepConfig.scala b/qep/service/src/main/scala/net/shrine/qep/QepConfig.scala index 828f95ad3..ff1de7693 100644 --- a/qep/service/src/main/scala/net/shrine/qep/QepConfig.scala +++ b/qep/service/src/main/scala/net/shrine/qep/QepConfig.scala @@ -1,78 +1,68 @@ package net.shrine.qep import com.typesafe.config.Config import net.shrine.authentication.AuthenticationType import net.shrine.authorization.AuthorizationType import net.shrine.client.EndpointConfig -import net.shrine.config.{DurationConfigParser, Keys,ConfigExtensions} +import net.shrine.config.{ConfigExtensions, DurationConfigParser, Keys} import net.shrine.crypto.SigningCertStrategy import net.shrine.log.Log -import net.shrine.protocol.CredentialConfig import scala.concurrent.duration.Duration /** * @author clint * @since Feb 28, 2014 */ final case class QepConfig ( authenticationType: AuthenticationType, authorizationType: AuthorizationType, - //NB: optional, only needed for HMS - sheriffEndpoint: Option[EndpointConfig], - //NB: optional, only needed for HMS - sheriffCredentials: Option[CredentialConfig], includeAggregateResults: Boolean, maxQueryWaitTime: Duration, broadcasterServiceEndpoint: Option[EndpointConfig], signingCertStrategy: SigningCertStrategy, collectQepAudit:Boolean) { Log.debug(s"QepConfig collectQepAudit is $collectQepAudit") def broadcasterIsLocal: Boolean = broadcasterServiceEndpoint.isEmpty } object QepConfig { val defaultAuthenticationType: AuthenticationType = AuthenticationType.Pm val defaultAuthorizationType: AuthorizationType = AuthorizationType.NoAuthorization def apply(config: Config): QepConfig = { import Keys._ QepConfig( //todo put these default values in reference.conf if you decide to use one optionalAuthenticationType(authenticationType,config).getOrElse(defaultAuthenticationType), optionalAuthorizationType(authorizationType,config).getOrElse(defaultAuthorizationType), - config.getOptionConfigured(sheriffEndpoint, EndpointConfig(_)), - credentialsOption(sheriffCredentials,config), config.getBoolean(includeAggregateResults), DurationConfigParser(config.getConfig("maxQueryWaitTime")), config.getOptionConfigured(broadcasterServiceEndpoint, EndpointConfig(_)), signingCertAttachmentStrategy(attachSigningCert,config), //todo change to shrine.queryEntryPoint... QepConfigSource.config.getBoolean("shrine.queryEntryPoint.audit.collectQepAudit") ) } def optionalAuthorizationType(k: String,config: Config): Option[AuthorizationType] = { config.getOption(k,_.getString).flatMap(AuthorizationType.valueOf) } def optionalAuthenticationType(k: String,config: Config): Option[AuthenticationType] = { config.getOption(k,_.getString).flatMap(AuthenticationType.valueOf) } def signingCertAttachmentStrategy(k: String,config: Config): SigningCertStrategy = { val attachSigningCerts: Boolean = config.getOption(k, _.getBoolean).getOrElse(false) import SigningCertStrategy._ if(attachSigningCerts) Attach else DontAttach } - - def credentialsOption(k: String,config: Config):Option[CredentialConfig] = config.getOptionConfigured(k, CredentialConfig(_)) - } diff --git a/qep/service/src/test/scala/net/shrine/qep/QepConfigTest.scala b/qep/service/src/test/scala/net/shrine/qep/QepConfigTest.scala index fceaa59f8..296067e3e 100644 --- a/qep/service/src/test/scala/net/shrine/qep/QepConfigTest.scala +++ b/qep/service/src/test/scala/net/shrine/qep/QepConfigTest.scala @@ -1,60 +1,50 @@ package net.shrine.qep import com.typesafe.config.ConfigFactory import net.shrine.authentication.AuthenticationType import net.shrine.authorization.AuthorizationType import net.shrine.config.Keys import net.shrine.util.ShouldMatchersForJUnit import org.junit.Test /** * @author clint * @since Mar 3, 2014 */ final class QepConfigTest extends ShouldMatchersForJUnit { private def entryPointServiceConfig(baseFileName: String) = QepConfig(ConfigFactory.load(baseFileName).getConfig(s"shrine.${Keys.queryEntryPoint}")) @Test - def testApply { + def testApply() { val conf: QepConfig = entryPointServiceConfig("shrine") import scala.concurrent.duration._ conf.authenticationType should be(AuthenticationType.Ecommons) conf.authorizationType should be(AuthorizationType.HmsSteward) conf.broadcasterIsLocal should be(false) conf.broadcasterServiceEndpoint.get.acceptAllCerts should be(true) conf.broadcasterServiceEndpoint.get.timeout should be(1.second) conf.broadcasterServiceEndpoint.get.url.toString should equal("http://example.com/shrine/rest/broadcaster/broadcast") conf.includeAggregateResults should equal(false) conf.maxQueryWaitTime should equal(5.minutes) - - conf.sheriffCredentials.get.domain should be(None) - conf.sheriffCredentials.get.username should be("sheriffUsername") - conf.sheriffCredentials.get.password should be("sheriffPassword") - - conf.sheriffEndpoint.get.acceptAllCerts should be(true) - conf.sheriffEndpoint.get.timeout should be(1.second) - conf.sheriffEndpoint.get.url.toString should be("http://localhost:8080/shrine-hms-authorization/queryAuthorization") } @Test - def testApplyOptionalFields { + def testApplyOptionalFields() { val conf = entryPointServiceConfig("shrine-some-optional-props") conf.authenticationType should be(AuthenticationType.Pm) conf.authorizationType should be(AuthorizationType.NoAuthorization) conf.broadcasterIsLocal should be(true) conf.broadcasterServiceEndpoint should be(None) - conf.sheriffCredentials should be(None) - conf.sheriffEndpoint should be(None) } } \ No newline at end of file