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

org.beangle.security.web.authc.preauth.scala Maven / Gradle / Ivy

package org.beangle.security.web

import java.util.Date

import org.beangle.commons.codec.digest.Digests
import org.beangle.commons.lang.{ Objects, Strings }
import org.beangle.commons.logging.Logging
import org.beangle.commons.web.filter.GenericHttpFilter
import org.beangle.commons.web.util.RequestUtils
import org.beangle.security.authc.{ AbstractAccountRealm, Account, AccountStore, AuthenticationException, AuthenticationInfo, AuthenticationToken }
import org.beangle.security.context.SecurityContext
import org.beangle.security.mgt.SecurityManager
import org.beangle.security.session.Session
import org.beangle.security.web.authc.WebDetails
import org.beangle.security.web.session.{ DefaultSessionIdPolicy, SessionIdPolicy }

import javax.servlet.{ FilterChain, ServletRequest, ServletResponse }
import javax.servlet.http.{ HttpServletRequest, HttpServletResponse }

trait PreauthAliveChecker {
  def check(session: Session, request: HttpServletRequest): Boolean
}

/**
 * Preauth Authentication Token
 */
class PreauthToken(val principal: Any) extends AuthenticationToken {

  var details: Map[String, Any] = Map.empty

  def credentials: Any = null

  override def equals(obj: Any): Boolean = {
    obj match {
      case test: PreauthToken =>
        Objects.equalsBuilder.add(principal, test.principal)
          .add(details, test.details)
          .isEquals
      case _ => false
    }
  }

}

abstract class AbstractPreauthFilter(val securityManager: SecurityManager) extends GenericHttpFilter with Logging {

  var aliveChecker: PreauthAliveChecker = _
  var sessionIdPolicy: SessionIdPolicy = new DefaultSessionIdPolicy
  /**
   * Try to authenticate a pre-authenticated user if the
   * user has not yet been authenticated.
   */
  override final def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {
    val request = req.asInstanceOf[HttpServletRequest]
    val response = res.asInstanceOf[HttpServletResponse]
    val requireAuth = requiresAuthentication(request, response)
    if (requireAuth) doAuthenticate(request, response)
    chain.doFilter(req, res)
  }

  /** Do the actual authentication for a pre-authenticated user. */
  private def doAuthenticate(request: HttpServletRequest, response: HttpServletResponse): Unit = {
    var authResult: AuthenticationInfo = null
    val token = getPreauthToken(request, response)
    if (null != token) {
      token.details ++= WebDetails.get(request)
      try {
        val session = securityManager.login(token, sessionIdPolicy.getSessionId(request))
        SecurityContext.session = session
        val httpSession = request.getSession(false)
        if (null != httpSession) httpSession.setMaxInactiveInterval(session.timeout)
      } catch {
        case failed: AuthenticationException => unsuccessfulAuthentication(request, response, failed)
        case e: Throwable => throw e
      }
    }
  }

  protected def getPreauthToken(request: HttpServletRequest, response: HttpServletResponse): PreauthToken

  protected def requiresAuthentication(request: HttpServletRequest, response: HttpServletResponse): Boolean = {
    SecurityContext.getSession match {
      case None => true
      case Some(s) => {
        if (null != aliveChecker && !aliveChecker.check(s, request)) {
          unsuccessfulAuthentication(request, response, null)
          true
        } else false
      }
    }
  }

  /**
   * Puts the Authentication instance returned by the
   * authentication manager into the secure context.
   */
  protected def successfulAuthentication(request: HttpServletRequest, response: HttpServletResponse,
    session: Session): Unit = {
    logger.debug(s"PreAuthentication success: $session")
    SecurityContext.session = session
  }

  /**
   * Ensures the authentication object in the secure context is set to null when authentication
   * fails.
   * If username not found or account status exception.just let other know by throw it.
   * It will be handled by ExceptionTranslationFilter
   */
  protected def unsuccessfulAuthentication(request: HttpServletRequest, response: HttpServletResponse,
    failed: AuthenticationException) {
    logger.debug("Cleared security context due to exception", failed)
    SecurityContext.session = null
    if (null != failed) throw failed
    //if (failed.isInstanceOf[UsernameNotFoundException] || failed.isInstanceOf[AccountStatusException]) throw failed
  }
}

/**
 * Source of the username supplied with pre-authenticated authentication
 * request. The username can be supplied in the request: in cookie, request
 * header, request parameter or as ServletRequest.getRemoteUser().
 */
trait UsernameSource {
  /**
   * Obtain username supplied in the request.
   */
  def obtainUsername(request: HttpServletRequest): Option[String]
}

/**
 * Abtain username by cookie
 */
class CookieUsernameSource extends UsernameSource {

  var cookieName: String = _

  def obtainUsername(request: HttpServletRequest): Option[String] = {
    val cookies = request.getCookies
    if (cookies != null) {
      cookies.find(c => c.getName == cookieName) match {
        case Some(c) => Some(c.getValue)
        case None => None
      }
    }
    None
  }
}

/**
 * Source of the username supplied with pre-authenticated authentication request
 * as remote user header value. Optionally can strip prefix: "domain\\username"
 * -> "username", if stripPrefix property value is "true".
 */
class RemoteUsernameSource extends UsernameSource with Logging {

  var stripPrefix = true

  def obtainUsername(request: HttpServletRequest): Option[String] = {
    var username: String = null
    val p = request.getUserPrincipal()
    if (null != p) username = p.getName()
    if (Strings.isEmpty(username)) username = request.getRemoteUser()
    if (null != username && stripPrefix) username = stripPrefix(username)
    if (null != username) logger.debug(s"Obtained username=[${username}] from remote user")
    if (null == username) None else Some(username)
  }

  private def stripPrefix(userName: String): String = {
    val index = userName.lastIndexOf("\\")
    if (-1 == index) userName else userName.substring(index + 1)
  }
}

class ParameterUsernameSource extends UsernameSource with Logging {

  var enableExpired = true

  // default 10min second
  var expiredTime = 600

  var timeParam = "t"

  var userParam = "cid"

  var digestParam = "s"

  var extra = "123456!"

  def obtainUsername(request: HttpServletRequest): Option[String] = {
    val ip = RequestUtils.getIpAddr(request)
    val cid = request.getParameter(userParam)
    val timeParamStr = request.getParameter(timeParam)
    var t: Long = 0
    if (null != timeParamStr) t = java.lang.Long.valueOf(timeParamStr)

    val s = request.getParameter(digestParam)
    if (0 == t || null == s || null == cid || null == ip) None
    else {
      val full = cid + "," + ip + "," + t + "," + extra
      val digest = Digests.md5Hex(full)
      if (logger.isDebugEnabled) {
        logger.debug(s"user $cid at :$ip")
        logger.debug(s"time:$t digest:$s ")
        logger.debug(s"full:$full")
        logger.debug(s"my_digest:$digest")
      }
      if (digest.equals(s)) {
        val time = t * 1000
        val now = new Date()
        if (enableExpired && (Math.abs(now.getTime() - time) > (expiredTime * 1000))) {
          logger.debug(s"user $cid time expired:server time:${now} and given time :${new java.util.Date(time)}")
          None
        } else {
          logger.debug(s"user $cid login at server time:$now")
          Some(cid)
        }
      } else None
    }
  }
}

class UsernamePreauthFilter(securityManager: SecurityManager) extends AbstractPreauthFilter(securityManager) {
  var usernameSource: UsernameSource = _

  protected override def getPreauthToken(request: HttpServletRequest, response: HttpServletResponse): PreauthToken = {
    usernameSource.obtainUsername(request) match {
      case Some(name) => if (Strings.isNotBlank(name)) new PreauthToken(name) else null
      case None => null
    }
  }
}

class PreauthRealm extends AbstractAccountRealm {
  var accountStore: AccountStore = _

  protected override def credentialsCheck(token: AuthenticationToken, account: Account): Unit = {}

  protected override def loadAccount(principal: Any): Option[Account] = accountStore.load(principal)

  override def supports(token: AuthenticationToken): Boolean = token.isInstanceOf[PreauthToken]
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy