# # # patch "app_state.cc" # from [6be9949e433ff6166fd030b34d8eb2d3e18c10b6] # to [27dc5372eb2796eb69b378f976cbe977f3466ef3] # # patch "app_state.hh" # from [f02f3114daf8b81c4a46a5741b1994cc95ebc692] # to [9c2f561213c3551e5aaed601a7847e6c708aabb7] # # patch "cert.cc" # from [6a58e3628ddaa11c5dbcbab1eb93816c074849f6] # to [6c245d8c84aba041f70e2c6fd28c1e4ab7410844] # # patch "cert.hh" # from [f34ca707b7e36e98331a580c2b411b65719e3116] # to [78f9c8cfd3726258dbf1ffd97f2e8a7ede4e1feb] # # patch "lua_hooks.cc" # from [26dbfb8306a7b7b4871f61877c7fd0f72abadfdd] # to [25272dcdc66248024922a306dfa46d90dc687dbd] # # patch "lua_hooks.hh" # from [a03b6a9de65a23204776355184e50a093f65f649] # to [a49284b3b3173b7639dbf3459901a27ea12c5a49] # # patch "project.cc" # from [b3e058f60be065000801b36916c50876b6287133] # to [b6f5000d510276c6d00d996d5464fac8531e03f2] # # patch "project.hh" # from [07509eac20359a3488d2264d85923f0e8f4c75fa] # to [0de01c9da8540fdd2915fc7026a4002b53c87ec5] # ============================================================ --- app_state.cc 6be9949e433ff6166fd030b34d8eb2d3e18c10b6 +++ app_state.cc 27dc5372eb2796eb69b378f976cbe977f3466ef3 @@ -32,8 +32,7 @@ app_state::app_state() : db(system_path()), keys(*this), work(db, lua), branch_is_sticky(false), - mtn_automate_allowed(false), - project(*this) + mtn_automate_allowed(false) { db.set_app(this); lua.set_app(this); @@ -215,7 +214,26 @@ app_state::get_project() project_t & app_state::get_project() { - return project; + if (projects.empty()) + { + map project_definitions; + lua.hook_get_projects(project_definitions); + for (map::const_iterator i = project_definitions.begin(); + i != project_definitions.end(); ++i) + { + projects.insert(make_pair(i->first, + project_t(i->first, + i->second, + *this))); + } + if (projects.empty()) + { + projects.insert(std::make_pair("default", project_t(*this))); + } + N(projects.size() == 1, F("multiple projects not supported yet")); + } + I(projects.size() == 1); + return projects.begin()->second; } // rc files are loaded after we've changed to the workspace so that ============================================================ --- app_state.hh f02f3114daf8b81c4a46a5741b1994cc95ebc692 +++ app_state.hh 9c2f561213c3551e5aaed601a7847e6c708aabb7 @@ -84,7 +84,7 @@ private: void make_branch_sticky(); private: - project_t project; + std::map projects; public: //project_t & get_project(string const & name); project_t & get_project(); // get_project(opts.project) or I() ============================================================ --- cert.cc 6a58e3628ddaa11c5dbcbab1eb93816c074849f6 +++ cert.cc 6c245d8c84aba041f70e2c6fd28c1e4ab7410844 @@ -158,8 +158,17 @@ erase_bogus_certs(vector< manifest certs = tmp_certs; } +void erase_bogus_certs(std::vector< revision > & certs, + app_state & app) +{ + erase_bogus_certs(certs, + boost::bind(&lua_hooks::hook_get_revision_cert_trust, + &app.lua, _1, _2, _3, _4), + app); +} void erase_bogus_certs(vector< revision > & certs, + trust_function trust_fn, app_state & app) { typedef vector< revision >::iterator it; @@ -197,10 +206,10 @@ erase_bogus_certs(vector< revision { cert_value decoded_value; decode_base64(get<2>(i->first), decoded_value); - if (app.lua.hook_get_revision_cert_trust(*(i->second.first), - get<0>(i->first), - get<1>(i->first), - decoded_value)) + if (trust_fn(*(i->second.first), + get<0>(i->first), + get<1>(i->first), + decoded_value)) { L(FL("trust function liked %d signers of %s cert on revision %s") % i->second.first->size() % get<1>(i->first) % get<0>(i->first)); ============================================================ --- cert.hh f34ca707b7e36e98331a580c2b411b65719e3116 +++ cert.hh 78f9c8cfd3726258dbf1ffd97f2e8a7ede4e1feb @@ -17,6 +17,9 @@ #include "vocab.hh" #include "dates.hh" +#include +#include + // Certs associate an opaque name/value pair with a revision ID, and // are accompanied by an RSA public-key signature attesting to the // association. Users can write as much extra meta-data as they like @@ -81,9 +84,19 @@ void put_simple_revision_cert(revision_i cert_value const & val, app_state & app); + +typedef boost::function const &, + hexenc const &, + cert_name const &, + cert_value const &)> trust_function; + void erase_bogus_certs(std::vector< revision > & certs, + trust_function trust_fn, app_state & app); +void erase_bogus_certs(std::vector< revision > & certs, + app_state & app); + void erase_bogus_certs(std::vector< manifest > & certs, app_state & app); ============================================================ --- lua_hooks.cc 26dbfb8306a7b7b4871f61877c7fd0f72abadfdd +++ lua_hooks.cc 25272dcdc66248024922a306dfa46d90dc687dbd @@ -440,8 +440,31 @@ lua_hooks::hook_accept_testresult_change return exec_ok && ok; } +bool +lua_hooks::hook_get_projects(std::map & project_definitions) +{ + project_definitions.clear(); + bool x; + Lua ll(st); + ll.func("get_projects") + .call(0,1); + ll.begin(); + while(ll.next()) + { + string key; + string value; + ll.extract_str(value).pop().extract_str(key); + if (ll.ok()) + project_definitions.insert(make_pair(key, system_path(value))); + } + + return ll.ok(); +} + + + bool lua_hooks::hook_merge3(file_path const & anc_path, file_path const & left_path, ============================================================ --- lua_hooks.hh a03b6a9de65a23204776355184e50a093f65f649 +++ lua_hooks.hh a49284b3b3173b7639dbf3459901a27ea12c5a49 @@ -69,6 +69,10 @@ public: bool hook_accept_testresult_change(std::map const & old_results, std::map const & new_results); + + bool hook_get_projects(std::map & project_definitions); + + // network hooks bool hook_get_netsync_key(utf8 const & server_address, globish const & include, ============================================================ --- project.cc b3e058f60be065000801b36916c50876b6287133 +++ project.cc b6f5000d510276c6d00d996d5464fac8531e03f2 @@ -1,11 +1,15 @@ // 2007 Timothy Brownawell // GNU GPL V2 or later #include "base.hh" #include "vector.hh" +#include +#include #include "app_state.hh" +#include "basic_io.hh" #include "cert.hh" +#include "globish.hh" #include "project.hh" #include "revision.hh" #include "transforms.hh" @@ -13,16 +17,333 @@ using std::vector; using std::string; using std::set; using std::vector; +using std::map; using std::multimap; using std::make_pair; +using boost::shared_ptr; +namespace basic_io +{ + namespace syms + { + symbol const branch_id("branch_id"); + symbol const committer("committer"); + symbol const policy("policy_branch_id"); + symbol const administrator("administrator"); + } +} + +struct branch_policy +{ + branch_name const visible_name; + branch_name const branch_cert_value; + set const committers; + + branch_policy(branch_name const & name, + branch_name const & value, + set const & keys) + : visible_name(name), + branch_cert_value(value), + committers(keys) + { } +}; + +class policy_revision; + +class policy_branch +{ + branch_name prefix; + branch_name my_branch_cert_value; + set my_committers; + + app_state & app; + shared_ptr rev; + void init(data const & spec) + { + basic_io::input_source src(spec(), "policy spec"); + basic_io::tokenizer tok(src); + basic_io::parser pa(tok); + + while (pa.symp()) + { + if(pa.symp(basic_io::syms::policy)) + { + pa.sym(); + string branch; + pa.str(branch); + my_branch_cert_value = branch_name(branch); + } + else if (pa.symp(basic_io::syms::administrator)) + { + pa.sym(); + string key; + pa.str(key); + my_committers.insert(rsa_keypair_id(key)); + } + else + { + N(false, F("Unable to understand policy spec file")); + } + } + + I(src.lookahead == EOF); + } +public: + policy_branch(data const & spec, + branch_name const & prefix, + app_state & app) + : prefix(prefix), app(app) + { + init(spec); + } + policy_branch(system_path const & spec_file, + branch_name const & prefix, + app_state & app) + : prefix(prefix), app(app) + { + require_path_is_file(spec_file, + F("policy spec file %s does not exist") % spec_file, + F("policy spec file %s is a directory") % spec_file); + data spec; + read_data(spec_file, spec); + init(spec); + } + shared_ptr get_policy(); + map branches(); +}; + +class policy_revision +{ + map branches; + map delegations; +public: + policy_revision(app_state & app, + revision_id const & rev, + branch_name const & prefix) + { + roster_t roster; + app.db.get_roster(rev, roster); + + file_path branch_dir = file_path_internal("branches"); + file_path delegation_dir = file_path_internal("delegations"); + + if (roster.has_node(branch_dir)) + { + dir_t branch_node = downcast_to_dir_t(roster.get_node(branch_dir)); + for (dir_map::const_iterator i = branch_node->children.begin(); + i != branch_node->children.end(); ++i) + { + branch_name branch; + if (i->first() != "__main__") + { + branch = branch_name(prefix() + "." + i->first()); + } + else + { + branch = prefix; + } + file_id ident = downcast_to_file_t(i->second)->content; + file_data spec; + app.db.get_file_version(ident, spec); + + branch_name branch_cert_value; + set committers; + + basic_io::input_source src(spec.inner()(), "branch spec"); + basic_io::tokenizer tok(src); + basic_io::parser pa(tok); + + while (pa.symp()) + { + if (pa.symp(basic_io::syms::branch_id)) + { + pa.sym(); + string branch; + pa.str(branch); + branch_cert_value = branch_name(branch); + } + else if (pa.symp(basic_io::syms::committer)) + { + pa.sym(); + string key; + pa.str(key); + committers.insert(rsa_keypair_id(key)); + } + else + { + N(false, + F("Unable to understand branch spec file for %s in revision %s") + % i->first() % rev); + } + } + + branches.insert(make_pair(branch, + branch_policy(branch, + branch_cert_value, + committers))); + } + } + if (roster.has_node(delegation_dir)) + { + dir_t delegation_node = downcast_to_dir_t(roster.get_node(delegation_dir)); + for (dir_map::const_iterator i = delegation_node->children.begin(); + i != delegation_node->children.end(); ++i) + { + branch_name subprefix(prefix() + "." + i->first()); + file_id ident = downcast_to_file_t(i->second)->content; + file_data spec; + app.db.get_file_version(ident, spec); + + delegations.insert(make_pair(subprefix, + policy_branch(spec.inner(), + subprefix, + app))); + } + } + } + map all_branches() + { + typedef map branch_policies; + typedef map policy_branches; + branch_policies out = branches; + for (policy_branches::iterator i = delegations.begin(); + i != delegations.end(); ++i) + { + branch_policies del = i->second.branches(); + for (branch_policies::const_iterator i = del.begin(); + i != del.end(); ++i) + { + out.insert(*i); + } + } + return out; + } +}; + +namespace +{ + struct not_in_policy_branch : public is_failure + { + app_state & app; + base64 const & branch_encoded; + set const & trusted_signers; + bool is_trusted(set const & signers, + hexenc const & rid, + cert_name const & name, + cert_value const & value) + { + for (set::const_iterator i = signers.begin(); + i != signers.end(); ++i) + { + set::const_iterator t = trusted_signers.find(*i); + if (t != trusted_signers.end()) + return true; + } + return false; + } + not_in_policy_branch(app_state & app, + base64 const & branch_encoded, + set const & trusted) + : app(app), branch_encoded(branch_encoded), trusted_signers(trusted) + {} + virtual bool operator()(revision_id const & rid) + { + vector< revision > certs; + app.db.get_revision_certs(rid, + cert_name(branch_cert_name), + branch_encoded, + certs); + erase_bogus_certs(certs, + boost::bind(¬_in_policy_branch::is_trusted, + this, _1, _2, _3, _4), + app); + return certs.empty(); + } + }; + + revision_id policy_branch_head(branch_name const & name, + set const & trusted_signers, + app_state & app) + { + L(FL("getting heads of policy branch %s") % name); + base64 branch_encoded; + encode_base64(cert_value(name()), branch_encoded); + set heads; + + app.db.get_revisions_with_cert(cert_name(branch_cert_name), + branch_encoded, + heads); + + not_in_policy_branch p(app, branch_encoded, trusted_signers); + erase_ancestors_and_failures(heads, p, app, NULL); + + E(heads.size() == 1, + F("policy branch %s has %d heads, should have 1 head") + % name % heads.size()); + + return *heads.begin(); + } +} + + +shared_ptr policy_branch::get_policy() +{ + if (!rev) + { + revision_id rid; + rid = policy_branch_head(my_branch_cert_value, my_committers, app); + rev.reset(new policy_revision(app, rid, prefix)); + } + return rev; +} +map policy_branch::branches() +{ + shared_ptr policy = get_policy(); + return policy->all_branches(); +} + +//////////////////////////////////////////////////////////////////////// + +class policy_info +{ +public: + policy_branch policy; + bool passthru; + policy_info(system_path const & spec_file, + branch_name const & prefix, + app_state & app) + : policy(spec_file, prefix, app), passthru(false) + { + } + explicit policy_info(app_state & app) + : policy(data(""), branch_name(""), app), passthru(true) + { + } +}; + +project_t::project_t(string const & project_name, + system_path const & spec_file, + app_state & app) + : project_policy(new policy_info(spec_file, branch_name(project_name), app)), app(app) +{} + project_t::project_t(app_state & app) - : app(app) + : project_policy(new policy_info(app)), app(app) {} void project_t::get_branch_list(std::set & names, bool allow_suspend_certs) { + if (!project_policy->passthru) + { + map branches = project_policy->policy.branches(); + for (map::const_iterator i = branches.begin(); + i != branches.end(); ++i) + { + names.insert(i->first); + } + return; + } if (indicator.outdated()) { std::vector got; @@ -53,6 +374,18 @@ project_t::get_branch_list(globish const std::set & names, bool allow_suspend_certs) { + if (!project_policy->passthru) + { + map branches = project_policy->policy.branches(); + for (map::const_iterator i = branches.begin(); + i != branches.end(); ++i) + { + if (glob.matches(i->first())) + names.insert(i->first); + } + return; + } + std::vector got; app.db.get_branches(glob, got); names.clear(); @@ -100,7 +433,7 @@ namespace app_state & app; base64 const & branch_encoded; suspended_in_branch(app_state & app, - base64 const & branch_encoded) + base64 const & branch_encoded) : app(app), branch_encoded(branch_encoded) {} virtual bool operator()(revision_id const & rid) ============================================================ --- project.hh 07509eac20359a3488d2264d85923f0e8f4c75fa +++ project.hh 0de01c9da8540fdd2915fc7026a4002b53c87ec5 @@ -25,15 +25,21 @@ typedef bool suspended_indicator; typedef bool suspended_indicator; +class policy_info; + class project_t { + boost::shared_ptr project_policy; app_state & app; std::map, std::pair > > branch_heads; std::set branches; outdated_indicator indicator; public: - project_t(app_state & app); + project_t(std::string const & project_name, + system_path const & spec_file, + app_state & app); + explicit project_t(app_state & app); void get_branch_list(std::set & names, bool allow_suspend_certs = true); void get_branch_list(globish const & glob, std::set & names,