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

com.loopfor.zookeeper.ACL.scala Maven / Gradle / Ivy

/*
 * Copyright 2020 David Edwards
 *
 * 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.loopfor.zookeeper

import org.apache.zookeeper.ZooDefs.{Ids, Perms}
import org.apache.zookeeper.data.{ACL => ZACL}
import scala.jdk.CollectionConverters._
import scala.language._
import scala.util.{Failure, Success, Try}

/**
 * An ''access control list'' assignable to a ZooKeeper node.
 *
 * @param id the identity to which permissions apply
 * @param permission the bitwise union of permissions that apply to this ACL
 */
case class ACL(id: Id, permission: Int) extends ZACL(permission, id.zid)

/**
 * Constructs and deconstructs [[ACL]] values.
 *
 * The permissions assigned to an ACL are constructed by performing a bitwise union of individual permission attributes:
 * [[Read]], [[Write]], [[Create]], [[Delete]], [[Admin]]. In addition, the [[All]] permission encompasses all of these
 * attributes.
 *
 * Several commonly used ACL values have been predefined for sake of convenience: [[AnyoneAll]], [[AnyoneRead]],
 * [[CreatorAll]].
 *
 * @see [[https://zookeeper.apache.org/doc/r3.5.6/zookeeperProgrammers.html#sc_ZooKeeperAccessControl ACLs]]
 */
object ACL {
  /**
   * A permission to read.
   */
  val Read: Int = Perms.READ

  /**
   * A permission to write.
   */
  val Write: Int = Perms.WRITE

  /**
   * A permission to create.
   */
  val Create: Int = Perms.CREATE

  /**
   * A permission to delete.
   */
  val Delete: Int = Perms.DELETE

  /**
   * A permission to perform administrative functions.
   */
  val Admin: Int = Perms.ADMIN

  /**
   * A composition of all permissions.
   *
   * This is equivalent to the bitwise union of the following permissions:
   * {{{
   * Read | Write | Create | Delete | Admin
   * }}}
   */
  val All: Int = Perms.ALL

  /**
   * An ACL in which [[Id#Anyone Anyone]] is granted [[All]] permissions.
   */
  val AnyoneAll: Seq[ACL] = ACL(Ids.OPEN_ACL_UNSAFE)

  /**
   * An ACL in which [[Id#Anyone Anyone]] is granted [[Read]] permission only.
   */
  val AnyoneRead: Seq[ACL] = ACL(Ids.READ_ACL_UNSAFE)

  /**
   * An ACL in which the [[Id#Creator Creator]] is granted [[All]] permissions.
   */
  val CreatorAll: Seq[ACL] = ACL(Ids.CREATOR_ALL_ACL)

  /**
   * Constructs a new ACL from the input string `s`.
   *
   * @param s a string representing the ACL
   * @return the ACL in `s` if it conforms to the specific syntax
   *
   * @throws IllegalArgumentException if `s` does not conform to the proper syntax
   *
   * @see [[parse]]
   */
  def apply(s: String): ACL = parse(s) match {
    case Success(acl) => acl
    case Failure(e) => throw e
  }

  /**
   * Parses the ACL in the input string `s`.
   *
   * The syntax of `s` is `"''scheme'':''id''=[rwcda*]"`, where the following apply:
   *  - the `:` delimiter may be omitted if ''id'' is not required
   *  - `rwcda*` may be repeated zero or more times
   *
   * @param s a string representing the ACL
   * @return a `Success` containing the ACL in `s` if it conforms to the proper syntax, otherwise a `Failure`
   * containing the offending exception
   */
  def parse(s: String): Try[ACL] = Try {
    def error(message: String): Nothing =
      throw new IllegalArgumentException(s"${s}: ${message}")

    s.split("=", 2) match {
      case Array(id, permission) =>
        Id.parse(id) match {
          case Success(i) =>
            permission match {
              case Permission(p) => ACL(i, p)
              case _ => error("invalid permissions")
            }
          case Failure(e) => throw e
        }
      case _ => error("expecting permissions")
    }
  }

  private[zookeeper] def apply(zacl: ZACL): ACL = ACL(Id(zacl.getId), zacl.getPerms)

  private[zookeeper] def apply(zacl: java.util.List[ZACL]): Seq[ACL] =
    zacl.asScala.foldLeft(Seq[ACL]()) { case (acl, zacl) => acl :+ ACL(zacl)}

  private[zookeeper] def toZACL(acl: Seq[ACL]): java.util.List[ZACL] =
    acl.foldLeft(Seq[ZACL]()) { case (zacl, acl) => zacl :+ toZACL(acl) }.asJava

  private[zookeeper] def toZACL(acl: ACL): ZACL = acl.asInstanceOf[ZACL]

  private object Permission {
    def apply(perms: Int): String = {
      (if ((perms & Read) == 0) "-" else "r") +
      (if ((perms & Write) == 0) "-" else "w") +
      (if ((perms & Create) == 0) "-" else "c") +
      (if ((perms & Delete) == 0) "-" else "d") +
      (if ((perms & Admin) == 0) "-" else "a")
    }

    def unapply(s: String): Option[Int] = {
      if (s == null) None
      else {
        s.foldLeft(Some(0): Option[Int]) { case (p, c) =>
          p match {
            case Some(_p) =>
              if (c == 'r') Some(_p | Read)
              else if (c == 'w') Some(_p | Write)
              else if (c == 'c') Some(_p | Create)
              else if (c == 'd') Some(_p | Delete)
              else if (c == 'a') Some(_p | Admin)
              else if (c == '*') Some(_p | All)
              else None
            case None =>
              None
          }
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy