[openstack-dev] [keystone] Domain-namespaced user attributes in SAML assertions from Keystone IdPs

Lance Bragstad lbragstad at gmail.com
Thu Sep 27 13:16:38 UTC 2018


Using the domain name + group name pairing also allows for things like:

<ns0:Attribute Name="openstack_groups">
    <ns0:AttributeValue xsi:type="xs:string">JSON:{"group_name": "C",
"domain_name": "X"}</ns0:AttributeValue>
    <ns0:AttributeValue xsi:type="xs:string">JSON:{"group_name": "C",
"domain_name": "Y"}</ns0:AttributeValue>
</ns0:Attribute>

To showcase how we solve the ambiguity in group names by namespacing them
with domains.

On Thu, Sep 27, 2018 at 3:11 AM Colleen Murphy <colleen at gazlene.net> wrote:

>
>
> On Thu, Sep 27, 2018, at 5:09 AM, vishakha agarwal wrote:
> > > From : Colleen Murphy <colleen at gazlene.net>
> > > To : <openstack-dev at lists.openstack.org>
> > > Date : Tue, 25 Sep 2018 18:33:30 +0900
> > > Subject : Re: [openstack-dev] [keystone] Domain-namespaced user
> attributes in SAML assertions from Keystone IdPs
> > > ============ Forwarded message ============
> > >  > On Mon, Sep 24, 2018, at 8:40 PM, John Dennis wrote:
> > >  > > On 9/24/18 8:00 AM, Colleen Murphy wrote:
> > >  > > > This is in regard to https://launchpad.net/bugs/1641625 and
> the proposed patch https://review.openstack.org/588211 for it. Thanks
> Vishakha for getting the ball rolling.
> > >  > > >
> > >  > > > tl;dr: Keystone as an IdP should support sending
> non-strings/lists-of-strings as user attribute values, specifically lists
> of keystone groups, here's how that might happen.
> > >  > > >
> > >  > > > Problem statement:
> > >  > > >
> > >  > > > When keystone is set up as a service provider with an external
> non-keystone identity provider, it is common to configure the mapping rules
> to accept a list of group names from the IdP and map them to some property
> of a local keystone user, usually also a keystone group name. When keystone
> acts as the IdP, it's not currently possible to send a group name as a user
> property in the assertion. There are a few problems:
> > >  > > >
> > >  > > >      1. We haven't added any openstack_groups key in the
> creation of the SAML assertion (
> http://git.openstack.org/cgit/openstack/keystone/tree/keystone/federation/idp.py?h=14.0.0#n164
> ).
> > >  > > >      2. If we did, this would not be enough. Unlike other IdPs,
> in keystone there can be multiple groups with the same name, namespaced by
> domain. So it's not enough for the SAML AttributeStatement to contain a
> semi-colon-separated list of group names, since a user could theoretically
> be a member of two or more groups with the same name.
> > >  > > >     * Why can't we just send group IDs, which are unique?
> Because two different keystones are not going to have independent groups
> with the same UUID, so we cannot possibly map an ID of a group from
> keystone A to the ID of a different group in keystone B. We could map the
> ID of the group in in A to the name of a group in B but then operators need
> to create groups with UUIDs as names which is a little awkward for both the
> operator and the user who now is a member of groups with nondescriptive
> names.
> > >  > > >      3. If we then were able to encode a complex type like a
> group dict in a SAML assertion, we'd have to deal with it on the service
> provider side by being able to parse such an environment variable from the
> Apache headers.
> > >  > > >      4. The current mapping rules engine uses basic python
> string formatting to translate remote key-value pairs to local rules. We
> would need to change the mapping API to work with values more complex than
> strings and lists of strings.
> > >  > > >
> > >  > > > Possible solution:
> > >  > > >
> > >  > > > Vishakha's patch (https://review.openstack.org/588211) starts
> to solve (1) but it doesn't go far enough to solve (2-4). What we talked
> about at the PTG was:
> > >  > > >
> > >  > > >      2. Encode the group+domain as a string, for example by
> using the dict string repr or a string representation of some custom XML
> and maybe base64 encoding it.
> > >  > > >          * It's not totally clear whether the AttributeValue
> class of the pysaml2 library supports any data types outside of the
> xmlns:xs namespace or whether nested XML is an option, so encoding the
> whole thing as an xs:string seems like the simplest solution.
> > >  > > >      3. The SP will have to be aware that openstack_groups is a
> special key that needs the encoding reversed.
> > >  > > >          * I wrote down "MultiDict" in my notes but I don't
> recall exactly what format the environment variable would take that would
> make a MultiDict make sense here, in any case I think encoding the whole
> thing as a string eliminates the need for this.
> > >  > > >      4. We didn't talk about the mapping API, but here's what I
> think. If we were just talking about group names, the mapping API today
> would work like this (slight oversimplification for brevity):
> > >  > > >
> > >  > > > Given a list of openstack_groups like ["A", "B", "C"], it would
> work like this:
> > >  > > >
> > >  > > > [
> > >  > > >    {
> > >  > > >      "local":
> > >  > > >      [
> > >  > > >        {
> > >  > > >          "group":
> > >  > > >          {
> > >  > > >            "name": "{0}",
> > >  > > >            "domain":
> > >  > > >            {
> > >  > > >              "name": "federated_domain"
> > >  > > >            }
> > >  > > >          }
> > >  > > >        }
> > >  > > >      ], "remote":
> > >  > > >      [
> > >  > > >        {
> > >  > > >          "type": "openstack_groups"
> > >  > > >        }
> > >  > > >      ]
> > >  > > >    }
> > >  > > > ]
> > >  > > > (paste in case the spacing makes this unreadable:
> http://paste.openstack.org/show/730623/ )
> > >  > > >
> > >  > > > But now, we no longer have a list of strings but something more
> like [{"name": "A", "domain_name": "Default"} {"name": "B", "domain_name":
> "Default", "name": "A", "domain_name": "domainB"}]. Since {0} isn't a
> string, this example doesn't really work. Instead, let's assume that in
> step (3) we converted the decoded AttributeValue text to an object. Then
> the mapping could look more like this:
> > >  > > >
> > >  > > > [
> > >  > > >    {
> > >  > > >      "local":
> > >  > > >      [
> > >  > > >        {
> > >  > > >          "group":
> > >  > > >          {
> > >  > > >            "name": "{0.name}",
> > >  > > >            "domain":
> > >  > > >            {
> > >  > > >              "name": "{0.domain_name}"
> > >  > > >            }
> > >  > > >          }
> > >  > > >        }
> > >  > > >      ], "remote":
> > >  > > >      [
> > >  > > >        {
> > >  > > >          "type": "openstack_groups"
> > >  > > >        }
> > >  > > >      ]
> > >  > > >    }
> > >  > > > ]
> > >  > > > (paste: http://paste.openstack.org/show/730622/ )
> > >  > > >
> > >  > > > Alternatively, we could forget about the namespacing problem
> and simply say we only pass group names in the assertion, and if you have
> ambiguous group names you're on your own. We could also try to support
> both, e.g. have an openstack_groups mean a list of group names for simpler
> use cases, and openstack_groups_unique mean the list of encoded
> group+domain strings for advanced use cases.
> > >  > > >
> > >  > > > Finally, whatever we decide for groups we should also apply to
> openstack_roles which currently only supports global roles and not
> domain-specific roles.
> > >  > > >
> > >  > > > (It's also worth noting, for clarity, that the samlize function
> does handle namespaced projects, but this is because it's retrieving the
> project from the token and therefore there is only ever one project and one
> project domain so there is no ambiguity.)
> > >  > > >
> > >  > >
> > >  > > A few thoughts to help focus the discussion:
> > >  > >
> > >  > > * Namespacing is critical, no design should be permitted which
> allows
> > >  > > for ambiguous names. Ambiguous names are a security issue and can
> be
> > >  > > used by an attacker. The SAML designers recognized the importance
> to
> > >  > > disambiguate names. In SAML names are conveyed inside a
> NameIdentifier
> > >  > > element which (optionally) includes "name qualifier" attributes
> which in
> > >  > > SAML lingo is a namespace name.
> > >  > >
> > >  > > * SAML does not define the format of an attribute value. You can
> use
> > >  > > anything you want as long as it can be expressed in valid XML as
> long as
> > >  > > the cooperating parties know how to interpret the XML content. But
> > >  > > herein lies the problem. Very few SAML implementations know how to
> > >  > > consume an attribute value other than a string. In the real world,
> > >  > > despite what the SAML spec says is permitted is the constraint
> attribute
> > >  > > values is a string.
> > >  > >
> > >  > > * I haven't looked at the pysaml implementation but I'd be
> surprised if
> > >  > > it treated attribute values as anything other than a string. In
> theory
> > >  > > it could take any Python object (or JSON) and serialize it into
> XML but
> > >  > > you would still be stuck with the receiver being unable to parse
> the
> > >  > > attribute value (see above point).
> > >  > >
> > >  > > * You can encode complex data in an attribute value while only
> using a
> > >  > > simple string. The only requirement is the relying party knowing
> how to
> > >  > > interpret the string value. Note, this is distinctly different
> than
> > >  > > using non-string attribute values because of who is responsible
> for
> > >  > > parsing the value. If you use a non-string attribute value the
> SAML
> > >  > > library need to know how to parse it, none or very few will know
> how to
> > >  > > process that element. But if it's a string value the SAML library
> will
> > >  > > happily pass that string back up to the application who can then
> > >  > > interpret it. The easiest way to embed complex data in a string
> is with
> > >  > > JSON, we do it all the time, all over the place in OpenStack.
> [1][2]
> > >  > >
> > >  > > So my suggestion would be to give the attribute a meaningful name.
> > >  > > Define a JSON schema for the data and then let the upper layers
> decode
> > >  > > the JSON and operate on it. This is no different than any other
> SAML
> > >  > > attribute passed as a string, the receive MUST know how to
> interpret the
> > >  > > string value.
> > >  > >
> > >  > > [1] We already pass complex data in a SAML attribute string
> value. We
> > >  > > permit a comma separated list of group names to appear in the
> 'groups'
> > >  > > mapping rule (although I don't think this feature is documented
> in our
> > >  > > mapping rules documentation). The receiver (our mapping engine)
> has
> > >  > > hard-coded logic to look for a list of names.
> > >  > >
> > >  > > [2] We might want to prepend a format specifier to string
> containing
> > >  > > complex data, e.g. "JSON:{json object}". Our parser could then
> look for
> > >  > > a leading format tag and if if finds one strip it off and pass
> the rest
> > >  > > of the string into the proper parser.
> > >  > >
> > >  > > --
> > >  > > John Dennis
> > >  > >
> > >  >
> > >  > Thanks for this response, John. I think serializing to JSON and
> prepending a format specifier makes sense.
> > >  >
> > >  > Colleen
> >
> > Thanks for the response Colleen, John. After reading the above mails,
> > what I understood is to pass list of group names specific to a
> > keystone in to a JSON schema prepending a tag. e.g - groups_names =
> > "JSON:{groups_names:[a,b,c]}". IN SAML assertion Attribute_name can be
> > openstack_groups and Attribute_value will be
> > "JSON:{groups_names:[a,b,c]}".
>
> Not quite. The important part is that the group name needs to be tied to a
> domain name. The attribute statement generator can already produce a list
> of attributes of one type by just adding new attribute values to the
> attribute, see for example openstack_roles[1]. The attribute value then
> needs to be a single group + domain JSON blob.
>
> As an example, if a user is a member of group A in domain X and also a
> member of group B in domain Y, the attribute could look like this:
>
> <ns0:Attribute Name="openstack_groups">
>     <ns0:AttributeValue xsi:type="xs:string">JSON:{"group_name": "A",
> "domain_name": "X"}</ns0:AttributeValue>
>     <ns0:AttributeValue xsi:type="xs:string">JSON:{"group_name": "B",
> "domain_name": "Y"}</ns0:AttributeValue>
> </ns0:Attribute>
>
> [1]
> http://git.openstack.org/cgit/openstack/keystone/tree/keystone/federation/idp.py?h=14.0.0#n177
>
> Colleen
>
> __________________________________________________________________________
> OpenStack Development Mailing List (not for usage questions)
> Unsubscribe: OpenStack-dev-request at lists.openstack.org?subject:unsubscribe
> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openstack.org/pipermail/openstack-dev/attachments/20180927/b156c5ad/attachment-0001.html>


More information about the OpenStack-dev mailing list