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

com.loopfor.zookeeper.cli.command.SetACL.scala Maven / Gradle / Ivy

The newest version!
/*
 * 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.cli.command

import com.loopfor.scalop._
import com.loopfor.zookeeper._
import com.loopfor.zookeeper.cli._
import scala.util.{Failure, Success}

object SetACL {
  val Usage = """usage: setacl [OPTIONS] PATH ACL[...]

  Sets the ACL for the node specified by PATH.

  At least one ACL entry must be provided, which must conform to the following
  syntax: :=[rwcda*], where zero or more of [rwcda*] characters may
  be given as permissions. The permission values are (r)ead, (w)rite, (c)reate,
  (d)elete, (a)dmin and all(*). The : fragment is omitted if the scheme is
  `auth` (see below).

  Unless otherwise specified, --set is assumed, which means that the given ACL
  replaces the current ACL associated with the node at PATH. Both --add
  and --remove options first query the current ACL before applying the
  respective operation. Therefore, the entire operation is not atomic, though
  specifying --version ensures that no intervening operations have changed the
  state.

  Valid scheme/id combinations:
    `world:anyone`
      Represents anyone. This is the only acceptable form of this scheme.

    `auth`
      Represents any authenticated user, hence elimination of : fragment.
      The is the only acceptable form of this scheme.

    `digest::`
      Represents users backed by a directory in ZooKeeper.
        ex: digest:alice:secret

    `host:`
      Represents a specific host or hosts within a given domain.
        ex: host:server1.foo.com
            host:foo.com

    `ip:
[/]` Represents an IPv4 or IPv6 address in dotted decimal notation. An optional network prefix specifies the number of leading bits to consider when matching. may be in the range [0,32] for IPv4 and [0,128] for IPv6. If not specified, is assumed to be the maximum bits. ex: ip:1.2.3.4 ip:1.2.3.4/24 options: --add, -a : adds ACL to existing list --remove, -r : removes ACL from existing list --set, -s : replaces existing list with ACL (default) --version, -v VERSION : version required to match in ZooKeeper --force, -f : forcefully set ACL regardless of version """ def command(zk: Zookeeper) = new CommandProcessor { implicit val _zk: Zookeeper = zk val opts = ("add", 'a') ~> just(true) ~~ false :: ("remove", 'r') ~> just(true) ~~ false :: ("set", 's') ~> just(true) ~~ false :: ("force", 'f') ~> just(true) ~~ false :: ("version", 'v') ~> as[Option[Int]] ~~ None :: Nil def apply(cmd: String, args: Seq[String], context: Path): Path = { val optr = opts <~ args val action = actionOpt(optr) val version = versionOpt(optr) val (path, afterPath) = pathArg(optr) val acl = aclArgs(afterPath) val node = Node(context.resolve(path)) setACL(node, version, action, acl) context } } def find(zk: Zookeeper, args: Seq[String]) = new FindProcessor { val opts = ("add", 'a') ~> just(true) ~~ false :: ("remove", 'r') ~> just(true) ~~ false :: ("set", 's') ~> just(true) ~~ false :: Nil val optr = opts <~ args val action = actionOpt(optr) val acl = aclArgs(optr.args) def apply(node: Node): Unit = { setACL(node, None, action, acl) } } private def setACL(node: Node, version: Option[Int], action: Symbol, acl: Seq[ACL]): Unit = { val (curACL, _) = try node.getACL() catch { case _: NoNodeException => complain(s"${node.path}: no such node") } val newACL = action match { case Symbol("add") => acl.foldLeft(toMap(curACL)) { case (c, a) => c + (a.id -> a) }.values.toSeq case Symbol("remove") => acl.foldLeft(toMap(curACL)) { case (c, a) => c - a.id }.values.toSeq case Symbol("set") => acl } newACL match { case Seq() => complain("new ACL would be empty") case _ => try node.setACL(newACL, version) catch { case _: NoNodeException => complain(s"${node.path}: no such node") case _: BadVersionException => complain(s"${version.get}: version does not match") case _: InvalidACLException => complain(s"${newACL.mkString(",")}: invalid ACL") } } } private def actionOpt(optr: OptResult): Symbol = { if (optr[Boolean]("set")) Symbol("set") else if (optr[Boolean]("add")) Symbol("add") else if (optr[Boolean]("remove")) Symbol("remove") else Symbol("set") } private def versionOpt(optr: OptResult): Option[Int] = { val force = optr[Boolean]("force") if (force) None else optr[Option[Int]]("version") match { case None => complain("version must be specified; otherwise use --force") case v => v } } private def pathArg(optr: OptResult): (Path, Seq[String]) = optr.args match { case Seq(path, rest @ _*) => (Path(path), rest) case Seq() => complain("path must be specified") } private def aclArgs(args: Seq[String]): Seq[ACL] = args match { case Seq() => complain("ACL must be specified") case _ => args.map { acl => ACL.parse(acl) match { case Success(a) => a case Failure(e) => complain(e.getMessage) } } } private def toMap(acl: Seq[ACL]): Map[Id, ACL] = { acl.foldLeft(Map.empty[Id, ACL]) { case (m, a) => m + (a.id -> a) } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy