//------------------------------------------------------------------------------
//! @file AclCmd.hh
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2017 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#pragma once
#include "mgm/Namespace.hh"
#include "mgm/proc/ProcCommand.hh"
#include "proto/Acl.pb.h"
#include
EOSMGMNAMESPACE_BEGIN
typedef std::pair Rule;
//typedef std::unordered_map RuleMap;
// We use a list as we need to be able to insert at any position
typedef std::list RuleMap;
template
typename C::iterator key_position(C& c, const K& k)
{
return std::find_if(c.begin(),
c.end(),
[&k](const typename C::value_type & val)->bool {
return k == val.first;
});
}
template
void insert_or_assign(C& c, K&& k, V&& v)
{
auto it = key_position(c, k);
if (it != c.end()) {
it->second = v;
return;
}
c.emplace_back(std::make_pair(std::forward(k),
std::forward(v)));
}
template
void insert_or_assign(C& c, K && k, V && v, It &&
pos, bool move_existing = false)
{
auto it = key_position(c, k);
if (it != c.end()) {
if (!move_existing || it == pos) {
it->second = v;
return;
}
// This function currently moves an existing key to a given position too, if
// this is not needed, we could skip the following erase! Since Iterator is at a
// different position, erase this, we'll readd the key back at the last step
auto next_it = c.erase(it);
// In case we're demoting an element the erase would've dropped an element
// so the index position should be incremented!
if (pos != c.end() &&
(std::distance(c.begin(), next_it) <= std::distance(c.begin(), pos))) {
++pos;
}
}
c.insert(pos, std::make_pair(std::forward(k),
std::forward(v)));
}
template
void insert_or_assign(C& c, typename C::value_type&& value)
{
using first_type = typename C::value_type::first_type;
using second_type = typename C::value_type::second_type;
insert_or_assign(c,
std::forward(value.first),
std::forward(value.second));
}
template
std::pair
get_iterator(C& c, size_t pos)
{
if (pos == 0 || pos > c.size()) {
return std::make_pair(c.end(), EINVAL);
}
auto it = c.begin();
std::advance(it, pos - 1);
return std::make_pair(it, 0);
}
//------------------------------------------------------------------------------
//! Class AclCmd - class handling acl command from a client
//------------------------------------------------------------------------------
class AclCmd: public IProcCommand
{
public:
//----------------------------------------------------------------------------
//! Constructor
//!
//! @param req client ProtocolBuffer request
//! @param vid client virtual identity
//----------------------------------------------------------------------------
explicit AclCmd(eos::console::RequestProto&& req,
eos::common::VirtualIdentity& vid):
IProcCommand(std::move(req), vid, true), mId(), mAddRule(0),
mRmRule(0), mSet(false)
{}
//----------------------------------------------------------------------------
//! Destructor
//----------------------------------------------------------------------------
virtual ~AclCmd() = default;
//----------------------------------------------------------------------------
//! Method implementing the specific behavior of the command executed by the
//! asynchronous thread
//----------------------------------------------------------------------------
eos::console::ReplyProto ProcessRequest() noexcept override;
//----------------------------------------------------------------------------
//! Generate rule map from the string representation of the acls. If there
//! are no acls then the rmap will be empty.
//!
//! @note Public only for testing.
//!
//! @param acl_string string containing acl
//! @param rmap map to be filled with acl rules
//----------------------------------------------------------------------------
static void
GenerateRuleMap(const std::string& acl_string, RuleMap& rmap);
//----------------------------------------------------------------------------
//! Check if id has the correct format i.e u:user_id or g:group_id
//!
//! @param id string containing id
//!
//! @return bool true if correct, otherwise false
//----------------------------------------------------------------------------
bool CheckCorrectId(const std::string& id) const;
//----------------------------------------------------------------------------
//! Get ACL rule from string by creating a pair of identifier for the ACL and
//! the bitmask representation.
//!
//! @param in ACL string
//!
//! @return std::pair containing ACL identifier (ie. u:user1 or g:group1)
//! and the bitmask representation
//----------------------------------------------------------------------------
static Rule GetRuleFromString(const std::string& in);
//----------------------------------------------------------------------------
//! Convert acl modification command into bitmask rule format
//!
//! @param input string containing the modifications of the acls
//! @param set if true "set" mode is active, otherwise false
//!
//! @return bool true if conversion successful, otherwise false
//----------------------------------------------------------------------------
bool GetRuleBitmask(const std::string& input, bool set = false);
//----------------------------------------------------------------------------
//! Generate the position to place the rule
//!
//! @param input rule_map size of the current rule_map
//! @param input rule_pos position to be inserted
//!
//! @return a pair of error, insert position
//----------------------------------------------------------------------------
static std::pair GetRulePosition(size_t rule_map_sz,
size_t rule_pos);
//----------------------------------------------------------------------------
//! Return mAddRule result after GetRuleBitmask call.
//----------------------------------------------------------------------------
unsigned short GetAddRule()
{
return mAddRule;
}
//----------------------------------------------------------------------------
//! Return mRmRule result after GetRuleBitmask call.
//----------------------------------------------------------------------------
unsigned short GetRmRule()
{
return mRmRule;
}
private:
//! Enumerator defining which bit represents which acl flag.
enum ACLPos {
R = 1 << 0, // 1 - r
W = 1 << 1, // 2 - w
X = 1 << 2, // 4 - x
M = 1 << 3, // 8 - m
nM = 1 << 4, // 16 - !m
nD = 1 << 5, // 32 - !d
pD = 1 << 6, // 64 - +d
nU = 1 << 7, // 128 - !u
pU = 1 << 8, // 256 - +u
Q = 1 << 9, // 512 - q
C = 1 << 10, // 1024 - c
WO = 1 << 11, // 2048 - wo
nR = 1 << 12, // 4096 - !r
nW = 1 << 13, // 8192 - !w
nX = 1 << 14, //16384 - !x
A = 1 << 15 //32768 - a
};
std::string mId; ///< Rule identifier extracted from command line
///< ACL rule bitmasks for adding and removing
unsigned short mAddRule, mRmRule;
bool mSet; ///< Rule is set operations i.e contains =
//----------------------------------------------------------------------------
//! Get sys.acl and user.acl for a given path
//!
//! @param path path to get the ACLs for
//! @param acls ACL VALUE
//! @param is_sys if true return sys.acl, otherwise user.acl
//! @param take_lock if true take namespace lock, otherwise don't
//----------------------------------------------------------------------------
void GetAcls(const std::string& path, std::string& acls, bool is_sys = false,
bool take_lock = true);
//----------------------------------------------------------------------------
//! Modify the acls for a path
//!
//! @param acl acl ProtoBuf object
//!
//! @return 0 if successful, otherwise errno
//----------------------------------------------------------------------------
int ModifyAcls(const eos::console::AclProto& acl);
//----------------------------------------------------------------------------
//! Generate acl string representation from a rule map
//!
//! @param rmap map of rules to be used for conversion
//!
//! @return true if conversion successful, otherwise false
//----------------------------------------------------------------------------
static std::string GenerateAclString(const RuleMap& rmap);
//----------------------------------------------------------------------------
//! Parse command line (modification) rule given by the client. This specifies
//! the modifications to be operated on the current acls of the dir(s).
//!
//! @param input string rule from command line
//!
//! @return bool true if rule is correct, otherwise false
//----------------------------------------------------------------------------
bool ParseRule(const std::string& input);
//----------------------------------------------------------------------------
//! Apply client modification rule(s) to the acls of the current entry
//!
//! @param rules map of acl rules for the current entry (directory)
//----------------------------------------------------------------------------
void ApplyRule(RuleMap& rules, size_t pos = 0);
//----------------------------------------------------------------------------
//! Convert ACL bitmask to string representation
//!
//! @param in ACL bitmask
//!
//! @return std::string representation of ACL
//----------------------------------------------------------------------------
static std::string AclBitmaskToString(const unsigned short in);
std::string mErr; ///< Command error output string
};
EOSMGMNAMESPACE_END