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 namespace po= boost::program_options;
00032 
00033 using namespace std;
00034 using namespace drizzled;
00035 
00036 namespace regex_policy
00037 {
00038 
00039 static int init(module::Context &context)
00040 {
00041   const module::option_map &vm= context.getOptions();
00042 
00043   Policy *policy= new (nothrow) Policy(fs::path(vm["policy"].as<string>()));
00044   if (policy == NULL or not policy->loadFile())
00045   {
00046     errmsg_printf(error::ERROR, _("Could not load regex policy file: %s\n"),
00047                   (policy ? policy->getError().str().c_str() : _("Unknown")));
00048     if (policy)
00049     {
00050       delete policy;
00051     }
00052     return 1;
00053   }
00054 
00055   context.add(policy);
00056   context.registerVariable(new sys_var_const_string_val("policy", vm["policy"].as<string>()));
00057 
00058   return 0;
00059 }
00060 
00061 static void init_options(drizzled::module::option_context &context)
00062 {
00063   context("policy",
00064       po::value<string>()->default_value(DEFAULT_POLICY_FILE.string()),
00065       N_("File to load for regex authorization policies"));
00066 }
00067 
00068 bool Policy::loadFile()
00069 {
00070   ifstream file(policy_file.string().c_str());
00071   boost::regex comment_re;
00072   boost::regex empty_re;
00073   boost::regex table_matches_re;
00074   boost::regex process_matches_re;
00075   boost::regex schema_matches_re;
00076 
00077   try
00078   {
00079     comment_re= comment_regex;
00080     empty_re= empty_regex;
00081     table_matches_re= table_match_regex;
00082     process_matches_re= process_match_regex;
00083     schema_matches_re= schema_match_regex;
00084   }   
00085   catch (const std::exception &e)
00086   {
00087     error << e.what();
00088     return false;
00089   }
00090 
00091   if (! file.is_open())
00092   {
00093     error << "Unable to open regex policy file: " << policy_file.string();
00094     return false;
00095   }
00096 
00097   int lines= 0;
00098   try
00099   {
00100     while (! file.eof())
00101     {
00102       ++lines;
00103       string line;
00104       getline(file, line);
00105       if (boost::regex_match(line, comment_re))
00106       {
00107         continue;
00108       }
00109       if (boost::regex_match(line, empty_re))
00110       {
00111         continue;
00112       }
00113       boost::smatch matches;
00114       PolicyItemList *policies;
00115       if (boost::regex_match(line, matches, table_matches_re, boost::match_extra))
00116       {
00117         policies= &table_policies;
00118       }
00119       else if (boost::regex_match(line, matches, process_matches_re, boost::match_extra))
00120       {
00121         policies= &process_policies;
00122       }
00123       else if (boost::regex_match(line, matches, schema_matches_re, boost::match_extra))
00124       {
00125         policies= &schema_policies;
00126       }
00127       else
00128       {
00129         throw std::exception();
00130       }
00131       string user_regex;
00132       string object_regex;
00133       string action;
00134       user_regex= matches[MATCH_REGEX_USER_POS];
00135       object_regex= matches[MATCH_REGEX_OBJECT_POS];
00136       action= matches[MATCH_REGEX_ACTION_POS];
00137       PolicyItem *i;
00138       try
00139       {
00140         i= new PolicyItem(user_regex, object_regex, action);
00141       }
00142       catch (const std::exception &e)
00143       {
00144         error << "Bad policy item: user=" << user_regex << " object=" << object_regex << " action=" << action;
00145         throw std::exception();
00146       }
00147       policies->push_back(i);
00148     }
00149     return true;
00150   }
00151   catch (const std::exception &e)
00152   {
00153     /* On any non-EOF break, unparseable line */
00154     error << "Unable to parse line " << lines << " of policy file " << policy_file.string() << ":" << e.what();
00155     return false;
00156   }
00157 }
00158 
00159 void clearPolicyItemList(PolicyItemList policies)
00160 {
00161   for (PolicyItemList::iterator x= policies.begin() ; x != policies.end() ; ++x)
00162   {
00163     delete *x;
00164     *x= NULL;
00165   }
00166 } 
00167 
00168 Policy::~Policy()
00169 {
00170   clearPolicyItemList(table_policies);
00171   clearPolicyItemList(process_policies);
00172   clearPolicyItemList(schema_policies);
00173   delete table_check_cache;
00174   delete process_check_cache;
00175   delete schema_check_cache;
00176 }
00177 
00178 bool Policy::restrictObject(const drizzled::identifier::User &user_ctx,
00179                                    const string &obj, const PolicyItemList &policies,
00180                                    CheckMap **check_cache)
00181 {
00182   CheckItem c(user_ctx.username(), obj, check_cache);
00183   if (!c.hasCachedResult())
00184   {
00185     PolicyItemList::const_iterator m= find_if(policies.begin(), policies.end(), c);
00186     if (m != policies.end())
00187     {
00188       c.setCachedResult((*m)->isRestricted());
00189     }
00190     else
00191     {
00192       /* TODO: make default action configurable */
00193       c.setCachedResult(false);
00194     }
00195   }
00196   return c.getCachedResult();
00197 }
00198 
00199 bool Policy::restrictSchema(const drizzled::identifier::User &user_ctx,
00200                                    drizzled::identifier::Schema::const_reference schema)
00201 {
00202   return restrictObject(user_ctx, schema.getSchemaName(), schema_policies, &schema_check_cache);
00203 }
00204 
00205 bool Policy::restrictProcess(const drizzled::identifier::User &user_ctx,
00206                                     const drizzled::identifier::User &session_ctx)
00207 {
00208   return restrictObject(user_ctx, session_ctx.username(), process_policies, &process_check_cache);
00209 }
00210 
00211 bool Policy::restrictTable(drizzled::identifier::User::const_reference user_ctx,
00212                              drizzled::identifier::Table::const_reference table)
00213 {
00214   return restrictObject(user_ctx, table.getTableName(), table_policies, &table_check_cache);
00215 }
00216 
00217 bool CheckItem::operator()(PolicyItem *p)
00218 {
00219   if (p->userMatches(user))
00220   {
00221     errmsg_printf(error::INSPECT, _("User %s matches regex\n"), user.c_str());
00222     if (p->objectMatches(object))
00223     {
00224       errmsg_printf(error::INSPECT, _("Object %s matches regex %s (%s)\n"), 
00225           object.c_str(),
00226           p->getObject().c_str(),
00227           p->getAction());
00228       return true;
00229     }
00230     errmsg_printf(error::INSPECT, _("Object %s NOT restricted by regex %s (%s)\n"), 
00231         object.c_str(),
00232         p->getObject().c_str(),
00233         p->getAction());
00234   }
00235   return false;
00236 }
00237 
00238 CheckItem::CheckItem(const std::string &user_in, const std::string &obj_in, CheckMap **check_cache_in)
00239   : user(user_in), object(obj_in), has_cached_result(false), check_cache(check_cache_in)
00240 {
00241   CheckMap::iterator check_val;
00242   std::stringstream keystream;
00243   keystream << user << "_" << object;
00244   key= keystream.str();
00245 
00246   /* using RCU to only need to lock when updating the cache */
00247   if ((*check_cache) && (check_val= (*check_cache)->find(key)) != (*check_cache)->end())
00248   {
00249     setCachedResult(check_val->second);
00250   }
00251 }
00252 
00253 void CheckItem::setCachedResult(bool result)
00254 {
00255   // TODO: make the mutex per-cache
00256   CheckMap *old_cache;
00257   CheckMap *new_cache;
00258   boost::mutex::scoped_lock lock(check_cache_mutex, boost::defer_lock);
00259   lock.lock();
00260 
00261   // Copy the current one
00262   if (*check_cache)
00263   {
00264     new_cache= new CheckMap(**check_cache);
00265   }
00266   else
00267   {
00268     new_cache= new CheckMap();
00269   }
00270 
00271   // Update it
00272   (*new_cache)[key]= result;
00273   // Replace old
00274   old_cache= *check_cache;
00275   *check_cache= new_cache;
00276 
00277   lock.unlock();
00278   has_cached_result= true;
00279   cached_result= result;
00280 
00281   if (old_cache)
00282   {
00283     delete old_cache;
00284   }
00285 }
00286 
00287 } /* namespace regex_policy */
00288 
00289 DRIZZLE_PLUGIN(regex_policy::init, NULL, regex_policy::init_options);