blob: 10703f09347b7646184f5740be81af95ec31883a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.openwhisk.core.controller
import akka.actor.ActorSystem
import akka.http.scaladsl.model.headers._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.directives.{AuthenticationDirective, AuthenticationResult}
import org.apache.openwhisk.common.{Logging, TransactionId}
import org.apache.openwhisk.core.database.NoDocumentException
import org.apache.openwhisk.core.entity._
import org.apache.openwhisk.core.entity.types.AuthStore
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
object BasicAuthenticationDirective extends AuthenticationDirectiveProvider {
def validateCredentials(credentials: Option[BasicHttpCredentials])(implicit transid: TransactionId,
ec: ExecutionContext,
logging: Logging,
authStore: AuthStore): Future[Option[Identity]] = {
credentials flatMap { pw =>
Try {
// authkey deserialization is wrapped in a try to guard against malformed values
val authkey = BasicAuthenticationAuthKey(UUID(pw.username), Secret(pw.password))
val future = Identity.get(authStore, authkey) map { result =>
if (authkey == result.authkey) {
logging.debug(this, s"authentication valid")
Some(result)
} else {
logging.debug(this, s"authentication not valid")
None
}
} recover {
case _: NoDocumentException | _: IllegalArgumentException =>
logging.debug(this, s"authentication not valid")
None
}
future.failed.foreach(t => logging.error(this, s"authentication error: $t"))
future
}.toOption
} getOrElse {
credentials.foreach(_ => logging.debug(this, s"credentials are malformed"))
Future.successful(None)
}
}
/** Creates HTTP BasicAuth handler */
def basicAuth[A](verify: Option[BasicHttpCredentials] => Future[Option[A]]): AuthenticationDirective[A] = {
extractExecutionContext.flatMap { implicit ec =>
authenticateOrRejectWithChallenge[BasicHttpCredentials, A] { creds =>
verify(creds).map {
case Some(t) => AuthenticationResult.success(t)
case None => AuthenticationResult.failWithChallenge(HttpChallenges.basic("OpenWhisk secure realm"))
}
}
}
}
def identityByNamespace(
namespace: EntityName)(implicit transid: TransactionId, system: ActorSystem, authStore: AuthStore) = {
Identity.get(authStore, namespace)
}
def authenticate(implicit transid: TransactionId,
authStore: AuthStore,
logging: Logging): AuthenticationDirective[Identity] = {
extractExecutionContext.flatMap { implicit ec =>
basicAuth(validateCredentials)
}
}
}