/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.master.file;

import alluxio.AlluxioURI;
import alluxio.conf.Configuration;
import alluxio.conf.PropertyKey;
import alluxio.master.audit.AsyncUserAccessAuditLogWriter;
import alluxio.master.audit.AuditContext;
import alluxio.master.file.meta.Inode;
import alluxio.security.authentication.AuthType;
import alluxio.security.authorization.Mode;

import javax.annotation.concurrent.NotThreadSafe;

/**
 * An audit context for file system master.
 */
@NotThreadSafe
public final class FileSystemMasterAuditContext implements AuditContext {
  private final AsyncUserAccessAuditLogWriter mAsyncAuditLogWriter;
  private boolean mAllowed;
  private boolean mSucceeded;
  private String mCommand;
  private AlluxioURI mSrcPath;
  private AlluxioURI mDstPath;
  private String mUgi;
  private AuthType mAuthType;
  private String mIp;
  private Inode mSrcInode;
  private long mCreationTimeNs;
  private long mExecutionTimeNs;
  private String mClientVersion;

  @Override
  public FileSystemMasterAuditContext setAllowed(boolean allowed) {
    mAllowed = allowed;
    return this;
  }

  @Override
  public FileSystemMasterAuditContext setSucceeded(boolean succeeded) {
    mSucceeded = succeeded;
    return this;
  }

  /**
   * Sets mCommand field.
   *
   * @param command the command associated with this {@link alluxio.master.Master}
   * @return this {@link AuditContext} instance
   */
  public FileSystemMasterAuditContext setCommand(String command) {
    mCommand = command;
    return this;
  }

  /**
   * Sets mSrcPath field.
   *
   * @param srcPath the source path of the command
   * @return this {@link AuditContext} instance
   */
  public FileSystemMasterAuditContext setSrcPath(AlluxioURI srcPath) {
    mSrcPath = srcPath;
    return this;
  }

  /**
   * Sets mDstPath field.
   *
   * @param dstPath the destination path of the command
   * @return this {@link AuditContext} instance
   */
  public FileSystemMasterAuditContext setDstPath(AlluxioURI dstPath) {
    mDstPath = dstPath;
    return this;
  }

  /**
   * Sets mUgi field.
   *
   * @param ugi the client user name of the authenticated client user of this thread
   * @return this {@link AuditContext} instance
   */
  public FileSystemMasterAuditContext setUgi(String ugi) {
    mUgi = ugi;
    return this;
  }

  /**
   * Sets mAuthType field.
   *
   * @param authType the authentication type
   * @return this {@link AuditContext} instance
   */
  public FileSystemMasterAuditContext setAuthType(AuthType authType) {
    mAuthType = authType;
    return this;
  }

  /**
   * Sets mIp field.
   *
   * @param ip the IP of the client
   * @return this {@link AuditContext} instance
   */
  public FileSystemMasterAuditContext setIp(String ip) {
    mIp = ip;
    return this;
  }

  /**
   * Sets mSrcInode field.
   *
   * @param srcInode the source inode of this operation
   * @return this {@link AuditContext} instance
   */
  public FileSystemMasterAuditContext setSrcInode(Inode srcInode) {
    mSrcInode = srcInode;
    return this;
  }

  /**
   * Sets mCreationTimeNs field.
   *
   * @param creationTimeNs the System.nanoTime() when this operation create,
   *                     it only can be used to compute operation mExecutionTime
   * @return this {@link AuditContext} instance
   */
  public FileSystemMasterAuditContext setCreationTimeNs(long creationTimeNs) {
    mCreationTimeNs = creationTimeNs;
    return this;
  }

  /**
   * set client version.
   *
   * @param version client version
   * @return this {@link AuditContext} instance
   */
  public FileSystemMasterAuditContext setClientVersion(String version) {
    mClientVersion = version;
    return this;
  }

  /**
   * Constructor of {@link FileSystemMasterAuditContext}.
   *
   * @param asyncAuditLogWriter async audit log writer
   */
  FileSystemMasterAuditContext(AsyncUserAccessAuditLogWriter asyncAuditLogWriter) {
    mAsyncAuditLogWriter = asyncAuditLogWriter;
    mAllowed = true;
  }

  @Override
  public void close() {
    if (mAsyncAuditLogWriter == null) {
      return;
    }
    mExecutionTimeNs = System.nanoTime() - mCreationTimeNs;
    mAsyncAuditLogWriter.append(this);
  }

  @Override
  public String toString() {
    StringBuilder auditLog = new StringBuilder();
    if (mSrcInode != null) {
      short mode = mSrcInode.getMode();
      auditLog.append(String.format(
          "succeeded=%b\tallowed=%b\tugi=%s (AUTH=%s)\tip=%s\tcmd=%s\tsrc=%s\tdst=%s\t"
              + "perm=%s:%s:%s%s%s\texecutionTimeUs=%d",
          mSucceeded, mAllowed, mUgi, mAuthType, mIp, mCommand, mSrcPath, mDstPath,
          mSrcInode.getOwner(), mSrcInode.getGroup(),
          Mode.extractOwnerBits(mode), Mode.extractGroupBits(mode), Mode.extractOtherBits(mode),
          mExecutionTimeNs / 1000));
    } else {
      auditLog.append(String.format(
          "succeeded=%b\tallowed=%b\tugi=%s (AUTH=%s)\tip=%s\tcmd=%s\tsrc=%s\tdst=%s\t"
              + "perm=null\texecutionTimeUs=%d",
          mSucceeded, mAllowed, mUgi, mAuthType, mIp, mCommand, mSrcPath, mDstPath,
          mExecutionTimeNs / 1000));
    }
    if (Configuration.global().getBoolean(PropertyKey.USER_CLIENT_REPORT_VERSION_ENABLED)) {
      auditLog.append(
          String.format("\tclientVersion=%s\t", mClientVersion));
    }
    auditLog.append("\tproto=rpc");
    return auditLog.toString();
  }
}
