/*
 * Copyright 2019 WeBank
 *
 * 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 com.webank.wedatasphere.linkis.filesystem.validator

import java.io.File

import com.webank.wedatasphere.linkis.common.utils.Logging
import com.webank.wedatasphere.linkis.filesystem.conf.WorkSpaceConfiguration._
import com.webank.wedatasphere.linkis.filesystem.exception.WorkSpaceException
import com.webank.wedatasphere.linkis.filesystem.util.WorkspaceUtil
import com.webank.wedatasphere.linkis.server
import com.webank.wedatasphere.linkis.server.Message
import com.webank.wedatasphere.linkis.server.security.SecurityFilter
import com.webank.wedatasphere.linkis.storage.utils.StorageUtils
import javax.servlet.http.HttpServletRequest
import javax.ws.rs.core.Response
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.{Around, Aspect, Pointcut}
import org.aspectj.lang.reflect.MethodSignature
import org.codehaus.jackson.JsonNode
import org.springframework.stereotype.Component
import org.springframework.util.StringUtils

@Aspect
@Component
class PathValidator extends Logging {

  @Pointcut("@annotation(javax.ws.rs.Path) && within(com.webank.wedatasphere.linkis.filesystem.restful.api.*)")
  def restfulResponseCatch(): Unit = {}

  def getPath(args: Array[Object], paramNames: Array[String]): String = {
    var path: String = null
    var index: Int = paramNames.indexOf("path")
    if (index != -1) {
      path = args(index).asInstanceOf[String]
    } else {
      index = paramNames.indexOf("json")
      if (index != -1) {
        args(index) match {
          case j: JsonNode if j.get("path") != null => path = j.get("path").getTextValue
          case m: java.util.Map[String, Object] if m.get("path") != null => path = m.get("path").asInstanceOf[String]
          case _ =>
        }
      }
    }
    path
  }

  def getUserName(args: Array[Object], paramNames: Array[String]): String = {
    var username: String = null
    paramNames.indexOf("req") match {
      case -1 =>
      case index: Int => {
        val proxyUser = paramNames.indexOf("proxyUser")
        if (proxyUser == -1 || StringUtils.isEmpty(args(proxyUser))) {
          username = SecurityFilter.getLoginUsername(args(index).asInstanceOf[HttpServletRequest])
        } else {
          //增加proxyuser的判断
          username = args(proxyUser).toString
        }
      }
    }
    username
  }

  def checkPath(path: String, username: String) = {
    //校验path的逻辑
    val userLocalRootPath: String = WorkspaceUtil.suffixTuning(LOCAL_USER_ROOT_PATH.getValue) +
      username
    var userHdfsRootPath: String = WorkspaceUtil.suffixTuning(HDFS_USER_ROOT_PATH_PREFIX.getValue) +
      username + HDFS_USER_ROOT_PATH_SUFFIX.getValue
    if (!(path.contains(StorageUtils.FILE_SCHEMA)) && !(path.contains(StorageUtils.HDFS_SCHEMA))) {
      throw new WorkSpaceException(80025, "the path should contain schema")
    }
    userHdfsRootPath = StringUtils.trimTrailingCharacter(userHdfsRootPath, File.separatorChar)
    if (path.contains("../")) {
      throw new WorkSpaceException(80026, "Relative path not allowed")
    }
    if (!(path.contains(userLocalRootPath)) && !(path.contains(userHdfsRootPath))) {
      throw new WorkSpaceException(80027, "The path needs to be within the user's own workspace path")
    }
  }

  def validate(args: Array[Object], paramNames: Array[String]) = {
    //获取path:String,json:JsonNode,json:Map中的path 参数
    val path: String = getPath(args, paramNames)
    val username: String = getUserName(args, paramNames)
    if (!StringUtils.isEmpty(path) && !StringUtils.isEmpty(username)) {
      logger.info(String.format("user:%s,path:%s", username, path))
      checkPath(path, username)
    }
  }

  @Around("restfulResponseCatch()")
  def dealResponseRestful(proceedingJoinPoint: ProceedingJoinPoint): Object = {
    val response: Response = server.catchIt {
      val signature = proceedingJoinPoint.getSignature.asInstanceOf[MethodSignature]
      logger.info("enter the path validator,the method is {}", signature.getName)
      if (FILESYSTEM_PATH_CHECK_TRIGGER.getValue) {
        logger.info("path check trigger is open,now check the path")
        validate(proceedingJoinPoint.getArgs, signature.getParameterNames)
      }
      Message.ok()
    }
    if (response.getStatus != 200) response else proceedingJoinPoint.proceed()
  }
}

object PathValidator {
  val validator = new PathValidator()

  def validate(path: String, username: String): Unit = {
    validator.checkPath(path, username)
  }
}
