You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@knox.apache.org by "Attila Magyar (Jira)" <ji...@apache.org> on 2022/03/01 14:30:00 UTC

[jira] [Resolved] (KNOX-2707) Virtual Group Mapping Provider

     [ https://issues.apache.org/jira/browse/KNOX-2707?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Attila Magyar resolved KNOX-2707.
---------------------------------
    Resolution: Fixed

> Virtual Group Mapping Provider
> ------------------------------
>
>                 Key: KNOX-2707
>                 URL: https://issues.apache.org/jira/browse/KNOX-2707
>             Project: Apache Knox
>          Issue Type: New Feature
>            Reporter: Attila Magyar
>            Assignee: Attila Magyar
>            Priority: Major
>          Time Spent: 6h 20m
>  Remaining Estimate: 0h
>
> Presenting a more flexible way to map principals to groups than the existing _group.principal.mapping_ mechanism in {_}CommonIdentityAssertionFilter{_}.
> See the motivations behind this at [https://cwiki.apache.org/confluence/display/KNOX/KIP-16+-+Virtual+Groups+in+Apache+Knox]
> Example:
> {code:xml}
> <provider>
>   <role>identity-assertion</role>
>   <name>Default</name>
>   <enabled>true</enabled>
>   <param>
>      <name>group.mapping.vgroup1</name>
>      <value>(or (username 'tom') (member 'analyst'))</value>
>   </param>
> </provider>
> {code}
> We add user 'tom' or any other users who are member of the 'analyst' group to virtual group vgroup1.
> General usage:
> {code:java}
> <name>group.mapping.VIRTUAL-GROUP-NAME</name>
> <value>PREDICATE</value>
> {code}
> If the *PREDICATE* evaluates to true the user is added to {*}VIRTUAL-GROUP-NAME{*}.
> There can be any number of virtual group mappings within the provider.
> h2. Language Syntax
> The predicate uses a parenthesized prefix notation language, similar to Lisp.
>  * Everything in the language is either an atom or a list
>  * A list is written with its elements separated by whitespace, and surrounded by parentheses, like (or true false false)
>  * Lists can be nested to arbitrary level, like (or true (and false true))
>  * An atom is either a boolean (true/false), a string, a number or a symbol (which denotes a functions name or a variable name).
>  * Strings are single-quoted which makes easier to embed the language into XML or JSON.
>  * There is a one to one mapping between the textual syntax and the parser generated AST. You can always infer the exact AST just by looking at the code (homoiconicity).
> From this code the parses generates the following AST:
> h5. Textual code:
> {code:java}
> (or true (and false true))
> {code}
> h5. AST:
> {code:java}
> [or, true, [and, false, true]]
> {code}
> This has exactly the same structure, but everything is converted to internal Java representations. Lists are ArrayLists, booleans are java.lang.Booleans, etc.
> h2. Evaluation rules
>  * A literal atom evaluates to itself ('astring', 123).
>  * If an atom is a symbol (like {_}or{_}, {_}username{_}, {_}true{_}, {_}false{_}) then the atom is looked up in a dictionary.
>  * The head of a list is the name of the function we're about to call. The rest are the parameters.
>  * Before calling the function (1st item of the list) we evaluate the rest of the list (recursively).
> {code:java}
> (=   0  (size groups))
>  ^   ^   ^^^^^^^^^^^^
>  |   |         |
> func param1  param2
> {code}
>  # Get the head of the list (=), see if it's an existing function
>  # Evaluate param1, and param2, recusrivly
>  # Call the function (=)
> h2. Special forms
> For some expressions the evaluation rule is slightly different. These are called special forms. These are the _or_ and the {_}and{_} operators. To support short-circuit evaluation, the parameters are not evaluated at the call site but by the definition itself.
> {code:java}
> (or   true  (and true false))
>  ^     ^         ^
>  |     |         |
> func param1   param2
> {code}
> First we call the operator (or) then we let the operator decide which part to evaluate. In the above example the _or_ will stop evaluating the expression after it sees that the first argument is {_}true{_}.
> These few evaluation rules (general + special forms) cover the semantics of the whole language.
> h2. Supported functions
> h3. or
> Evaluates true if one or more of its operands is true. Supports short-circuit evaluation and variable number of arguments.
> Number of arguments: 1..N
> {code:java}
> (or bool1 bool2 ... boolN)
> {code}
> h5. Example
> {code:java}
> (or true false true)
> {code}
> h3. and
> Evaluates true if all of its operands are true. Supports short-circuit evaluation and variable number of arguments.
> Number of arguments: 1..N
> {code:java}
> (and bool1 bool2 ... boolN)
> {code}
> h5. Example
> {code:java}
> (and true false true)
> {code}
> h3. not
> Negates the operand.
> Number of arguments: 1
> {code:java}
> (not aBool)
> {code}
> h5. Example
> {code:java}
> (not true)
> {code}
> h3. =
> Evaluates true if the two operands are equal.
> Number of arguments: 2
> {code:java}
> (= op1 op2)
> {code}
> h5. Example
> {code:java}
> (= 'apple' 'orange')
> {code}
> h3. !=
> Evaluates true if the two operands are not equal.
> Number of arguments: 2
> {code:java}
> (!= op1 op2)
> {code}
> h5. Example
> {code:java}
> (!= 'apple' 'orange')
> {code}
> h3. member
> Evaluates true if the current user is a member of the given group
> Number of arguments: 1
> {code:java}
> (member aString)
> {code}
> h5. Example
> {code:java}
> (member 'analyst')
> {code}
> h3. username
> Evaluates true if the current user has the given username
> Number of arguments: 1
> {code:java}
> (username aString)
> {code}
> h5. Example
> {code:java}
> (username 'admin')
> {code}
> This is a shorter version of (= username 'admin')
> h3. size
> Gets the size of a list
> Number of arguments: 1
> {code:java}
> (size alist)
> {code}
> h5. Example
> {code:java}
> (size groups)
> {code}
> h3. empty
> Evaluates to true if the given list is empty
> Number of arguments: 1
> {code:java}
> (empty alist)
> {code}
> h5. Example
> {code:java}
> (empty groups)
> {code}
> h3. match
> Evaluates true if the given string matches to the given regexp. Or any items of the given list matches the given regexp.
> Number of arguments: 2
> {code:java}
> (match aString aRegExpString)
> (match aList aRegExpString)
> {code}
> h5. Example
> {code:java}
> (match username 'tom|sam')
> {code}
> This function can also take a list as a first argument. In this case it will return true if the regexp matches to *any of the items* in the list.
> {code:java}
> (match groups 'analyst|scientist')
> {code}
> This returns true if the user is either in the 'analyst' group or in the 'scientist' group.
> h3. request-header
> Returns the value of the specified request header as a String. If the given key doesn't exist empty string is returned.
> Number of arguments: 1
> {code:java}
> (request-header aString)
> {code}
> h5. Example
> {code:java}
> (request-header 'User-Agent')
> {code}
> h3. request-attribute
> Returns the value of the specified request attribute as a String.  If the given key doesn't exist empty string is returned.
> Number of arguments: 1
> {code:java}
> (request-attribute aString)
> {code}
> h5. Example
> {code:java}
> (request-attribute 'sourceRequestUrl')
> {code}
> h3. session
> Returns the value of the specified session attribute as a String.  If the given key doesn't exist empty string is returned.
> Number of arguments: 1
> {code:java}
> (session aString)
> {code}
> h5. Example
> {code:java}
> (session 'subject.userRoles')
> {code}
> h3. lowercase
> Converts the given string to lowercase.
> Number of arguments: 1
> {code:java}
> (lowercase aString)
> {code}
> h5. Example
> {code:java}
> (lowercase 'KNOX')
> {code}
> h3. uppercase
> Converts the given string to uppercase.
> Number of arguments: 1
> {code:java}
> (uppercase aString)
> {code}
> h5. Example
> {code:java}
> (uppercase 'knox')
> {code}
> h2. Constants
> The following constants are populated automatically from the current security context.
> h3. username
> The username (principal) of the current user, derived from javax.security.auth.Subject.
> h3. groups
> The groups of the current user (LDAP or OS level), derived from {_}subject.getPrincipals(GroupPrincipal.class{_}.
> h2. Examples
> {code:java}
> (or 
>   (and
>     (member 'admin')
>     (member 'datalake'))
>     (or
>         (username 'lmccay')
>         (username 'pzampino')))
> {code}
> 1. Returns true if the user is either 'lmccay' or 'pzampino'
> 2. Returns true if the user is both in the 'admin' and the 'datalake' group.
> 3. Returns false otherwise.
> A shorter version of this is
> {code:java}
> (or 
>   (and (member 'admin') (member 'datalake'))
>   (match username 'lmccay|pzampino'))
> {code}
> h2. Any group
> If we want to put a user into a virtual group if they are a member of ANY groups, we can use either of the following predicates.
> {code:java}
> (!= (size groups) 0)
> {code}
> {code:java}
> (not (empty groups))
> {code}
> {code:java}
> (match groups '.*')
> {code}
> h3. Same group as the username
> If we wan to check if the user is in the same group as their username we can use the following predicate
> {code:java}
> (member username)
> {code}
> h2. Implementation notes
> CommonIdentityAssertionFilter parses all the group mapping predicates at deployment time and caches the ASTs. In the doFilter we only evaluate the ASTs without parsing the text over and over.



--
This message was sent by Atlassian Jira
(v8.20.1#820001)