Open windley opened 11 years ago
I added a few examples relevant to this to the XDI Local Messenger tool:
Giving $get access to all my friends: http://xdi2.projectdanube.org/XDILocalMessenger?category=2&sample=3
Allowing only certain events: http://xdi2.projectdanube.org/XDILocalMessenger?category=2&sample=4
I’ve come up with a small EBNF grammar below for the language. I have not included any condition expression on event domains, types or attributes because I thought those are handled quite effectively by eventexes in the rule today. My understanding is that, when an event hits a personal cloud, unless there is a rule which is supposed to handle that event, it will be ignored. There may be very good reasons to include those filters in this language too. So, I can put them in if required. So, I have only included filter expressions which are not easily handled by an eventex.
policy : cloud CLOUD_IDENTIFER (allows|blocks) events [on channel CHANNEL_IDENTIFIER] [if ‘(‘ condition ‘)’] ‘;’ | cloud CLOUD_IDENTIFER owns channel CHANNEL_IDENTIFIER ‘;’ | policy ; condition : NOT ‘(‘ condition ‘)’ | ‘(‘ condition ‘)’ AND ‘(‘ condition ‘)’ | ‘(‘ condition ‘)’ OR ‘(‘ condition ‘)’ | from cloud (INUMBER|INAME) equals CLOUD_IDENTIFER | from cloud (INUMBER|INAME) in ‘{‘ CLOUD_IDENTIFER_LIST ‘}’ | channel relationship equals CHANNEL_RELATIONSHIP_IDENTIFIER ;
CHANNEL_RELATIONSHIPIDENTIFIER : ([A-Za-z-])+ ; CLOUD_IDENTIFIER : iname | inumber ; CLOUD_IDENTIFER_LIST : (CLOUD_IDENTIFIER)+ ; CHANNEL_IDENTIFER : ([A-Za-z0-9])+ ;
comments below:
policy : cloud CLOUD_IDENTIFER (allows|blocks) events
[on channel CHANNEL_IDENTIFIER] [if ‘(‘ condition ‘)’] ‘;’ |
cloud CLOUD_IDENTIFER owns channel CHANNEL_IDENTIFIER ‘;’ |
policy
;
I think this could be rewritten to put the separator in the recursion and be clearer:
policy : cloud CLOUD_IDENTIFER (allows|blocks) events
[on channel CHANNEL_IDENTIFIER] [if ‘(‘ condition ‘)’] |
cloud CLOUD_IDENTIFER owns channel CHANNEL_IDENTIFIER |
policy [';' policy]*
;
We need to add a way to specify primitive events in the policy language because one of the major uses is to filter specific events on a per channel basis, not merely events in general. Really a simple syntax statement that allows for a domain and type separated by a ':' would be sufficient.
Any channel allows cloudos:{notification|subscription|deletion} events if channel relationship equals +friend We need to allow wild cards and choice in event specifications.
condition : NOT ‘(‘ condition ‘)’ |
‘(‘ condition ‘)’ AND ‘(‘ condition ‘)’ |
‘(‘ condition ‘)’ OR ‘(‘ condition ‘)’ |
from cloud (INUMBER|INAME) equals CLOUD_IDENTIFER |
from cloud (INUMBER|INAME) in ‘{‘ CLOUD_IDENTIFER_LIST ‘}’ |
channel relationship equals CHANNEL_RELATIONSHIP_IDENTIFIER ;
I think this should be rewritten to not require so much explicit parenthesezation. For example, I want to write
channel relationship equals foo and cloud inumber equals !4444
rather than
(channel relationship equals 'foo') and (cloud inumber equals !4444)
Is 'equals' the right relation or should we use 'is'? Not sure.
Also, we might not want to specify that it's a iname or inumber, we may want to be more general in some cases:
cloud identifier equals !4444
and then take either an iname or inumber.
CHANNEL_RELATIONSHIP_IDENTIFIER : ([A-Za-z_-])+ ;
This looks fine, but I'm wondering whether we ought to allow namespacing (two level hierarchy of relationships). Namespaces cover a multitude of sins. So, something like
CHANNEL_RELATIONSHIP_IDENTIFIER : ([A-Za-z_-])+':'([A-Za-z_-])+ ;
It may be helpful to specify these in the BNF separately. Should these be XDI compatible as well so we can reference a XDI specified relationship? +friend, for example? Should relationships always be in the dictionary?
We also need to allow for specifying choice here. {+friend|+puppy}
CLOUD_IDENTIFIER : iname | inumber ;
I presume you're referring to standard iname and inumber syntax here.
CLOUD_IDENTIFER_LIST : (CLOUD_IDENTIFIER)+ ;
I think the delimiters ought to be here rather than in the condition statement and we ought to use [] rather than {} so that list syntax is compatible with JSON, etc.
CHANNEL_IDENTIFER : ([A-Za-z0-9])+ ;
Add !, -, and _. We want these to be inumber compatible.
policy : cloud identifier CLOUD_IDENTIFER (allows|blocks) EVENT_FILTER events on channel CHANNEL_IDENTIFIER [if ‘(‘ condition ‘)’] | cloud identifier CLOUD_IDENTIFER owns channel CHANNEL_IDENTIFIER | policy [';' policy]* ;
condition : not condition_seg | condition_seg and condition_seg | condition_seg or condition_seg ;
condition_seg : cloud identifier is CLOUD_IDENTIFER | cloud identifier is in CLOUD_IDENTIFER_LIST | channel relationship is CHANNEL_RELATIONSHIP_IDENTIFIER | channel relationship is in CHANNEL_RELATIONSHIP_IDENTIFIER_LIST;
CHANNEL_RELATIONSHIP_IDENTIFIER : ([A-Za-z+])+;
CHANNEL_RELATIONSHIP_IDENTIFIER_LIST : '[' CHANNEL_RELATIONSHIP_IDENTIFIER [ ',' CHANNEL_RELATIONSHIP_IDENTIFIER ]* ']' ; CLOUD_IDENTIFIER : '*' | INAME| INUMBER ;
CLOUD_IDENTIFER_LIST : '[' CLOUD_IDENTIFIER [',' CLOUD_IDENTIFIER]* ']' ; CHANNELIDENTIFER : '*' | ([A-Za-z0-9-!])+ ;
EVENT_FILTER : '*' | EVENT_DOMAIN ':' '[' EVENT_TYPE_LIST ']' ;
EVENT_TYPE_LIST : EVENT_TYPE ['|' EVENT_TYPE]* ; EVENTDOMAIN : ([A-Za-z+])+ ; EVENTTYPE : ([A-Za-z+])+ ; INAME : [=@]INAME_SEG INAME_SEG : ([A-Za-z+0-9])+ ['' INAMESEG ] INUMBER : '=!' INUMBER_SEG INUMBER_SEG : ([A-Za-z+_0-9])+ ['!' INUMBER_SEG ]*
reg. hierarchy of channel relationship, in XDI a hierarchy can be specified as "+neustar+colleague" or "+friend+cousin". So, "CHANNEL_RELATIONSHIP_IDENTIFIER : ([A-Za-z+])+" should be able to handle that.
reg. INAME and INUMBER syntax, these will be treated as strings for comparison. So, as long as valid inames and inumbers are not rejected by the syntax, then for the purposes of this language it should be ok.
This looks good. Let's get it in ANTLR and see how it parses! :)
grammar PersonalChannelPolicy; tokens { cloud = 'cloud'; identifier = 'identifier'; allows = 'allows'; blocks = 'blocks'; events = 'events'; on = 'on'; channel = 'channel'; owns = 'owns'; not = 'not'; and = 'and'; or = 'or'; relationship = 'relationship'; is = 'is'; in = 'in'; NEWLINE = '\n';
}
policy : policy_stmt [NEWLINE policy_stmt]* ;
policy_stmt : cloud identifier CLOUD_IDENTIFIER (allows|blocks) EVENT_FILTER events on channel CHANNEL_IDENTIFER [if ‘(‘ condition ‘)’] | cloud identifier CLOUD_IDENTIFIER owns channel CHANNEL_IDENTIFER ';' ; condition : not condition_seg | condition_seg and condition_seg | condition_seg or condition_seg ;
condition_seg : cloud identifier is CLOUD_IDENTIFIER | cloud identifier is in CLOUD_IDENTIFER_LIST | channel relationship is CHANNEL_RELATIONSHIP_IDENTIFIER | channel relationship is in CHANNEL_RELATIONSHIP_IDENTIFIER_LIST;
UPPERCASE_LETTERS
: 'A'..'Z';
LOWERCASE_LETTERS
: 'a'..'z';
DIGIT :
'0'..'9';
PLUS : '+';
BANG : '!'; EQUAL : '='; AT : '@'; UNDERSCORE : '_'; HYPHEN : '-';
CHANNEL_RELATIONSHIP_IDENTIFIER : ([UPPERCASE_LETTERS LOWERCASE_LETTERS PLUS])+;
CHANNEL_RELATIONSHIP_IDENTIFIER_LIST : '[' CHANNEL_RELATIONSHIP_IDENTIFIER [ ',' CHANNEL_RELATIONSHIP_IDENTIFIER ]* ']' ; CLOUD_IDENTIFIER : '*' | INAME| INUMBER ;
CLOUD_IDENTIFER_LIST : '[' CLOUD_IDENTIFIER [',' CLOUD_IDENTIFIER]* ']' ; CHANNEL_IDENTIFER : '*' | ([UPPERCASE_LETTERS LOWERCASE_LETTERS HYPHEN UNDERSCORE BANG])+ ;
EVENT_FILTER : '*' | EVENT_DOMAIN ':' '[' EVENT_TYPE_LIST ']' ;
EVENT_TYPE_LIST : EVENT_TYPE ['|' EVENT_TYPE]* ; EVENT_DOMAIN : ([UPPERCASE_LETTERS LOWERCASE_LETTERS PLUS UNDERSCORE])+ ; EVENT_TYPE : ([UPPERCASE_LETTERS LOWERCASE_LETTERS PLUS UNDERSCORE])+ ; INAME : [EQUAL AT]INAME_SEG ; INAME_SEG : ([UPPERCASE_LETTERS LOWERCASELETTERS PLUS UNDERSCORE DIGIT])+ ['' INAMESEG ]; INUMBER : '=!' INUMBER_SEG ; INUMBER_SEG : ([UPPERCASE_LETTERS LOWERCASE_LETTERS DIGIT])+ ['!' INUMBER_SEG ]*;
ANTLR grammar that compiles - grammar PersonalChannelPolicy;
/*------------------------------------------------------------------
policy : policy_expr (NEWLINE policy_expr)* EOF;
policy_expr : policy_stmt ';' ; policy_stmt : CLOUD IDENTIFIER cloud_id (ALLOWS|BLOCKS) event_filter EVENTS ON CHANNEL channel_id IF condition | CHANNEL channel_id BELONGS_TO CLOUD IDENTIFIER cloud_id ;
condition : FROM CLOUD IDENTIFIER IS cloud_id | FROM CLOUD IDENTIFIER IS NOT cloud_id FROM CLOUD IDENTIFIER IS IN cloud_id_list | FROM CLOUD IDENTIFIER IS NOT IN cloud_id_list | CHANNEL RELATIONSHIP IS channel_relationship_id | CHANNEL RELATIONSHIP IS NOT channel_relationship_id | CHANNEL RELATIONSHIP IS IN channel_relationship_id_list | CHANNEL RELATIONSHIP IS NOT IN channel_relationship_id_list;
cloud_id : ALL | iname | inumber ; channel_relationship_id : (UPPERCASE_LETTERS|LOWERCASE_LETTERS|PLUS)+;
channel_relationship_id_list : '[' channel_relationship_id ( ',' channel_relationship_id )* ']' ;
cloud_id_list : '[' cloud_id (',' (iname|inumber))* ']' ;
channel_id : ALL | (UPPERCASE_LETTERS|LOWERCASE_LETTERS|HYPHEN|UNDERSCORE|BANG)+;
event_type_list : '[' event_type ('|' event_type)* ']'; event_domain : (UPPERCASE_LETTERS|LOWERCASE_LETTERS|PLUS|UNDERSCORE)+ ; event_type : (UPPERCASE_LETTERS|LOWERCASE_LETTERS|PLUS|UNDERSCORE)+ ; event_filter : ALL | event_expr; event_expr : event_domain ':' event_type_list; iname : (EQUAL|AT) inameseg ; inameseg : (UPPERCASE_LETTERS|LOWERCASE_LETTERS|PLUS|UNDERSCORE|DIGIT|SEPARATOR_I)+ ; inumber : '=!' inumberseg ; inumberseg : (UPPERCASE_LETTERS|LOWERCASE_LETTERS|DIGIT|BANG)+;
/*------------------------------------------------------------------
UPPERCASE_LETTERS
: 'A'..'Z';
LOWERCASE_LETTERS
: 'a'..'z';
DIGIT :
'0'..'9';
PLUS : '+';
BANG : '!'; EQUAL : '='; AT : '@'; UNDERSCORE : '_'; HYPHEN : '-';
CLOUD : 'cloud'; IDENTIFIER : 'identifier'; ALLOWS : 'allows'; BLOCKS : 'blocks'; EVENTS : 'events'; ON : 'on'; CHANNEL : 'channel'; OWNS : 'owns'; NOT : 'not'; AND : 'and'; OR : 'or'; RELATIONSHIP : 'relationship'; IS : 'is'; IN : 'in'; NEWLINE : '\n'; IF : 'if'; FROM : 'from'; BELONGS_TO : 'belongs to'; ALL : 'all'; SEPARATOR_I : '*';
Very good.
Ultimately, I think this policy language needs to be part of KRL so a KRL programmer can express policy changes based on apps getting installed, deleted, accounts being linked, subscriptions being set up, channels being created, etc.
So, what I'd love to see next if you're game is a perl-based parser module that uses ANTLR (modeled after lib/per/Kynetx/Parser.pm) and a stand-alone program (like lib/perl/bin/krl-parser) that can parse policy statements and validate them.
--phil--
Phil Windley www.windley.com @windley
My Book on Event-Based Programming: The Live Web
On Jan 11, 2013, at 9:54 AM, animeshc notifications@github.com wrote:
ANTLR grammar that compiles - grammar PersonalChannelPolicy;
/*------------------------------------------------------------------
PARSER RULES ------------------------------------------------------------------/ policy : policy_expr (NEWLINE policy_expr)* EOF;
policy_expr : policy_stmt ';' ; policy_stmt : CLOUD IDENTIFIER cloud_id (ALLOWS|BLOCKS) event_filter EVENTS ON CHANNEL channel_id IF condition | CHANNEL channel_id BELONGS_TO CLOUD IDENTIFIER cloud_id ;
condition : FROM CLOUD IDENTIFIER IS cloud_id | FROM CLOUD IDENTIFIER IS NOT cloud_id FROM CLOUD IDENTIFIER IS IN cloud_id_list | FROM CLOUD IDENTIFIER IS NOT IN cloud_id_list | CHANNEL RELATIONSHIP IS channel_relationship_id | CHANNEL RELATIONSHIP IS NOT channel_relationship_id | CHANNEL RELATIONSHIP IS IN channel_relationship_id_list | CHANNEL RELATIONSHIP IS NOT IN channel_relationship_id_list;
cloud_id : ALL | iname | inumber ; channel_relationship_id : (UPPERCASE_LETTERS|LOWERCASE_LETTERS|PLUS)+;
channel_relationship_id_list : '[' channel_relationship_id ( ',' channel_relationship_id )* ']' ;
cloud_id_list : '[' cloud_id (',' (iname|inumber))* ']' ;
channel_id : ALL | (UPPERCASE_LETTERS|LOWERCASE_LETTERS|HYPHEN|UNDERSCORE|BANG)+;
event_type_list : '[' event_type ('|' event_type)* ']'; event_domain : (UPPERCASE_LETTERS|LOWERCASE_LETTERS|PLUS|UNDERSCORE)+ ; event_type : (UPPERCASE_LETTERS|LOWERCASE_LETTERS|PLUS|UNDERSCORE)+ ; event_filter : ALL | event_expr; event_expr : event_domain ':' event_type_list; iname : (EQUAL|AT) inameseg ; inameseg : (UPPERCASE_LETTERS|LOWERCASE_LETTERS|PLUS|UNDERSCORE|DIGIT|SEPARATOR_I)+ ; inumber : '=!' inumberseg ; inumberseg : (UPPERCASE_LETTERS|LOWERCASE_LETTERS|DIGIT|BANG)+;
/*------------------------------------------------------------------
LEXER RULES ------------------------------------------------------------------/ UPPERCASE_LETTERS : 'A'..'Z'; LOWERCASE_LETTERS : 'a'..'z'; DIGIT :
'0'..'9'; PLUS : '+';
BANG : '!'; EQUAL : '='; AT : '@'; UNDERSCORE : '_'; HYPHEN : '-';
CLOUD : 'cloud'; IDENTIFIER : 'identifier'; ALLOWS : 'allows'; BLOCKS : 'blocks'; EVENTS : 'events'; ON : 'on'; CHANNEL : 'channel'; OWNS : 'owns'; NOT : 'not'; AND : 'and'; OR : 'or'; RELATIONSHIP : 'relationship'; IS : 'is'; IN : 'in'; NEWLINE : '\n'; IF : 'if'; FROM : 'from'; BELONGS_TO : 'belongs to'; ALL : 'all'; SEPARATOR_I : '*';
— Reply to this email directly or view it on GitHub.
Ok. Will start on it. BTW, the de-compilation back from XDI to this language is going to be a lot trickier than going from this one to XDI , because XDI grammar is way more complicated than this one. So, Markus had a thought - how about we store policy defined in this language as is in the personal graph as a literal and fetch it when required. One has to keep the XDI definition and this literal value in-sync, that's the overhead. What do you think ?
I think that's a reasonable compromise. Especially at this point.
--phil--
On Jan 11, 2013, at 1:49 PM, animeshc notifications@github.com wrote:
Ok. Will start on it. BTW, the de-compilation back from XDI to this language is going to be a lot trickier than going from this one to XDI , because XDI grammar is way more complicated than this one. So, Markus had a thought - how about we store policy defined in this language as is in the personal graph as a literal and fetch it when required. One has to keep the XDI definition and this literal value in-sync, that's the overhead. What do you think ?
— Reply to this email directly or view it on GitHub.
For now, this work has moved to https://github.com/kynetx/PolicyLanguage. Once that's ready, we'll need to modify KRE to use it.
As Drummond went thru his example of a link contract as an event channel policy expression, it because more clear than ever that for this to work, we need a policy expression language that compiles to XDI. Programmers aren't going to write the XDI directly. I don't know how they could. More and more I think of XDI as the assembly language of data.
Here is Drummond's example from https://wiki.oasis-open.org/xdi/XdiPolicyExpression/Discussion
This says, to the extent I understand it:
We'd probably more likely need the following kinds of policy:
Or, more likely,
Suppose this is our policy language (and it could be; I don't see any reason we could write a parser for statements like the three proceeding). We would want to compile it to the equivalent XDI statements for storage in the XDI graph of the cloud.
We would want a system that allows us to query the XDI graph and produce a data structure like this for use in filtering events on channels:
This would be cached and only flushed if the XDI server raised a system event saying that the policy graph had changed and allow the rules engine to make quick determinations whether or not the event was allowed on a specific channel.
This all seems relatively straightforward. We could have (maybe) an XDI pretty printer that turns the XDI statements that these policy statements compile to back into the policy statements. That means that the XDI graph for the cloud is the repository for the policy (if we can't get it back out in a human readable form, you would have to have another repository for the human readable statements and keep them in sync and that would be ugly).
The biggest question in my mind is where does this policy come from in the first place? Who writes it? Using what tool? The first two statements are created when the channel is created, so that's OK:
The last one is likely a global policy that the user might override or customize, but I don't know how that would work.
So, there are my thoughts for today.