Drizzled Public API Documentation

module.cc
00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2010 Monty Taylor <mordred@inaugust.com>
00005  *  Copyright (C) 2011 Canonical, Ltd.
00006  *  Author: Clint Byrum <clint.byrum@canonical.com>
00007  *
00008  *  Copied from simple_user_policy
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; version 2 of the License.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program; if not, write to the Free Software
00021  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00022  */
00023 
00024 #include <config.h>
00025 
00026 #include <drizzled/plugin/authorization.h>
00027 #include <drizzled/module/option_map.h>
00028 
00029 #include "policy.h"
00030 
00031 #include <fstream>
00032 
00033 namespace po= boost::program_options;
00034 
00035 using namespace std;
00036 using namespace drizzled;
00037 
00038 namespace regex_policy
00039 {
00040 
00041 static int init(module::Context &context)
00042 {
00043   const module::option_map &vm= context.getOptions();
00044 
00045   Policy *policy= new Policy(fs::path(vm["policy"].as<string>()));
00046   if (not policy->loadFile())
00047   {
00048     errmsg_printf(error::ERROR, _("Could not load regex policy file: %s\n"),
00049                   (policy ? policy->getError().str().c_str() : _("Unknown")));
00050     delete policy;
00051     return 1;
00052   }
00053 
00054   context.add(policy);
00055   context.registerVariable(new sys_var_const_string_val("policy", vm["policy"].as<string>()));
00056 
00057   return 0;
00058 }
00059 
00060 static void init_options(drizzled::module::option_context &context)
00061 {
00062   context("policy",
00063       po::value<string>()->default_value(DEFAULT_POLICY_FILE.string()),
00064       N_("File to load for regex authorization policies"));
00065 }
00066 
00067 bool Policy::loadFile()
00068 {
00069   ifstream file(policy_file.string().c_str());
00070   boost::regex comment_re;
00071   boost::regex empty_re;
00072   boost::regex table_matches_re;
00073   boost::regex process_matches_re;
00074   boost::regex schema_matches_re;
00075 
00076   try
00077   {
00078     comment_re= comment_regex;
00079     empty_re= empty_regex;
00080     table_matches_re= table_match_regex;
00081     process_matches_re= process_match_regex;
00082     schema_matches_re= schema_match_regex;
00083   }   
00084   catch (const std::exception &e)
00085   {
00086     error << e.what();
00087     return false;
00088   }
00089 
00090   if (! file.is_open())
00091   {
00092     error << "Unable to open regex policy file: " << policy_file.string();
00093     return false;
00094   }
00095 
00096   int lines= 0;
00097   try
00098   {
00099     while (! file.eof())
00100     {
00101       ++lines;
00102       string line;
00103       getline(file, line);
00104       if (boost::regex_match(line, comment_re))
00105       {
00106         continue;
00107       }
00108       if (boost::regex_match(line, empty_re))
00109       {
00110         continue;
00111       }
00112       boost::smatch matches;
00113       PolicyItemList *policies;
00114       if (boost::regex_match(line, matches, table_matches_re, boost::match_extra))
00115       {
00116         policies= &table_policies;
00117       }
00118       else if (boost::regex_match(line, matches, process_matches_re, boost::match_extra))
00119       {
00120         policies= &process_policies;
00121       }
00122       else if (boost::regex_match(line, matches, schema_matches_re, boost::match_extra))
00123       {
00124         policies= &schema_policies;
00125       }
00126       else
00127       {
00128         throw std::exception();
00129       }
00130       string user_regex;
00131       string object_regex;
00132       string action;
00133       user_regex= matches[MATCH_REGEX_USER_POS];
00134       object_regex= matches[MATCH_REGEX_OBJECT_POS];
00135       action= matches[MATCH_REGEX_ACTION_POS];
00136       PolicyItem *i;
00137       try
00138       {
00139         i= new PolicyItem(user_regex, object_regex, action);
00140       }
00141       catch (const std::exception &e)
00142       {
00143         error << "Bad policy item: user=" << user_regex << " object=" << object_regex << " action=" << action;
00144         throw std::exception();
00145       }
00146       policies->push_back(i);
00147     }
00148     return true;
00149   }
00150   catch (const std::exception &e)
00151   {
00152     /* On any non-EOF break, unparseable line */
00153     error << "Unable to parse line " << lines << " of policy file " << policy_file.string() << ":" << e.what();
00154     return false;
00155   }
00156 }
00157 
00158 void clearPolicyItemList(PolicyItemList policies)
00159 {
00160   for (PolicyItemList::iterator x= policies.begin() ; x != policies.end() ; ++x)
00161   {
00162     delete *x;
00163     *x= NULL;
00164   }
00165 } 
00166 
00167 Policy::~Policy()
00168 {
00169   clearPolicyItemList(table_policies);
00170   clearPolicyItemList(process_policies);
00171   clearPolicyItemList(schema_policies);
00172   delete table_check_cache;
00173   delete process_check_cache;
00174   delete schema_check_cache;
00175 }
00176 
00177 bool Policy::restrictObject(const drizzled::identifier::User &user_ctx,
00178                                    const string &obj, const PolicyItemList &policies,
00179                                    CheckMap **check_cache)
00180 {
00181   CheckItem c(user_ctx.username(), obj, check_cache);
00182   if (!c.hasCachedResult())
00183   {
00184     PolicyItemList::const_iterator m= find_if(policies.begin(), policies.end(), c);
00185     if (m != policies.end())
00186     {
00187       c.setCachedResult((*m)->isRestricted());
00188     }
00189     else
00190     {
00191       /* TODO: make default action configurable */
00192       c.setCachedResult(false);
00193     }
00194   }
00195   return c.getCachedResult();
00196 }
00197 
00198 bool Policy::restrictSchema(const drizzled::identifier::User &user_ctx,
00199                                    const drizzled::identifier::Schema& schema)
00200 {
00201   return restrictObject(user_ctx, schema.getSchemaName(), schema_policies, &schema_check_cache);
00202 }
00203 
00204 bool Policy::restrictProcess(const drizzled::identifier::User &user_ctx,
00205                                     const drizzled::identifier::User &session_ctx)
00206 {
00207   return restrictObject(user_ctx, session_ctx.username(), process_policies, &process_check_cache);
00208 }
00209 
00210 bool Policy::restrictTable(const drizzled::identifier::User& user_ctx,
00211                              const drizzled::identifier::Table& table)
00212 {
00213   return restrictObject(user_ctx, table.getTableName(), table_policies, &table_check_cache);
00214 }
00215 
00216 bool CheckItem::operator()(PolicyItem *p)
00217 {
00218   if (p->userMatches(user))
00219   {
00220     errmsg_printf(error::INSPECT, _("User %s matches regex\n"), user.c_str());
00221     if (p->objectMatches(object))
00222     {
00223       errmsg_printf(error::INSPECT, _("Object %s matches regex %s (%s)\n"), 
00224           object.c_str(),
00225           p->getObject().c_str(),
00226           p->getAction());
00227       return true;
00228     }
00229     errmsg_printf(error::INSPECT, _("Object %s NOT restricted by regex %s (%s)\n"), 
00230         object.c_str(),
00231         p->getObject().c_str(),
00232         p->getAction());
00233   }
00234   return false;
00235 }
00236 
00237 CheckItem::CheckItem(const std::string &user_in, const std::string &obj_in, CheckMap **check_cache_in)
00238   : user(user_in), object(obj_in), has_cached_result(false), check_cache(check_cache_in)
00239 {
00240   CheckMap::iterator check_val;
00241   std::stringstream keystream;
00242   keystream << user << "_" << object;
00243   key= keystream.str();
00244 
00245   /* using RCU to only need to lock when updating the cache */
00246   if ((*check_cache) && (check_val= (*check_cache)->find(key)) != (*check_cache)->end())
00247   {
00248     setCachedResult(check_val->second);
00249   }
00250 }
00251 
00252 void CheckItem::setCachedResult(bool result)
00253 {
00254   // TODO: make the mutex per-cache
00255   boost::mutex::scoped_lock lock(check_cache_mutex, boost::defer_lock);
00256   lock.lock();
00257 
00258   // Copy the current one
00259   CheckMap* new_cache= *check_cache ? new CheckMap(**check_cache) : new CheckMap;
00260 
00261   // Update it
00262   (*new_cache)[key]= result;
00263   // Replace old
00264   CheckMap* old_cache= *check_cache;
00265   *check_cache= new_cache;
00266 
00267   lock.unlock();
00268   has_cached_result= true;
00269   cached_result= result;
00270 
00271   delete old_cache;
00272 }
00273 
00274 } /* namespace regex_policy */
00275 
00276 DRIZZLE_PLUGIN(regex_policy::init, NULL, regex_policy::init_options);