All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.beangle.security.authc.authentication.scala Maven / Gradle / Ivy

There is a newer version: 4.3.21
Show newest version
/*
 * Copyright (C) 2005, The Beangle Software.
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program.  If not, see .
 */

package org.beangle.security.authc

import org.beangle.commons.logging.Logging
import org.beangle.security.realm.Realm
import org.beangle.security.session.Session

/**
  * Authentication Manager
  */
trait Authenticator {

  @throws(classOf[AuthenticationException])
  def authenticate(token: AuthenticationToken): Account
}

trait AuthenticationListener {
  def onSuccess(token: AuthenticationToken, info: Account): Unit

  def onFailure(token: AuthenticationToken, cause: AuthenticationException): Unit

  def onLogout(info: Session): Unit
}

abstract class AbstractAuthenticator extends Authenticator with Logging {

  var listeners: List[AuthenticationListener] = List.empty

  override def authenticate(token: AuthenticationToken): Account = {
    try {
      val info = doAuthenticate(token)
      if (info == null) throw new AuthenticationException(s"No account information found for authentication token [$token]", token)
      notifySuccess(token, info)
      info
    } catch {
      case ae: AuthenticationException =>
        try {
          notifyFailure(token, ae)
        } catch {
          case e2: Throwable =>
            logger.warn("Unable to send notification for failed authentication attempt - listener error?.  " +
              "Please check your AuthenticationListener implementation(s).  Logging sending exception " +
              "and propagating original AuthenticationException instead...", e2)
        }
        throw ae
      case e: Throwable => throw e
    }
  }

  def doAuthenticate(token: AuthenticationToken): Account

  @inline
  protected final def notifySuccess(token: AuthenticationToken, info: Account): Unit = {
    listeners.foreach(listener => listener.onSuccess(token, info))
  }

  @inline
  protected final def notifyFailure(token: AuthenticationToken, ae: AuthenticationException): Unit = {
    listeners.foreach(listener => listener.onFailure(token, ae))
  }

}

/** How we authenticate user within multiple realms
  */
trait RealmAuthenticationStrategy {

  @throws(classOf[AuthenticationException])
  def authenticate(realms: List[Realm], token: AuthenticationToken): Account

  protected def returnOrRaise(info: Account, token: AuthenticationToken, t: Throwable): Account = {
    if (null == info) throw if (null == t) new AuthenticationException(s"Realm not found for $token", token.principal, null) else t
    else info
  }
}

/** First win,imply at least one and ignore remainders
  */
object FirstSuccessfulStrategy extends RealmAuthenticationStrategy with Logging {

  override def authenticate(realms: List[Realm], token: AuthenticationToken): Account = {
    val realmIter = realms.iterator
    var info: Account = null
    var lastException: Throwable = null
    while (null == info && realmIter.hasNext) {
      val realm = realmIter.next()
      if (realm.supports(token)) {
        try {
          info = realm.getAccount(token)
        } catch {
          case t: Throwable =>
            lastException = t
            logger.debug(s"Realm [$realm] threw an exception during a multi-realm authentication attempt:", t)
        }
      }
    }
    returnOrRaise(info, token, lastException)
  }
}

/** Realm Authenticator
  */
class RealmAuthenticator(val reams: List[Realm]) extends AbstractAuthenticator with Logging {
  var strategy: RealmAuthenticationStrategy = FirstSuccessfulStrategy

  override def doAuthenticate(token: AuthenticationToken): Account = {
    strategy.authenticate(reams, token)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy