7 files changed, 126 insertions(+), 24 deletions(-)
ocaml/idl/datamodel.ml | 23 +++++----
ocaml/idl/datamodel_types.ml | 4 +
ocaml/idl/datamodel_utils.ml | 6 ++
ocaml/idl/ocaml_backend/gen_rbac.ml | 21 +++++++-
ocaml/idl/ocaml_backend/gen_server.ml | 3 -
ocaml/idl/ocaml_backend/rbac.ml | 79 +++++++++++++++++++++++++++++++--
ocaml/idl/ocaml_backend/rbac_audit.ml | 14 ++---
# HG changeset patch
# User Marcus Granado <marcus.granado@xxxxxxxxxx>
# Date 1259255339 0
# Node ID c9fd958b25ee2e4334558024f64bf0659410e756
# Parent c2e75d891d3dd4542066384cf07331bf727500ca
CA-35368: add support to per-key RBAC
Signed-off-by: Marcus Granado <marcus.granado@xxxxxxxxxxxxx>
diff -r c2e75d891d3d -r c9fd958b25ee ocaml/idl/datamodel.ml
--- a/ocaml/idl/datamodel.ml Fri Nov 13 14:01:05 2009 +0000
+++ b/ocaml/idl/datamodel.ml Thu Nov 26 17:08:59 2009 +0000
@@ -187,6 +187,7 @@
?(no_current_operations=false) ?(secret=false) ?(hide_from_docs=false)
?(pool_internal=false)
~allowed_roles
+ ?(map_keys_roles=[])
?(params=[]) ?versioned_params () =
(* if you specify versioned_params then these get put in the params field of
the message record;
otherwise params go in with no default values and
param_release=call_release...
@@ -213,7 +214,8 @@
msg_no_current_operations = no_current_operations;
msg_hide_from_docs = hide_from_docs;
msg_pool_internal = pool_internal;
- msg_allowed_roles = allowed_roles
+ msg_allowed_roles = allowed_roles;
+ msg_map_keys_roles = map_keys_roles
}
let assert_operation_valid enum cls self = call
@@ -2537,7 +2539,9 @@
(** Make an object field record *)
let field ?(in_oss_since = Some "3.0.3") ?(in_product_since = rel_rio)
?(internal_only = false)
?internal_deprecated_since ?(ignore_foreign_key = false)
?(writer_roles=None) ?(reader_roles=None)
- ?(qualifier = RW) ?(ty = String) ?(effect = false) ?(default_value = None)
?(persist = true) name desc =
+ ?(qualifier = RW) ?(ty = String) ?(effect = false) ?(default_value = None)
?(persist = true)
+ ?(map_keys_roles=[]) (* list of (key_name,(writer_roles)) for a map field
*)
+ name desc =
Field { release={internal=get_product_releases in_product_since;
@@ -2552,6 +2556,7 @@
field_ignore_foreign_key = ignore_foreign_key;
field_setter_roles = writer_roles;
field_getter_roles = reader_roles;
+ field_map_keys_roles = map_keys_roles;
}
let uid ?(in_oss_since=Some "3.0.3") ?(reader_roles=None) refname =
@@ -2859,7 +2864,7 @@
field ~qualifier:DynamicRO ~ty:String "type" "if the task has completed
successfully, this field contains the type of the encoded result (i.e. name of
the class whose reference is in the result field). Undefined otherwise.";
field ~qualifier:DynamicRO ~ty:String "result" "if the task has
completed successfully, this field contains the result value (either Void or an
object reference). Undefined otherwise.";
field ~qualifier:DynamicRO ~ty:(Set String) "error_info" "if the task
has failed, this field contains the set of associated error strings. Undefined
otherwise.";
- field ~writer_roles:_R_VM_OP ~in_product_since:rel_miami
~default_value:(Some (VMap [])) ~ty:(Map(String, String)) "other_config"
"additional configuration";
+ field ~in_product_since:rel_miami ~default_value:(Some (VMap []))
~ty:(Map(String, String)) "other_config" "additional configuration"
~map_keys_roles:[("applies_to",(_R_VM_OP));("XenCenterUUID",(_R_VM_OP))];
(* field ~ty:(Set(Ref _alert)) ~in_product_since:rel_miami
~qualifier:DynamicRO "alerts" "all alerts related to this task"; *)
field ~qualifier:DynamicRO ~in_product_since:rel_orlando
~default_value:(Some (VRef "")) ~ty:(Ref _task) "subtask_of" "Ref pointing to
the task this is a substask of.";
field ~qualifier:DynamicRO ~in_product_since:rel_orlando ~ty:(Set (Ref
_task)) "subtasks" "List pointing to all the substasks.";
@@ -3481,7 +3486,7 @@
namespace ~name:"API_version" ~contents:api_version ();
field ~qualifier:DynamicRO ~ty:Bool "enabled" "True if the host is
currently enabled";
field ~qualifier:StaticRO ~ty:(Map(String, String)) "software_version"
"version strings";
- field ~ty:(Map(String, String)) "other_config" "additional
configuration";
+ field ~ty:(Map(String, String)) "other_config" "additional
configuration"
~map_keys_roles:[("folder",(_R_VM_OP));("XenCenter.CustomFields.*",(_R_VM_OP))];
field ~qualifier:StaticRO ~ty:(Set(String)) "capabilities" "Xen
capabilities";
field ~qualifier:DynamicRO ~ty:(Map(String, String))
"cpu_configuration" "The CPU configuration on this host. May contain keys such
as \"nr_nodes\", \"sockets_per_node\", \"cores_per_socket\", or
\"threads_per_core\"";
field ~qualifier:DynamicRO ~ty:String "sched_policy" "Scheduler policy
currently in force on this host";
@@ -3618,7 +3623,7 @@
] @ (allowed_and_current_operations ~writer_roles:_R_POOL_OP
network_operations) @ [
field ~qualifier:DynamicRO ~ty:(Set (Ref _vif)) "VIFs" "list of
connected vifs";
field ~qualifier:DynamicRO ~ty:(Set (Ref _pif)) "PIFs" "list of
connected pifs";
- field ~writer_roles:_R_POOL_OP ~ty:(Map(String, String)) "other_config"
"additional configuration" ;
+ field ~writer_roles:_R_POOL_OP ~ty:(Map(String, String)) "other_config"
"additional configuration"
~map_keys_roles:[("folder",(_R_VM_OP));("XenCenter.CustomFields.*",(_R_VM_OP));("XenCenterCreateInProgress",(_R_VM_OP))];
field ~in_oss_since:None ~qualifier:DynamicRO "bridge" "name of the
bridge corresponding to this network on the local host";
field ~qualifier:DynamicRO ~in_product_since:rel_orlando
~ty:(Map(String, Ref _blob)) ~default_value:(Some (VMap [])) "blobs" "Binary
blobs associated with this network";
field ~writer_roles:_R_POOL_OP ~in_product_since:rel_orlando
~default_value:(Some (VSet [])) ~ty:(Set String) "tags" "user-specified tags
for categorization purposes"
@@ -4112,7 +4117,7 @@
field ~qualifier:StaticRO "type" "type of the storage repository";
field ~qualifier:StaticRO "content_type" "the type of the SR's content,
if required (e.g. ISOs)";
field ~qualifier:DynamicRO "shared" ~ty:Bool "true if this SR is
(capable of being) shared between multiple hosts";
- field ~ty:(Map(String, String)) "other_config" "additional
configuration";
+ field ~ty:(Map(String, String)) "other_config" "additional
configuration"
~map_keys_roles:[("folder",(_R_VM_OP));("XenCenter.CustomFields.*",(_R_VM_OP))];
field ~in_product_since:rel_orlando ~default_value:(Some (VSet []))
~ty:(Set String) "tags" "user-specified tags for categorization purposes";
field ~ty:Bool ~qualifier:DynamicRO ~in_oss_since:None
~internal_only:true "default_vdi_visibility" "";
field ~in_oss_since:None ~ty:(Map(String, String))
~in_product_since:rel_miami ~qualifier:RW "sm_config" "SM dependent data"
~default_value:(Some (VMap []));
@@ -4385,7 +4390,7 @@
field ~qualifier:StaticRO ~ty:vdi_type "type" "type of the VDI";
field ~qualifier:StaticRO ~ty:Bool "sharable" "true if this disk may be
shared";
field ~qualifier:StaticRO ~ty:Bool "read_only" "true if this disk may
ONLY be mounted read-only";
- field ~ty:(Map(String, String)) "other_config" "additional
configuration" ;
+ field ~ty:(Map(String, String)) "other_config" "additional
configuration"
~map_keys_roles:[("folder",(_R_VM_OP));("XenCenter.CustomFields.*",(_R_VM_OP))];
field ~qualifier:DynamicRO ~ty:Bool "storage_lock" "true if this disk
is locked at the storage level";
(* XXX: location field was in the database in rio, now API in miami *)
field ~in_oss_since:None ~in_product_since:rel_miami ~ty:String
~qualifier:DynamicRO ~default_value:(Some (VString "")) "location" "location
information";
@@ -5026,7 +5031,7 @@
; field ~in_oss_since:None ~qualifier:RW ~ty:(Ref _sr)
"default_SR" "Default SR for VDIs"
; field ~in_oss_since:None ~qualifier:RW ~ty:(Ref _sr)
"suspend_image_SR" "The SR in which VDIs for suspend images are created"
; field ~in_oss_since:None ~qualifier:RW ~ty:(Ref _sr)
"crash_dump_SR" "The SR in which VDIs for crash dumps are created"
- ; field ~writer_roles:_R_VM_OP ~in_oss_since:None
~ty:(Map(String, String)) "other_config" "additional configuration"
+ ; field ~in_oss_since:None ~ty:(Map(String, String))
"other_config" "additional configuration"
~map_keys_roles:[("folder",(_R_VM_OP));("XenCenter.CustomFields.*",(_R_VM_OP));("EMPTY_FOLDERS",(_R_VM_OP))]
; field ~in_oss_since:None
~in_product_since:rel_orlando ~qualifier:DynamicRO ~ty:Bool
~default_value:(Some (VBool false)) "ha_enabled" "true if HA is enabled on the
pool, false otherwise"
; field ~in_oss_since:None
~in_product_since:rel_orlando ~qualifier:DynamicRO ~ty:(Map(String, String))
~default_value:(Some (VMap [])) "ha_configuration" "The current HA
configuration"
; field ~in_oss_since:None
~in_product_since:rel_orlando ~qualifier:DynamicRO ~ty:(Set String)
~default_value:(Some (VSet [])) "ha_statefiles" "HA statefile VDIs in use"
@@ -5437,7 +5442,7 @@
field ~ty:(Map(String, String)) "platform" "platform-specific
configuration";
field "PCI_bus" "PCI bus path for pass-through devices";
- field ~ty:(Map(String, String)) "other_config" "additional
configuration";
+ field ~ty:(Map(String, String)) "other_config" "additional
configuration"
~map_keys_roles:[("folder",(_R_VM_OP));("XenCenter.CustomFields.*",(_R_VM_OP))];
field ~qualifier:DynamicRO ~ty:Int "domid" "domain ID (if available, -1
otherwise)";
field ~qualifier:DynamicRO ~in_oss_since:None ~ty:String "domarch"
"Domain architecture (if available, null string otherwise)";
field ~in_oss_since:None ~qualifier:DynamicRO ~ty:(Map(String, String))
"last_boot_CPU_flags" "describes the CPU flags on which the VM was last booted";
diff -r c2e75d891d3d -r c9fd958b25ee ocaml/idl/datamodel_types.ml
--- a/ocaml/idl/datamodel_types.ml Fri Nov 13 14:01:05 2009 +0000
+++ b/ocaml/idl/datamodel_types.ml Thu Nov 26 17:08:59 2009 +0000
@@ -133,6 +133,7 @@
msg_custom_marshaller: bool;
msg_hide_from_docs: bool;
msg_allowed_roles: string list option;
+ msg_map_keys_roles: (string * (string list option)) list
}
and field = {
@@ -148,7 +149,8 @@
field_has_effect: bool;
field_ignore_foreign_key: bool;
field_setter_roles: string list option;
- field_getter_roles: string list option
+ field_getter_roles: string list option;
+ field_map_keys_roles: (string * (string list option)) list
}
and error = {
diff -r c2e75d891d3d -r c9fd958b25ee ocaml/idl/datamodel_utils.ml
--- a/ocaml/idl/datamodel_utils.ml Fri Nov 13 14:01:05 2009 +0000
+++ b/ocaml/idl/datamodel_utils.ml Thu Nov 26 17:08:59 2009 +0000
@@ -219,7 +219,8 @@
msg_hide_from_docs = false;
msg_pool_internal = false;
msg_db_only = fld.internal_only;
- msg_allowed_roles = None
+ msg_allowed_roles = None;
+ msg_map_keys_roles = []
} in
let getter = { common with
msg_name = prefix "get_";
@@ -283,6 +284,7 @@
"Add the given key-value pair to the %s field of the
given %s."
(String.concat "/" fld.full_name) x.name);
msg_allowed_roles = fld.field_setter_roles;
+ msg_map_keys_roles = List.map (fun (k,(w))->(k,w))
fld.field_map_keys_roles;
msg_tag = FromField(Add, fld) };
{ common with
msg_name = prefix "remove_from_";
@@ -293,6 +295,7 @@
"Remove the given key and its corresponding value
from the %s field of the given %s. If the key is not in that Map, then do
nothing."
(String.concat "/" fld.full_name) x.name);
msg_allowed_roles = fld.field_setter_roles;
+ msg_map_keys_roles = List.map (fun (k,(w))->(k,w))
fld.field_map_keys_roles;
msg_tag = FromField(Remove, fld) };
]
| t, _ -> [
@@ -320,6 +323,7 @@
msg_session=false; msg_release=x.obj_release;
msg_has_effect=false; msg_tag=Custom;
msg_force_custom = x.force_custom_actions;
msg_allowed_roles = None;
+ msg_map_keys_roles = [];
msg_obj_name=x.name } in
(* Constructor *)
let ctor = { common with
diff -r c2e75d891d3d -r c9fd958b25ee ocaml/idl/ocaml_backend/gen_rbac.ml
--- a/ocaml/idl/ocaml_backend/gen_rbac.ml Fri Nov 13 14:01:05 2009 +0000
+++ b/ocaml/idl/ocaml_backend/gen_rbac.ml Thu Nov 26 17:08:59 2009 +0000
@@ -79,7 +79,8 @@
let permission_description = "A basic permission"
let permission_name wire_name =
let s1 =replace_char (Printf.sprintf "permission_%s" wire_name) '.' '_'
in
- replace_char s1 '/' '_'
+ let s2 = replace_char s1 '/' '_' in
+ Stringext.String.replace "*" "WILDCHAR" s2
let permission_index = ref 0
let writer_permission name nperms =
@@ -213,8 +214,12 @@
let r,perms = match r1 with []->(xr,[])|r1::_->r1 in
concat (xperm,((r,xperm::perms)::r2),extra_rs)
+let get_key_permission_name permission key_name =
+ permission ^ "/key_" ^ key_name
+
let add_permission_to_roles roles_permissions (obj: obj) (x: message) =
let msg_allowed_roles = x.msg_allowed_roles in
+ let msg_map_keys_roles = x.msg_map_keys_roles in
let wire_name = DU.wire_name ~sync:true obj x in
match msg_allowed_roles with
| None -> (
@@ -222,7 +227,19 @@
(* a message should have at least one role *)
failwith (Printf.sprintf "No roles for message
%s" wire_name);
)
- | Some(allowed_roles) -> concat
(wire_name,roles_permissions,allowed_roles)
+ | Some(allowed_roles) ->
+ let with_msg_roles_permissions =
+ (concat
(wire_name,roles_permissions,allowed_roles))
+ in
+ List.fold_left
+ (fun rsps (k,rs)->
+ let wire_name_key =
get_key_permission_name wire_name k in
+ match rs with
+ |None->failwith (Printf.sprintf
"No roles for key %s" wire_name_key)
+ |Some(allowed_roles)->(concat
(wire_name_key, rsps, allowed_roles))
+ )
+ with_msg_roles_permissions
+ msg_map_keys_roles
let get_http_permissions_roles =
List.fold_left
diff -r c2e75d891d3d -r c9fd958b25ee ocaml/idl/ocaml_backend/gen_server.ml
--- a/ocaml/idl/ocaml_backend/gen_server.ml Fri Nov 13 14:01:05 2009 +0000
+++ b/ocaml/idl/ocaml_backend/gen_server.ml Thu Nov 26 17:08:59 2009 +0000
@@ -158,7 +158,8 @@
let rbac_check_begin = if has_session_arg
then [
"let arg_names = "^(List.fold_right (fun arg args ->
"\""^arg^"\"::"^args) string_args (if is_non_constructor_with_defaults then
"\"default_args\"::[]" else "[]"))^" in";
- "Rbac.check session_id __call
~args:(arg_names,__params) ~__context ~fn:(fun ()-> (*RBAC-BEGIN*)"]
+ "let key_names = "^(List.fold_right (fun arg args ->
"\""^arg^"\"::"^args) (List.map (fun (k,_)->k) x.msg_map_keys_roles) "[]")^"
in";
+ "Rbac.check session_id __call
~args:(arg_names,__params) ~keys:key_names ~__context ~fn:(fun ()->
(*RBAC-BEGIN*)"]
else []
in
let rbac_check_end = if has_session_arg then [") (*RBAC-END*)"] else [] in
diff -r c2e75d891d3d -r c9fd958b25ee ocaml/idl/ocaml_backend/rbac.ml
--- a/ocaml/idl/ocaml_backend/rbac.ml Fri Nov 13 14:01:05 2009 +0000
+++ b/ocaml/idl/ocaml_backend/rbac.ml Thu Nov 26 17:08:59 2009 +0000
@@ -95,6 +95,80 @@
session_permissions_tbl
session_id
+(* create a key permission name that can be in the session *)
+let get_key_permission_name permission key_name =
+ permission ^ "/key_" ^ key_name
+
+(* create a key-error permission name that is never in the session *)
+let get_keyERR_permission_name permission err =
+ permission ^ "/keyERR_" ^ err
+
+let permission_of_action ?args ~keys _action =
+ (* all permissions are in lowercase, see gen_rbac.writer_ *)
+ let action = (String.lowercase _action) in
+ if (List.length keys) < 1
+ then (* most actions do not use rbac-guarded map keys in the arguments
*)
+ action
+
+ else (* only actions with rbac-guarded map keys fall here *)
+ match args with
+ |None -> begin (* this should never happen *)
+ debug "DENYING access: no args for keyed-action %s"
action;
+ get_keyERR_permission_name action "DENY_NOARGS" (* will
always deny *)
+ end
+ |Some (arg_keys,arg_values) ->
+ if (List.length arg_keys) <> (List.length arg_values)
+ then begin (* this should never happen *)
+ debug "DENYING access: arg_keys and arg_values lengths
don't match: arg_keys=[%s], arg_values=[%s]"
+ ((List.fold_left (fun ss s->ss^s^",") ""
arg_keys))
+ ((List.fold_left (fun ss s->ss^(Xml.to_string
s)^",") "" arg_values))
+ ;
+ get_keyERR_permission_name action "DENY_WRGLEN" (* will
always deny *)
+ end
+ else (* keys and values have the same length *)
+ let rec get_permission_name_of_keys arg_keys arg_values =
+ match arg_keys,arg_values with
+ |[],[]|_,[]|[],_-> (* this should never happen *)
+ begin
+ debug "DENYING access: no 'key'
argument in the action %s" action;
+ get_keyERR_permission_name action
"DENY_NOKEY" (* deny by default *)
+ end
+ |k::ks,v::vs->
+ if k<>"key" (* "key" is defined in
datamodel_utils.ml *)
+ then
+ (get_permission_name_of_keys ks vs)
+ else (* found "key" in args *)
+ match v with
+ | Xml.Element("value",_,(Xml.PCData
key_name_in_args)::[]) ->
+ begin
+ (*debug "key_name_in_args=%s,
keys=[%s]" key_name_in_args ((List.fold_left (fun ss s->ss^s^",") "" keys)) ;*)
+ try
+ let key_name =
+ List.find
+ (fun key_name ->
+ if
Stringext.String.endswith "*" key_name
+ then begin (* resolve
wildcards at the end *)
+
Stringext.String.startswith
+
(String.sub key_name 0 ((String.length key_name) - 1))
+
key_name_in_args
+ end
+ else (* no wildcards
to resolve *)
+ key_name =
key_name_in_args
+ )
+ keys
+ in
+ get_key_permission_name
action (String.lowercase key_name)
+ with Not_found -> (* expected,
key_in_args is not rbac-protected *)
+ action
+ end
+ |_ -> begin (* this should never happen
*)
+ debug "DENYING access: wrong
XML value [%s] in the 'key' argument of action %s" (Xml.to_string v) action;
+ get_keyERR_permission_name
action "DENY_NOVALUE"
+ end
+ in
+ get_permission_name_of_keys arg_keys arg_values
+
+
let is_permission_in_session ~session_id ~permission ~session =
let find_linear elem set = List.exists (fun e -> e = elem) set in
let find_log elem set = Permission_set.mem elem set in
@@ -137,10 +211,9 @@
(* Execute fn if rbac access is allowed for action, otherwise fails. *)
let nofn = fun () -> ()
-let check ?(extra_dmsg="") ?(extra_msg="") ?args ~__context ~fn session_id
action =
+let check ?(extra_dmsg="") ?(extra_msg="") ?args ?(keys=[]) ~__context ~fn
session_id action =
- (* all permissions are in lowercase, see gen_rbac.writer_ *)
- let permission = String.lowercase action in
+ let permission = permission_of_action action ?args ~keys in
if (is_access_allowed ~__context ~session_id ~permission)
then (* allow access to action *)
diff -r c2e75d891d3d -r c9fd958b25ee ocaml/idl/ocaml_backend/rbac_audit.ml
--- a/ocaml/idl/ocaml_backend/rbac_audit.ml Fri Nov 13 14:01:05 2009 +0000
+++ b/ocaml/idl/ocaml_backend/rbac_audit.ml Thu Nov 26 17:08:59 2009 +0000
@@ -299,7 +299,7 @@
with e -> (* never bubble up the error here *)
D.debug "ignoring %s" (ExnHelper.string_of_exn e)
-let sexpr_of __context session_id allowed_denied ok_error result_error ?args
action =
+let sexpr_of __context session_id allowed_denied ok_error result_error ?args
action permission =
let result_error =
if result_error = "" then result_error else ":"^result_error
in
@@ -312,7 +312,7 @@
SExpr.String (ok_error ^ result_error)::
SExpr.String (call_type_of action)::
(*SExpr.String (Helper_hostname.get_hostname ())::*)
- SExpr.String action::
+ SExpr.String permission::
(SExpr.Node (sexpr_of_parameters action args))::
[]
)
@@ -321,11 +321,11 @@
let fn_append_to_master_audit_log = ref None
-let audit_line_of __context session_id allowed_denied ok_error result_error
action ?args =
+let audit_line_of __context session_id allowed_denied ok_error result_error
action permission ?args =
let _line =
(SExpr.string_of
(sexpr_of __context session_id allowed_denied
- ok_error result_error ?args action
+ ok_error result_error ?args action
permission
)
)
in
@@ -339,7 +339,7 @@
let allowed_ok ~__context ~session_id ~action ~permission ?args ?result () =
wrap (fun () ->
if has_to_audit action then
- audit_line_of __context session_id "ALLOWED" "OK" ""
action ?args
+ audit_line_of __context session_id "ALLOWED" "OK" ""
action permission ?args
)
let allowed_error ~__context ~session_id ~action ~permission ?args ?error () =
@@ -350,13 +350,13 @@
| None -> ""
| Some error -> (ExnHelper.string_of_exn error)
in
- audit_line_of __context session_id "ALLOWED" "ERROR"
error_str action ?args
+ audit_line_of __context session_id "ALLOWED" "ERROR"
error_str action permission ?args
)
let denied ~__context ~session_id ~action ~permission ?args () =
wrap (fun () ->
if has_to_audit action then
- audit_line_of __context session_id "DENIED" "" ""
action ?args
+ audit_line_of __context session_id "DENIED" "" ""
action permission ?args
)
let session_destroy ~__context ~session_id =
xen-api.hg.patch
Description: Text Data
_______________________________________________
xen-api mailing list
xen-api@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/mailman/listinfo/xen-api
|