View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.security.access;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.hadoop.conf.Configuration;
24  import org.apache.hadoop.hbase.util.Bytes;
25  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
26  import org.apache.hadoop.hbase.zookeeper.ZooKeeperListener;
27  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
28  import org.apache.zookeeper.KeeperException;
29  
30  import java.io.IOException;
31  import java.util.List;
32  
33  /**
34   * Handles synchronization of access control list entries and updates
35   * throughout all nodes in the cluster.  The {@link AccessController} instance
36   * on the {@code _acl_} table regions, creates a znode for each table as
37   * {@code /hbase/acl/tablename}, with the znode data containing a serialized
38   * list of the permissions granted for the table.  The {@code AccessController}
39   * instances on all other cluster hosts watch the znodes for updates, which
40   * trigger updates in the {@link TableAuthManager} permission cache.
41   */
42  public class ZKPermissionWatcher extends ZooKeeperListener {
43    private static Log LOG = LogFactory.getLog(ZKPermissionWatcher.class);
44    // parent node for permissions lists
45    static final String ACL_NODE = "acl";
46    TableAuthManager authManager;
47    String aclZNode;
48  
49    public ZKPermissionWatcher(ZooKeeperWatcher watcher,
50        TableAuthManager authManager, Configuration conf) {
51      super(watcher);
52      this.authManager = authManager;
53      String aclZnodeParent = conf.get("zookeeper.znode.acl.parent", ACL_NODE);
54      this.aclZNode = ZKUtil.joinZNode(watcher.baseZNode, aclZnodeParent);
55    }
56  
57    public void start() throws KeeperException {
58      watcher.registerListener(this);
59      if (ZKUtil.watchAndCheckExists(watcher, aclZNode)) {
60        List<ZKUtil.NodeAndData> existing =
61            ZKUtil.getChildDataAndWatchForNewChildren(watcher, aclZNode);
62        if (existing != null) {
63          refreshNodes(existing);
64        }
65      }
66    }
67  
68    @Override
69    public void nodeCreated(String path) {
70      if (path.equals(aclZNode)) {
71        try {
72          List<ZKUtil.NodeAndData> nodes =
73              ZKUtil.getChildDataAndWatchForNewChildren(watcher, aclZNode);
74          refreshNodes(nodes);
75        } catch (KeeperException ke) {
76          LOG.error("Error reading data from zookeeper", ke);
77          // only option is to abort
78          watcher.abort("Zookeeper error obtaining acl node children", ke);
79        }
80      }
81    }
82  
83    @Override
84    public void nodeDeleted(String path) {
85      if (aclZNode.equals(ZKUtil.getParent(path))) {
86        String table = ZKUtil.getNodeName(path);
87        authManager.remove(Bytes.toBytes(table));
88      }
89    }
90  
91    @Override
92    public void nodeDataChanged(String path) {
93      if (aclZNode.equals(ZKUtil.getParent(path))) {
94        // update cache on an existing table node
95        String table = ZKUtil.getNodeName(path);
96        try {
97          byte[] data = ZKUtil.getDataAndWatch(watcher, path);
98          authManager.refreshCacheFromWritable(Bytes.toBytes(table), data);
99        } catch (KeeperException ke) {
100         LOG.error("Error reading data from zookeeper for node "+table, ke);
101         // only option is to abort
102         watcher.abort("Zookeeper error getting data for node " + table, ke);
103       } catch (IOException ioe) {
104         LOG.error("Error reading permissions writables", ioe);
105       }
106     }
107   }
108 
109   @Override
110   public void nodeChildrenChanged(String path) {
111     if (path.equals(aclZNode)) {
112       // table permissions changed
113       try {
114         List<ZKUtil.NodeAndData> nodes =
115             ZKUtil.getChildDataAndWatchForNewChildren(watcher, aclZNode);
116         refreshNodes(nodes);
117       } catch (KeeperException ke) {
118         LOG.error("Error reading data from zookeeper for path "+path, ke);
119         watcher.abort("Zookeeper error get node children for path "+path, ke);
120       }
121     }
122   }
123 
124   private void refreshNodes(List<ZKUtil.NodeAndData> nodes) {
125     for (ZKUtil.NodeAndData n : nodes) {
126       if (n.isEmpty()) continue;
127       String path = n.getNode();
128       String table = ZKUtil.getNodeName(path);
129       try {
130         byte[] nodeData = n.getData();
131         if (LOG.isDebugEnabled()) {
132           LOG.debug("Updating permissions cache from node "+table+" with data: "+
133               Bytes.toStringBinary(nodeData));
134         }
135         authManager.refreshCacheFromWritable(Bytes.toBytes(table), nodeData);
136       } catch (IOException ioe) {
137         LOG.error("Failed parsing permissions for table '" + table +
138             "' from zk", ioe);
139       }
140     }
141   }
142 
143   /***
144    * Write a table's access controls to the permissions mirror in zookeeper
145    * @param tableName
146    * @param permsData
147    */
148   public void writeToZookeeper(byte[] tableName, byte[] permsData) {
149     String zkNode = ZKUtil.joinZNode(watcher.baseZNode, ACL_NODE);
150     zkNode = ZKUtil.joinZNode(zkNode, Bytes.toString(tableName));
151 
152     try {
153       ZKUtil.createWithParents(watcher, zkNode);
154       ZKUtil.updateExistingNodeData(watcher, zkNode, permsData, -1);
155     } catch (KeeperException e) {
156       LOG.error("Failed updating permissions for table '" + 
157                 Bytes.toString(tableName) + "'", e);
158       watcher.abort("Failed writing node "+zkNode+" to zookeeper", e);
159     }
160   }
161 }