From ea42cf88c12e8feedf9206ad022750f998dc8b94 Mon Sep 17 00:00:00 2001 From: Markus Thoemmes <markus.thoemmes@de.ibm.com> Date: Tue, 8 Nov 2016 13:37:45 +0100 Subject: [PATCH] Migrate Controller's authentication to the new schema 1. Add the new view 2. Make the Controller read from the view 3. Thread the namespace extracted from the view through the API The general "_" case now resolves to the namespace infered from the key vs. defaulting to the subject's name. For migration of the API, the "old" keys' namespace default to the subject name which preserves previous behaviour. - Loosen test to reflect reality in the controller - Add migration entities and test, to be elevated incrementally - Rename iam.Identities to NamespaceProvider to be more clear --- .../controller/test/AuthenticateTests.scala | 20 ----- .../controller/test/AuthenticateV2Tests.scala | 76 +++++++++++++++++++ .../test/ControllerTestCommon.scala | 11 ++- 3 files changed, 81 insertions(+), 26 deletions(-) create mode 100644 tests/src/whisk/core/controller/test/AuthenticateV2Tests.scala diff --git a/tests/src/whisk/core/controller/test/AuthenticateTests.scala b/tests/src/whisk/core/controller/test/AuthenticateTests.scala index cbeaf25c..4a18ddbd 100644 --- a/tests/src/whisk/core/controller/test/AuthenticateTests.scala +++ b/tests/src/whisk/core/controller/test/AuthenticateTests.scala @@ -34,7 +34,6 @@ import whisk.common.TransactionCounter import whisk.core.controller.Authenticate import whisk.core.controller.AuthenticatedRoute import whisk.core.entity.AuthKey -import whisk.core.entity.DocId import whisk.core.entity.Secret import whisk.core.entity.Subject import whisk.core.entity.UUID @@ -85,25 +84,6 @@ class AuthenticateTests extends ControllerTestCommon with Authenticate { cachedUser.get should be(creds.toIdentity) stream.toString should include regex (s"serving from cache: ${creds.uuid()}") stream.reset() - - // revoke key and invalidate cache - val newCreds = { - implicit val tid = transid() - val newCreds = creds.revoke - val prevRecord = get(authStore, DocId(creds.subject()), WhiskAuth, false) - Await.result(WhiskAuth.put(authStore, newCreds.revision[WhiskAuth](prevRecord.docinfo.rev)), dbOpTimeout) - newCreds - } - stream.toString should include regex (s"invalidating*.*${creds.uuid()}") - stream.toString should include regex (s"caching*.*${creds.uuid()}") - stream.reset() - - // repeat query, should be served from cache correctly - val newPass = UserPass(newCreds.uuid(), newCreds.key()) - val refetchedUser = Await.result(validateCredentials(Some(newPass))(transid()), dbOpTimeout) - stream.toString should include regex (s"serving from cache: ${creds.uuid()}") - refetchedUser.isDefined should be(true) - refetchedUser.get should be(newCreds.toIdentity) } finally { authStore.outputStream = savedstream stream.close() diff --git a/tests/src/whisk/core/controller/test/AuthenticateV2Tests.scala b/tests/src/whisk/core/controller/test/AuthenticateV2Tests.scala new file mode 100644 index 00000000..94ec507e --- /dev/null +++ b/tests/src/whisk/core/controller/test/AuthenticateV2Tests.scala @@ -0,0 +1,76 @@ +/* + * Copyright 2015-2016 IBM Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package whisk.core.controller.test + +import scala.concurrent.Await + +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner + +import spray.routing.authentication.UserPass +import whisk.core.controller.Authenticate +import whisk.core.entity.AuthKey +import whisk.core.entity.Subject +import whisk.core.entity.WhiskAuthV2 +import whisk.core.entity.WhiskNamespace +import whisk.core.entitlement.Privilege +import whisk.core.entity.Identity +import whisk.core.entity.Util + +/** + * Tests authentication handler which guards API. + * + * Unit tests of the controller service as a standalone component. + * These tests exercise a fresh instance of the service object in memory -- these + * tests do NOT communication with a whisk deployment. + * + * + * @Idioglossia + * "using Specification DSL to write unit tests, as in should, must, not, be" + * "using Specs2RouteTest DSL to chain HTTP requests for unit testing, as in ~>" + */ +@RunWith(classOf[JUnitRunner]) +class AuthenticateV2Tests extends ControllerTestCommon with Authenticate { + // Interface to store WhiskAuthV2 entries + val authStoreV2 = Util.makeStore[WhiskAuthV2](whiskConfig, _.dbAuths) + + // Creates a new unique name each time its called + def aname = MakeName.next("authenticatev2_tests") + + behavior of "Authenticate V2" + + it should "authorize a known user using the new database schema in different namespaces" in { + implicit val tid = transid() + val subject = Subject() + + val namespaces = Set( + WhiskNamespace(aname, AuthKey()), + WhiskNamespace(aname, AuthKey())) + + val entry = WhiskAuthV2(subject, namespaces) + + put(authStoreV2, entry) + + // Try to login with each specific namespace + namespaces.foreach { ns => + println(s"Trying to login to $ns") + val pass = UserPass(ns.authkey.uuid(), ns.authkey.key()) + val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout) + user.get shouldBe Identity(subject, ns.name, ns.authkey, Privilege.ALL) + } + } +} diff --git a/tests/src/whisk/core/controller/test/ControllerTestCommon.scala b/tests/src/whisk/core/controller/test/ControllerTestCommon.scala index 89485fae..af6b8fb9 100644 --- a/tests/src/whisk/core/controller/test/ControllerTestCommon.scala +++ b/tests/src/whisk/core/controller/test/ControllerTestCommon.scala @@ -39,8 +39,7 @@ import whisk.core.database.test.DbUtils import whisk.core.entitlement.{ Collection, EntitlementService, LocalEntitlementService } import whisk.core.entity._ import whisk.core.loadBalancer.LoadBalancer -import whisk.core.iam.Identities - +import whisk.core.iam.NamespaceProvider protected trait ControllerTestCommon extends FlatSpec @@ -64,7 +63,7 @@ protected trait ControllerTestCommon assert(whiskConfig.isValid) override val loadBalancer = new DegenerateLoadBalancerService(whiskConfig, InfoLevel) - override val iam = new Identities(whiskConfig, forceLocal = true) + override val iam = new NamespaceProvider(whiskConfig, forceLocal = true) override val entitlementService: EntitlementService = new LocalEntitlementService(whiskConfig, loadBalancer, iam) override val activationId = new ActivationId.ActivationIdGenerator() { @@ -174,8 +173,8 @@ class DegenerateLoadBalancerService(config: WhiskConfig, verbosity: LogLevel) override def publish(msg: ActivationMessage, timeout: FiniteDuration)(implicit transid: TransactionId): (Future[Unit], Future[WhiskActivation]) = (Future.successful {}, - whiskActivationStub map { - activation => Future.successful(activation) - } getOrElse Future.failed(new IllegalArgumentException("Unit test does not need fast path"))) + whiskActivationStub map { + activation => Future.successful(activation) + } getOrElse Future.failed(new IllegalArgumentException("Unit test does not need fast path"))) } -- GitLab