Newer
Older
GitBucket / src / main / scala / gitbucket / core / controller / IndexController.scala
@KOUNOIKE Yuusuke KOUNOIKE Yuusuke on 27 Jan 2018 7 KB fix for build and test
package gitbucket.core.controller

import java.net.URI

import com.nimbusds.oauth2.sdk.id.State
import com.nimbusds.openid.connect.sdk.Nonce
import gitbucket.core.helper.xml
import gitbucket.core.model.Account
import gitbucket.core.service._
import gitbucket.core.util.Implicits._
import gitbucket.core.util.SyntaxSugars._
import gitbucket.core.util.{Keys, LDAPUtil, ReferrerAuthenticator, UsersAuthenticator}
import org.scalatra.Ok
import org.scalatra.forms._


class IndexController extends IndexControllerBase
  with RepositoryService
  with ActivityService
  with AccountService
  with RepositorySearchService
  with IssuesService
  with LabelsService
  with MilestonesService
  with PrioritiesService
  with UsersAuthenticator
  with ReferrerAuthenticator
  with AccountFederationService
  with OpenIDConnectService


trait IndexControllerBase extends ControllerBase {
  self: RepositoryService
    with ActivityService
    with AccountService
    with RepositorySearchService
    with UsersAuthenticator
    with ReferrerAuthenticator
    with OpenIDConnectService =>

  case class SignInForm(userName: String, password: String, hash: Option[String])

  val signinForm = mapping(
    "userName" -> trim(label("Username", text(required))),
    "password" -> trim(label("Password", text(required))),
    "hash" -> trim(optional(text()))
  )(SignInForm.apply)

//  val searchForm = mapping(
//    "query"      -> trim(text(required)),
//    "owner"      -> trim(text(required)),
//    "repository" -> trim(text(required))
//  )(SearchForm.apply)
//
//  case class SearchForm(query: String, owner: String, repository: String)

  case class OidcContext(state: State, nonce: Nonce, redirectBackURI: String)

  get("/"){
    context.loginAccount.map { account =>
      val visibleOwnerSet: Set[String] = Set(account.userName) ++ getGroupsByUserName(account.userName)
      gitbucket.core.html.index(getRecentActivitiesByOwners(visibleOwnerSet), Nil, getUserRepositories(account.userName, withoutPhysicalInfo = true))
    }.getOrElse {
      gitbucket.core.html.index(getRecentActivities(), getVisibleRepositories(None, withoutPhysicalInfo = true), Nil)
    }
  }

  get("/signin"){
    val redirect = params.get("redirect")
    if(redirect.isDefined && redirect.get.startsWith("/")){
      flash += Keys.Flash.Redirect -> redirect.get
    }
    gitbucket.core.html.signin(flash.get("userName"), flash.get("password"), flash.get("error"))
  }

  post("/signin", signinForm){ form =>
    authenticate(context.settings, form.userName, form.password) match {
      case Some(account) =>
        flash.get(Keys.Flash.Redirect) match {
          case Some(redirectUrl: String) => signin(account, redirectUrl + form.hash.getOrElse(""))
          case _ => signin(account)
        }
      case None =>
        flash += "userName" -> form.userName
        flash += "password" -> form.password
        flash += "error" -> "Sorry, your Username and/or Password is incorrect. Please try again."
        redirect("/signin")
    }
  }

  /**
    * Initiate an OpenID Connect authentication request.
    */
  post("/signin/oidc") {
    context.settings.oidc.map { oidc =>
      val redirectURI = new URI(s"$baseUrl/signin/oidc")
      val authenticationRequest = createOIDCAuthenticationRequest(oidc.issuer, oidc.clientID, redirectURI)
      val redirectBackURI = flash.get(Keys.Flash.Redirect) match {
        case Some(redirectBackURI: String) => redirectBackURI + params.getOrElse("hash", "")
        case _ => "/"
      }
      session.setAttribute(Keys.Session.OidcContext, OidcContext(authenticationRequest.getState, authenticationRequest.getNonce, redirectBackURI))
      redirect(authenticationRequest.toURI.toString)
    } getOrElse {
      NotFound()
    }
  }

  /**
    * Handle an OpenID Connect authentication response.
    */
  get("/signin/oidc") {
    context.settings.oidc.map { oidc =>
      val redirectURI = new URI(s"$baseUrl/signin/oidc")
      session.get(Keys.Session.OidcContext) match {
        case Some(context: OidcContext) =>
          authenticate(params, redirectURI, context.state, context.nonce, oidc) map { account =>
            signin(account, context.redirectBackURI)
          } orElse {
            flash += "error" -> "Sorry, authentication failed. Please try again."
            session.invalidate()
            redirect("/signin")
          }
        case _ =>
          flash += "error" -> "Sorry, something wrong. Please try again."
          session.invalidate()
          redirect("/signin")
      }
    } getOrElse {
      NotFound()
    }
  }

  get("/signout"){
    session.invalidate
    redirect("/")
  }

  get("/activities.atom"){
    contentType = "application/atom+xml; type=feed"
    xml.feed(getRecentActivities())
  }

  post("/sidebar-collapse"){
    if(params("collapse") == "true"){
      session.setAttribute("sidebar-collapse", "true")
    }  else {
      session.setAttribute("sidebar-collapse", null)
    }
    Ok()
  }

  /**
    * Set account information into HttpSession and redirect.
    */
  private def signin(account: Account, redirectUrl: String = "/") = {
    session.setAttribute(Keys.Session.LoginAccount, account)
    updateLastLoginDate(account.userName)

    if(LDAPUtil.isDummyMailAddress(account)) {
      redirect("/" + account.userName + "/_edit")
    }

    if (redirectUrl.stripSuffix("/") == request.getContextPath) {
      redirect("/")
    } else {
      redirect(redirectUrl)
    }
  }

  /**
   * JSON API for collaborator completion.
   */
  get("/_user/proposals")(usersOnly {
    contentType = formats("json")
    val user  = params("user").toBoolean
    val group = params("group").toBoolean
    org.json4s.jackson.Serialization.write(
      Map("options" -> (
        getAllUsers(false)
          .withFilter { t => (user, group) match {
            case (true, true) => true
            case (true, false) => !t.isGroupAccount
            case (false, true) => t.isGroupAccount
            case (false, false) => false
          }}.map { t =>
            Map(
              "label" -> s"<b>@${t.userName}</b> ${t.fullName}",
              "value" -> t.userName
            )
          }
      ))
    )
  })

  /**
   * JSON API for checking user or group existence.
   * Returns a single string which is any of "group", "user" or "".
   */
  post("/_user/existence")(usersOnly {
    getAccountByUserName(params("userName")).map { account =>
      if(account.isGroupAccount) "group" else "user"
    } getOrElse ""
  })

  // TODO Move to RepositoryViwerController?
  get("/:owner/:repository/search")(referrersOnly { repository =>
    defining(params.getOrElse("q", "").trim, params.getOrElse("type", "code")){ case (query, target) =>
      val page = try {
        val i = params.getOrElse("page", "1").toInt
        if(i <= 0) 1 else i
      } catch {
        case e: NumberFormatException => 1
      }

      target.toLowerCase match {
        case "issue" => gitbucket.core.search.html.issues(
          if(query.nonEmpty) searchIssues(repository.owner, repository.name, query) else Nil,
          query, page, repository)

        case "wiki" => gitbucket.core.search.html.wiki(
          if(query.nonEmpty) searchWikiPages(repository.owner, repository.name, query) else Nil,
          query, page, repository)

        case _ => gitbucket.core.search.html.code(
          if(query.nonEmpty) searchFiles(repository.owner, repository.name, query) else Nil,
          query, page, repository)
      }
    }
  })

  get("/search"){
    val query = params.getOrElse("query", "").trim.toLowerCase
    val visibleRepositories = getVisibleRepositories(context.loginAccount, repositoryUserName = None, withoutPhysicalInfo = true)
    val repositories = visibleRepositories.filter { repository =>
      repository.name.toLowerCase.indexOf(query) >= 0 || repository.owner.toLowerCase.indexOf(query) >= 0
    }
    context.loginAccount.map { account =>
      gitbucket.core.search.html.repositories(query, repositories, Nil, getUserRepositories(account.userName, withoutPhysicalInfo = true))
    }.getOrElse {
      gitbucket.core.search.html.repositories(query, repositories, visibleRepositories, Nil)
    }
  }

}