Replace sphinxcontrib-*diag

As expected (and as done in lot of other OpenStack services) *blockdiag
and *seqdiag extensions stop working for us and we need to replace this
usage. Other services go directly to graphviz. Sadly sequence diagrams
doesn't look good, but there is no other alternative so far. It would be
nice if we would have mermaid allowed in global requirements, but that
tends to be a long process while we need an urgent solution (since
otherwise doc build is broken).

Change-Id: Ie5fc80d7e5506c8c426a289687acc6c7c30f5ebd
This commit is contained in:
Artem Goncharov 2024-11-19 18:02:47 +01:00
parent 15f34eff17
commit 51045cc0dd
5 changed files with 491 additions and 194 deletions

View File

@ -34,3 +34,6 @@ python3-devel [platform:rpm]
libmariadb-devel [platform:suse] libmariadb-devel [platform:suse]
openldap2-devel [platform:suse] openldap2-devel [platform:suse]
# Required for sphinx graphviz image generation
graphviz [test doc]

View File

@ -4,9 +4,7 @@
openstackdocstheme>=2.2.1 # Apache-2.0 openstackdocstheme>=2.2.1 # Apache-2.0
sphinx>=2.0.0,!=2.1.0 # BSD sphinx>=2.0.0,!=2.1.0 # BSD
sphinxcontrib-apidoc>=0.2.0 # BSD sphinxcontrib-apidoc>=0.2.0 # BSD
sphinxcontrib-seqdiag>=0.8.4 # BSD
sphinx-feature-classification>=0.3.2 # Apache-2.0 sphinx-feature-classification>=0.3.2 # Apache-2.0
sphinxcontrib-blockdiag>=1.5.5 # BSD
reno>=3.1.0 # Apache-2.0 reno>=3.1.0 # Apache-2.0
os-api-ref>=1.4.0 # Apache-2.0 os-api-ref>=1.4.0 # Apache-2.0
python-ldap>=3.0.0 # PSF python-ldap>=3.0.0 # PSF

View File

@ -117,20 +117,51 @@ process is key to being able to debug later on.
Normal keystone Normal keystone
--------------- ---------------
.. seqdiag:: .. graphviz::
:name: normal-keystone :name: normal-keystone
:alt: Diagram of keystone's normal auth flow, in which a user agent :alt: Diagram of keystone's normal auth flow, in which a user agent
authenticates and authorizes themself with keystone and obtains a authenticates and authorizes themself with keystone and obtains a
scoped token to pass to an OpenStack service. scoped token to pass to an OpenStack service.
seqdiag { digraph {
default_fontsize = 13; nodesep=1
useragent [label = "User Agent"]; keystone [label = "Keystone"]; openstack [label = "OpenStack"]; node [shape=point]
useragent -> keystone [label = "GET /v3/auth/tokens"];
keystone -> keystone [label = "Authenticate"]; // first column
keystone -> keystone [label = "Authorize"]; useragent_top [label = "User Agent" shape="box"]
useragent <- keystone [label = "Scoped token"]; useragent_bottom [label = "User Agent" shape="box"]
useragent -> openstack [label = "GET /v2.1/servers"]; // chain rows of the column
useragent_top -> u0 [arrowhead="none" style="dashed"]
u0 -> u1 -> u2 [arrowhead="none" style="bold"]
u2 -> useragent_bottom [arrowhead="none" style="dashed"]
// second column
keystone_top [label = "Keystone" shape="box"]
keystone_bottom [label = "Keystone" shape="box"]
keystone_top -> k0 [arrowhead="none" style="dashed"]
k0 -> k1 -> k2 -> k3 [arrowhead="none" style="bold"]
k3 -> keystone_bottom [arrowhead="none" style="dashed"]
openstack_top [label = "OpenStack" shape="box"]
openstack_bottom [label = "OpenStack" shape="box"]
openstack_top -> o0 -> openstack_bottom [arrowhead="none" style="dashed"]
// Order columns
useragent_top -> keystone_top -> openstack_top [style="invis"]
// Event links
u0 -> k0 [weight=0 label = "GET /v3/auth/tokens"]
k1 -> k1 [weight=0 label = "Authenticate"]
k2 -> k2 [weight=0 label = "Authorize"]
u1 -> k3 [weight=0 label = "Scoped token" dir="back"]
u2 -> o0 [weight=0 xlabel = "GET /v2.1/servers"]
// bind nodes to levels
{rank=same; useragent_top keystone_top openstack_top }
{rank=same; u0 k0 }
{rank=same; u1 k3 }
{rank=same; u2 o0 }
{rank=same; useragent_bottom keystone_bottom openstack_bottom }
} }
In a normal keystone flow, the user requests a scoped token directly from In a normal keystone flow, the user requests a scoped token directly from
@ -147,26 +178,61 @@ SAML2.0
SAML2.0 WebSSO SAML2.0 WebSSO
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
.. seqdiag:: .. graphviz::
:name: saml2-websso :name: saml2-websso
:alt: Diagram of a standard WebSSO authentication flow. :alt: Diagram of a standard WebSSO authentication flow.
seqdiag { digraph {
edge_length = 325; nodesep=1
default_fontsize = 13; node [shape=point]
useragent [label = "User Agent"]; sp [label = "Service Provider"]; idp [label = "Identity Provider"];
useragent -> sp [label = "GET /secure"]; // first column
useragent <- sp [label = "HTTP 302 useragent_top [label = "User Agent" shape="box"]
Location: https://idp/auth? useragent_bottom [label = "User Agent" shape="box"]
SAMLRequest=req"]; // chain rows of the column
useragent -> idp [label = "GET /auth?SAMLRequest=req"]; useragent_top -> u0 [arrowhead="none" style="dashed"]
idp -> idp [label = "Authenticate"]; u0 -> u1 -> u2 -> u3 -> u4 -> u5 -> u6 [arrowhead="none" style="bold"]
useragent <- idp [label = "HTTP 200 u6 -> useragent_bottom [arrowhead="none" style="dashed"]
SAMLResponse in HTML form"];
useragent -> sp [label = "POST /assertionconsumerservice"]; // second column
sp -> sp [label = "Validate"]; sp_top [label = "Service Provider" shape="box"]
useragent <- sp [label = "HTTP 302; Location: /secure"]; sp_bottom [label = "Service Provider" shape="box"]
useragent -> sp [label = "GET /secure"]; sp_top -> sp0 [arrowhead="none" style="dashed"]
sp0 -> sp1 [arrowhead="none" style="bold"]
sp1 -> sp2 [arrowhead="none" style="dashed"]
sp2 -> sp3 -> sp4 [arrowhead="none" style="bold"]
sp4 -> sp5 -> sp_bottom [arrowhead="none" style="dashed"]
idp_top [label = "Identity Provider" shape="box"]
idp_bottom [label = "Identity Provider" shape="box"]
idp_top -> idp0 [arrowhead="none" style="dashed"]
idp0 -> idp1 -> idp2 [arrowhead="none" style="bold"]
idp2 -> idp_bottom [arrowhead="none" style="dashed"]
// Order columns
useragent_top -> sp_top -> idp_top [style="invis"]
// Event links
u0 -> sp0 [weight=0 xlabel = "GET /secure"]
u1 -> sp1 [weight=0 xlabel = "HTTP 302\nLocation: https://idp/auth?SAMLRequest=req" dir=back]
u2 -> idp0 [weight=0 xlabel = "GET /auth?SAMLRequest=req"]
idp1 -> idp1 [weight=0 xlabel = "Authenticate"]
u3 -> idp2 [weight=0 xlabel = "HTTP 200\nSAMLResponse in HTML form" dir=back]
u4 -> sp2 [weigth=0 xlabel= "POST /assertionconsumerservice"]
sp3 -> sp3 [weight=0 xlabel = "Validate"]
u5 -> sp4 [ weight=0 xlabel = "HTTP 302; Location: /secure" dir=back]
u6 -> sp5 [weight=0 xlabel = "GET /secure"]
// bind nodes to levels
{rank=same; useragent_top sp_top idp_top }
{rank=same; u0 sp0 }
{rank=same; u1 sp1 }
{rank=same; u2 idp0 }
{rank=same; u3 idp2 }
{rank=same; u4 sp2 }
{rank=same; u5 sp4 }
{rank=same; u6 sp5 }
{rank=same; useragent_bottom sp_bottom idp_bottom }
} }
This diagram shows a standard `WebSSO`_ authentication flow, not one involving This diagram shows a standard `WebSSO`_ authentication flow, not one involving
@ -194,25 +260,60 @@ redirect back to the original resource the user had requested.
SAML2.0 ECP SAML2.0 ECP
~~~~~~~~~~~ ~~~~~~~~~~~
.. seqdiag:: .. graphviz::
:name: saml2-ecp :name: saml2-ecp
:alt: Diagram of a standard ECP authentication flow. :alt: Diagram of a standard ECP authentication flow.
seqdiag { digraph {
default_fontsize = 13; nodesep=1
useragent [label = "User Agent"]; sp [label = "Service Provider"]; idp [label = "Identity Provider"]; node [shape=point]
useragent -> sp [label = "GET /secure"];
useragent <- sp [label = "HTTP 200 // first column
SAML Request"]; useragent_top [label = "User Agent" shape="box"]
useragent -> idp [label = "POST /auth useragent_bottom [label = "User Agent" shape="box"]
SAML Request"]; // chain rows of the column
idp -> idp [label = "Authenticate"]; useragent_top -> u0 [arrowhead="none" style="dashed"]
useragent <- idp [label = "HTTP 200 u0 -> u1 -> u2 -> u3 -> u4 -> u5 [arrowhead="none" style="bold"]
SAMLResponse in SOAP"]; u5 -> useragent_bottom [arrowhead="none" style="dashed"]
useragent -> sp [label = "POST /responseconsumer"];
sp -> sp [label = "Validate"]; // second column
useragent <- sp [label = "HTTP 200 /secure"]; sp_top [label = "Service Provider" shape="box"]
} sp_bottom [label = "Service Provider" shape="box"]
sp_top -> sp0 [arrowhead="none" style="dashed"]
sp0 -> sp1 [arrowhead="none" style="bold"]
sp1 -> sp2 [arrowhead="none" style="dashed"]
sp2 -> sp3 -> sp4 [arrowhead="none" style="bold"]
sp4 -> sp_bottom [arrowhead="none" style="dashed"]
idp_top [label = "Identity Provider" shape="box"]
idp_bottom [label = "Identity Provider" shape="box"]
idp_top -> idp0 [arrowhead="none" style="dashed"]
idp0 -> idp1 -> idp2 [arrowhead="none" style="bold"]
idp2 -> idp_bottom [arrowhead="none" style="dashed"]
// Order columns
useragent_top -> sp_top -> idp_top [style="invis"]
// Event links
u0 -> sp0 [weight=0 xlabel = "GET /secure"]
u1 -> sp1 [weight=0 xlabel = "HTTP 200\nSAML Request" dir=back]
u2 -> idp0 [weight=0 xlabel = "POST /auth\nSAML Request"]
idp1 -> idp1 [weight=0 xlabel = "Authenticate"]
u3 -> idp2 [weight=0 xlabel = "HTTP 200\nSAMLResponse in SOAP" dir=back]
u4 -> sp2 [weigth=0 xlabel= "POST /assertionconsumerservice"]
sp3 -> sp3 [weight=0 xlabel = "Validate"]
u5 -> sp4 [ weight=0 xlabel = "HTTP 200; Location: /secure" dir=back]
// bind nodes to levels
{rank=same; useragent_top sp_top idp_top }
{rank=same; u0 sp0 }
{rank=same; u1 sp1 }
{rank=same; u2 idp0 }
{rank=same; u3 idp2 }
{rank=same; u4 sp2 }
{rank=same; u5 sp4 }
{rank=same; useragent_bottom sp_bottom idp_bottom }
}
`ECP`_ is another SAML profile. Generally the flow is similar to the WebSSO `ECP`_ is another SAML profile. Generally the flow is similar to the WebSSO
flow, but it is designed for a client that natively understands SAML, for flow, but it is designed for a client that natively understands SAML, for
@ -231,36 +332,89 @@ WebSSO with keystone and horizon
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. seqdiag:: .. graphviz::
:name: saml2-keystone-horizon :name: saml2-keystone-horizon
:alt: Diagram of the SAML2.0 WebSSO auth flow specific to horizon, keystone, and the :alt: Diagram of the SAML2.0 WebSSO auth flow specific to horizon, keystone, and the
HTTPD module acting as service provider. HTTPD module acting as service provider.
seqdiag { digraph {
default_fontsize = 13; nodesep=1
useragent [label = "User Agent"]; horizon [label = "Horizon"]; httpd [label = "HTTPD", color = "lightgrey"]; keystone [label = "Keystone", color = "lightgrey"]; idp [label = "Identity Provider"]; node [shape=point]
useragent -> horizon [label = "POST /auth/login"];
useragent <- horizon [label = "HTTP 302 // first column
Location: useragent_top [label = "User Agent" shape="box"]
/v3/auth/OS-FEDERATION useragent_bottom [label = "User Agent" shape="box"]
/websso/saml2"]; // chain rows of the column
useragent -> httpd [label = "GET /v3/auth/OS-FEDERATION/websso/saml2"]; useragent_top -> u0 [arrowhead="none" style="dashed"]
useragent <- httpd [label = "HTTP 302 u0 -> u1 -> u2 -> u3 -> u4 -> u5 -> u6 -> u7 -> u8 -> u9 -> u10 -> u11 [arrowhead="none" style="bold"]
Location: https://idp/auth?SAMLRequest=req"]; u11 -> useragent_bottom [arrowhead="none" style="bold"]
useragent -> idp [label = "GET /auth"];
idp -> idp [label = "Authenticate"]; // second column
useragent <- idp [label = "HTTP 200 h_top [label = "Horizon" shape="box"]
SAMLResponse in HTML form"]; h_bottom [label = "Horizon" shape="box"]
useragent -> httpd [label = "POST /assertionconsumerservice"]; h_top -> h0 [arrowhead="none" style="dashed"]
httpd -> httpd [label = "Validate"]; h0 -> h1 [arrowhead="none" style="bold"]
useragent <- httpd [label = "HTTP 302 h1 -> h2 [arrowhead="none" style="dashed"]
Location: /v3/auth/OS-FEDERATION/websso/saml2"]; h2 -> h3 [arrowhead="none" style="bold"]
useragent -> keystone [label = "GET /v3/auth/OS-FEDERATION/websso/saml2"]; h3 -> h_bottom [arrowhead="none" style="dashed"]
keystone -> keystone [label = "Issue token"];
useragent <- keystone [label = "HTTP 200 // second column
HTML form containing unscoped token"]; http_top [label = "Httpd" shape="box"]
useragent -> horizon [label = "POST /auth/websso"]; http_bottom [label = "Httpd" shape="box"]
useragent <- horizon [label = "successful login"]; http_top -> http0 [arrowhead="none" style="dashed"]
http0 -> http1 [arrowhead="none" style="bold"]
http1 -> http2 [arrowhead="none" style="dashed"]
http2 -> http3 -> http4 [arrowhead="none" style="bold"]
http4 -> http_bottom [arrowhead="none" style="dashed"]
// second column
k_top [label = "Keystone" shape="box"]
k_bottom [label = "Keystone" shape="box"]
k_top -> k0 [arrowhead="none" style="dashed"]
k0 -> k1 -> k2 [arrowhead="none" style="bold"]
k2 -> k_bottom [arrowhead="none" style="dashed"]
idp_top [label = "Identity Provider" shape="box"]
idp_bottom [label = "Identity Provider" shape="box"]
idp_top -> idp0 [arrowhead="none" style="dashed"]
idp0 -> idp1 -> idp2 [arrowhead="none" style="bold"]
idp2 -> idp_bottom [arrowhead="none" style="dashed"]
// Order columns
useragent_top -> h_top -> http_top -> k_top -> idp_top [style="invis"]
// Event links
u0 -> h0 [weight=0 xlabel = "POST /v3/auth/tokens"]
u1 -> h1 [weight=0 xlabel = "HTTP 302\nLocation: /v3/auth/OS-FEDERATION/webssol/saml2" dir=back]
u2 -> http0 [weight=0 xlabel = "GET /v3/auth/OS-FEDERATION/websso/saml2"]
u3 -> http1 [weight=0 xlabel = "HTTP 302\nLocation: https://idp/auth?SAMLRequest=req" dir=back]
u4 -> idp0 [weight=0 xlabel = "GET /auth"]
idp1 -> idp1 [weight=0 xlabel = "Authenticate"]
u5 -> idp2 [weight=0 xlabel = "HTTP 200\nSAMLResonse in HTML form" dir=back]
u6 -> http2 [weight=0 xlabel = "POST /assertionconsumerservice"]
http3 -> http3 [weight=0 xlabel = "Validate"]
u7 -> http4 [weight=0 xlabel = "HTTP 302\nLocation: /v3/auth/OS-FEDERATION/websso/saml2" dir=back]
u8 -> k0 [weight=0 xlabel="GET /v3/auth/OS-FEDERATION/websso/saml2"]
k1 -> k1 [weight=0 xlabel="Issue token"]
u9 -> k2 [weight=0 xlabel="HTTP 200\nHTML form containing unscoped token" dir=back]
u10 -> h2 [weight=0 xlabel="POST /auth/websso"]
u11 -> h3 [weight=0 xlabel="successful login" dir=back]
// bind nodes to levels
{rank=same;useragent_top;h_top;http_top;k_top;idp_top}
{rank=same;u0;h0 }
{rank=same;u1;h1 }
{rank=same;u2;http0 }
{rank=same;u3;http1 }
{rank=same;u4;idp0 }
{rank=same;u5;idp2 }
{rank=same;u6;http2 }
{rank=same;u7;http4 }
{rank=same;u8;k0 }
{rank=same;u9;k2 }
{rank=same;u10;h2}
{rank=same;u11;h3}
{rank=same;useragent_bottom;h_bottom;http_bottom;k_bottom;idp_bottom}
} }
Keystone is not a web front-end, which means horizon needs to handle some parts Keystone is not a web front-end, which means horizon needs to handle some parts
@ -292,29 +446,68 @@ secure resource it requests from keystone.
Keystone to Keystone Keystone to Keystone
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
.. seqdiag:: .. graphviz::
:name: keystone-to-keystone :name: keystone-to-keystone
:alt: Diagram of the IdP-initiated auth flow in a keystone-to-keystone model. :alt: Diagram of the IdP-initiated auth flow in a keystone-to-keystone model.
seqdiag { digraph {
edge_length = 240; nodesep=1
default_fontsize = 13; node [shape=point]
useragent [label = "User Agent"]; sp [label = "Service Provider"]; idp [label = "Identity Provider"];
useragent -> idp [label = "POST /v3/auth/tokens"]; // first column
idp -> idp [label = "Authenticate"]; useragent_top [label = "User Agent" shape="box"]
useragent <- idp [label = "HTTP 201 useragent_bottom [label = "User Agent" shape="box"]
X-Subject-Token: token"]; // chain rows of the column
useragent -> idp [label = "POST /v3/auth/OS-FEDERATION/saml2/ecp"]; useragent_top -> u0 [arrowhead="none" style="dashed"]
useragent <- idp [label = "HTTP 201 u0 -> u1 -> u2 -> u3 -> u4 -> u5 -> u6 -> u7 -> u8 [arrowhead="none" style="bold"]
SAMLResponse in SOAP envelope"]; u8 -> useragent_bottom [arrowhead="none" style="dashed"]
useragent -> sp [label = "POST /PAOS-url"];
sp -> sp [label = "Validate"]; // second column
useragent <- sp [label = "HTTP 302"]; sp_top [label = "Service Provider" shape="box"]
useragent -> sp [label = "GET /v3/OS-FED/.../auth"]; sp_bottom [label = "Service Provider" shape="box"]
useragent <- sp [label = "HTTP 201 sp_top -> sp0 [arrowhead="none" style="dashed"]
X-Subject-Token: unscoped token"]; sp0 -> sp1 -> sp2 [arrowhead="none" style="bold"]
useragent -> sp [label = "POST /v3/auth/tokens sp2 -> sp3 [arrowhead="none" style="dashed"]
(request scoped token)"]; sp3 -> sp4 [arrowhead="none" style="bold"]
sp4 -> sp5 [arrowhead="none" style="dashed"]
sp5 -> sp_bottom [arrowhead="none" style="dashed"]
idp_top [label = "Identity Provider" shape="box"]
idp_bottom [label = "Identity Provider" shape="box"]
idp_top -> idp0 [arrowhead="none" style="dashed"]
idp0 -> idp1 -> idp2 [arrowhead="none" style="bold"]
idp2 -> idp3 [arrowhead="none" style="dashed"]
idp3 -> idp4 [arrowhead="none" style="bold"]
idp4 -> idp_bottom [arrowhead="none" style="dashed"]
// Order columns
useragent_top -> sp_top -> idp_top [style="invis"]
// Event links
u0 -> idp0 [weight=0 xlabel = "POST /v3/auth/tokens"]
idp1 -> idp1 [weight=0 xlabel = "Authenticate"]
u1 -> idp2 [weight=0 xlabel = "HTTP 201\nX-Subject-Token: token" dir=back]
u2 -> idp3 [weight=0 xlabel = "POST /v3/auth/OS-FEDERATION/saml2/ecp"]
u3 -> idp4 [weight=0 xlabel = "HTTP 201\nSAMLResponse in SOAP envelope" dir=back]
u4 -> sp0 [weight=0 xlabel = "POST /PAOS-url"]
sp1 -> sp1 [weight=0 xlabel = "Validate"]
u5 -> sp2 [weight=0 xlabel = "HTTP 302" dir=back]
u6 -> sp3 [weight=0 xlabel = "GET /v3/OS-FED/.../auth"]
u7 -> sp4 [weight=0 xlabel = "HTTP 201\nX-Subject-Token: unscoped token" dir=back]
u8 -> sp5 [weight=0 xlabel = "POST /v3/auth/tokens\n(request scoped token)"]
// bind nodes to levels
{rank=same;useragent_top;sp_top;idp_top}
{rank=same;u0;idp0 }
{rank=same;u1;idp2 }
{rank=same;u2;idp3 }
{rank=same;u3;idp4 }
{rank=same;u4;sp0 }
{rank=same;u5;sp2 }
{rank=same;u6;sp3 }
{rank=same;u7;sp4 }
{rank=same;u8;sp5 }
{rank=same;useragent_bottom;sp_bottom;idp_bottom}
} }
When keystone is used as an Identity Provider in a Keystone to Keystone When keystone is used as an Identity Provider in a Keystone to Keystone
@ -339,31 +532,67 @@ OpenID Connect
OpenID Connect Authentication Flow OpenID Connect Authentication Flow
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. seqdiag:: .. graphviz::
:name: openidc :name: openidc
:alt: Diagram of a standard OpenID Connect authentication flow :alt: Diagram of a standard OpenID Connect authentication flow
:align: left :align: left
seqdiag { digraph auth {
edge_length = 330; nodesep=1
default_fontsize = 13; node [shape=point]
useragent [label = "User Agent"]; sp [label = "Service Provider"]; idp [label = "Identity Provider"];
useragent -> sp [label = "GET /secure"]; // first column
useragent <- sp [label = "HTTP 302 useragent_top [label = "User Agent" shape="box"]
Location: https://idp/auth? useragent_bottom [label = "User Agent" shape="box"]
client_id=XXX&redirect_uri=https://sp/secure"]; // chain rows of the column
useragent -> idp [label = "GET /auth?client_id=XXX&redirect_uri=https://sp/secure"]; useragent_top -> u0 [arrowhead="none" style="dashed"]
idp -> idp [label = "Authenticate"]; u0 -> u1 -> u2 -> u3 -> u4 -> u5 -> u6 [arrowhead="none" style="bold"]
useragent <- idp [label = "HTTP 302 u6 -> useragent_bottom [arrowhead="none" style="dashed"]
Location: https://sp/auth?code=XXX"];
useragent -> sp [label = "GET /auth?code=XXX"]; // second column
sp -> idp [label = "POST https://idp/token sp_top [label = "Service Provider" shape="box"]
code=XXX&redirect_uri=https://sp/secure"]; sp_bottom [label = "Service Provider" shape="box"]
sp <- idp [label = "HTTP 200 sp_top -> sp0 [arrowhead="none" style="dashed"]
{\"access_code\": \"XXX\", sp0 -> sp1 [arrowhead="none" style="bold"]
\"id_token\": \"XXX\"}"]; sp1 -> sp2 [arrowhead="none" style="dashed"]
useragent <- sp [label = "HTTP 302; Location: /secure"]; sp2 -> sp3 -> sp4 -> sp5 [arrowhead="none" style="bold"]
useragent -> sp [label = "GET /secure"]; sp5 -> sp6 -> sp_bottom [arrowhead="none" style="dashed"]
idp_top [label = "Identity Provider" shape="box"]
idp_bottom [label = "Identity Provider" shape="box"]
idp_top -> idp0 [arrowhead="none" style="dashed"]
idp0 -> idp1 -> idp2 [arrowhead="none" style="bold"]
idp2 -> idp3 [arrowhead="none" style="dashed"]
idp3 -> idp4 [arrowhead="none" style="bold"]
idp4 -> idp_bottom [arrowhead="none" style="dashed"]
// Order columns
useragent_top -> sp_top -> idp_top [style="invis"]
// Event links
u0 -> sp0 [weight=0 xlabel = "GET /secure"]
u1 -> sp1 [weight=0 xlabel = "HTTP 302\nLocation: https://idp/auth?client_id=XXX&redirect_uri=https://sp/secure" dir=back]
u2 -> idp0 [weight=0 xlabel = "GET /auth?client_id=XXX&redirect_uri=https://sp/secure"]
idp1 -> idp1 [weight=0 xlabel = "Authenticate"]
u3 -> idp2 [weight=0 xlabel = "HTTP 302\nLocation: https://sp/auth?code=XXX" dir=back]
u4 -> sp2 [weight=0 xlabel = "GET /auth?code=XXX"]
sp3 -> idp3 [weight=0 xlabel = "POST https://idp/token?code=XXX&redirect_uri=https://sp/secure"]
sp4 -> idp4 [weight=0 xlabel = "HTTP 200\n{\"access_code\": \"XXX\", \"id_token\": \"XXX\"}" dir=back]
u5 -> sp5 [weight=0 xlabel = "HTTP 302; Location: /secure" dir=back]
u6 -> sp6 [weight=0 xlabel = "GET /secure"]
// bind nodes to levels
{rank=same;useragent_top;sp_top;idp_top}
{rank=same;u0;sp0}
{rank=same;u1;sp1}
{rank=same;u2;idp0}
{rank=same;u3;idp2}
{rank=same;u4;sp2}
{rank=same;sp3;idp3}
{rank=same;sp4;idp4}
{rank=same;u5;sp5}
{rank=same;u6;sp6}
{rank=same;useragent_bottom;sp_bottom;idp_bottom }
} }
OpenID Connect is different from any SAML2.0 flow because the negotiation is not OpenID Connect is different from any SAML2.0 flow because the negotiation is not
@ -382,47 +611,93 @@ and exchange it for an ID token.
OpenID Connect with keystone and horizon OpenID Connect with keystone and horizon
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. seqdiag:: .. graphviz::
:name: oidc-keystone-horizon :name: oidc-keystone-horizon
:alt: Diagram of the OpenID Connect WebSSO auth flow specific to horizon, :alt: Diagram of the OpenID Connect WebSSO auth flow specific to horizon,
keystone, and the HTTPD module acting as service provider. keystone, and the HTTPD module acting as service provider.
seqdiag { digraph {
edge_length = 200 nodesep=1
default_fontsize = 13; node [shape=point]
useragent [label = "User Agent"]; horizon [label = "Horizon"]; httpd [label = "HTTPD", color = "lightgrey"]; keystone [label = "Keystone", color = "lightgrey"]; idp [label = "Identity Provider"];
useragent -> horizon [label = "POST /auth/login"]; // first column
useragent <- horizon [label = "HTTP 302 useragent_top [label = "User Agent" shape="box"]
Location: useragent_bottom [label = "User Agent" shape="box"]
/v3/auth/OS-FEDERATION // chain rows of the column
/websso/openid"]; useragent_top -> u0 [arrowhead="none" style="dashed"]
useragent -> httpd [label = "GET /v3/auth/OS-FEDERATION/websso/openid"]; u0 -> u1 -> u2 -> u3 -> u4 -> u5 -> u6 -> u7 -> u8 -> u9 -> u10 -> u11 [arrowhead="none" style="bold"]
useragent <- httpd [label = "HTTP 302 u11 -> useragent_bottom [arrowhead="none" style="dashed"]
Location:
https://idp/auth? // second column
client_id=XXX& h_top [label = "Horizon" shape="box"]
redirect_uri=https://sp/v3/auth/OS-FEDERATION/websso"]; h_bottom [label = "Horizon" shape="box"]
useragent -> idp [label = "GET /auth?client_id=XXX& h_top -> h0 [arrowhead="none" style="dashed"]
redirect_uri=https://sp/v3/auth/OS-FEDERATION/websso"]; h0 -> h1 [arrowhead="none" style="bold"]
idp -> idp [label = "Authenticate"]; h1 -> h2 [arrowhead="none" style="dashed"]
useragent <- idp [label = "HTTP 302 h2 -> h3 [arrowhead="none" style="bold"]
Location: https://sp/v3/auth/OS-FEDERATION/websso"]; h3 -> h_bottom [arrowhead="none" style="dashed"]
useragent -> httpd [label = "GET /v3/auth/OS-FEDERATION/websso"];
httpd -> idp [label = "POST https://idp/token http_top [label = "Httpd" shape="box"]
code=XXX& http_bottom [label = "Httpd" shape="box"]
redirect_uri=https://sp/v3/auth/OS-FEDERATION/websso"]; http_top -> http0 [arrowhead="none" style="dashed"]
httpd <- idp [label = "HTTP 200 http0 -> http1 [arrowhead="none" style="bold"]
{\"access_code\": \"XXX\", http1 -> http2 [arrowhead="none" style="dashed"]
\"id_token\": \"XXX\"}"]; http2 -> http3 -> http4 -> http5 [arrowhead="none" style="bold"]
useragent <- httpd [label = "HTTP 302 http5 -> http_bottom [arrowhead="none" style="dashed"]
Location: /v3/auth/OS-FEDERATION/websso/mapped"];
useragent -> keystone [label = "GET /v3/auth/OS-FEDERATION/websso/mapped"]; k_top [label = "Keystone" shape="box"]
keystone -> keystone [label = "Issue token"]; k_bottom [label = "Keystone" shape="box"]
useragent <- keystone [label = "HTTP 200 k_top -> k0 [arrowhead="none" style="dashed"]
HTML form containing unscoped token"]; k0 -> k1 -> k2 [arrowhead="none" style="bold"]
useragent -> horizon [label = "POST /auth/websso"]; k2 -> k_bottom [arrowhead="none" style="dashed"]
useragent <- horizon [label = "successful login"];
} idp_top [label = "Identity Provider" shape="box"]
idp_bottom [label = "Identity Provider" shape="box"]
idp_top -> idp0 [arrowhead="none" style="dashed"]
idp0 -> idp1 -> idp2 [arrowhead="none" style="bold"]
idp2 -> idp3 [arrowhead="none" style="dashed"]
idp3 -> idp4 [arrowhead="none" style="bold"]
idp4 -> idp_bottom [arrowhead="none" style="dashed"]
// Order columns
useragent_top -> h_top -> http_top -> k_top -> idp_top [style="invis"]
// Event links
u0 -> h0 [weight=0 xlabel = "POST /v3/auth/tokens"]
u1 -> h1 [weight=0 xlabel = "HTTP 302\nLocation: /v3/auth/OS-FEDERATION/webssol/openid" dir=back]
u2 -> http0 [weight=0 xlabel = "GET /v3/auth/OS-FEDERATION/websso/openid"]
u3 -> http1 [weight=0 xlabel = "HTTP 302\nLocation: https://idp/auth?client_id=XXX&redirect_uri=https://sp/v3/auth/OS-FEDERATION/websso" dir=back]
u4 -> idp0 [weight=0 xlabel = "GET /auth?client_id=XXX&redirect_uri=https://sp/v3/auth/OS-FEDERATION/websso"]
idp1 -> idp1 [weight=0 xlabel = "Authenticate"]
u5 -> idp2 [weight=0 xlabel = "HTTP 203\nLocation: https://sp/v3/auth/OS-FEDERATION/websso" dir=back]
u6 -> http2 [weight=0 xlabel = "GET /v3/auth/OS-FEDERATION/websso"]
http3 -> idp3 [weight=0 xlabel = "POST https://idp/tokencode=XXX&redirect_uri=https://sp/v3/auth/OS-FEDERATION/websso"]
http4 -> idp4 [weight=0 xlabel = "HTTP 200\n{\"access_code\": \"XXX\"\n\"id_token\": \"XXX\"}" dir=back]
u7 -> http5 [weight=0 xlabel = "HTTP 302\nLocation: /v3/auth/OS-FEDERATION/websso/mapped" dir=back]
u8 -> k0 [weight=0 xlabel="GET /v3/auth/OS-FEDERATION/websso/mapped"]
k1 -> k1 [weight=0 xlabel="Issue token"]
u9 -> k2 [weight=0 xlabel="HTTP 200\nHTML form containing unscoped token" dir=back]
u10 -> h2 [weight=0 xlabel="POST /auth/websso"]
u11 -> h3 [weight=0 xlabel="successful login" dir=back]
// bind nodes to levels
{rank=same;useragent_top;h_top;http_top;k_top;idp_top}
{rank=same;u0;h0 }
{rank=same;u1;h1 }
{rank=same;u2;http0 }
{rank=same;u3;http1 }
{rank=same;u4;idp0 }
{rank=same;u5;idp2 }
{rank=same;u6;http2 }
{rank=same;http3;idp3 }
{rank=same;http4;idp4 }
{rank=same;u7;http5 }
{rank=same;u8;k0 }
{rank=same;u9;k2 }
{rank=same;u10;h2}
{rank=same;u11;h3}
{rank=same;useragent_bottom;h_bottom;http_bottom;k_bottom;idp_bottom}
}
From horizon and keystone's point of view, the authentication flow is the same From horizon and keystone's point of view, the authentication flow is the same
for OpenID Connect as it is for SAML2.0. It is only the HTTPD OpenIDC module for OpenID Connect as it is for SAML2.0. It is only the HTTPD OpenIDC module

View File

@ -174,10 +174,11 @@ child of project `Alpha`. All projects assume a default limit of 10 cores via a
registered limit. The labels in the diagrams below use shorthand notation for registered limit. The labels in the diagrams below use shorthand notation for
`limit` and `usage` as `l` and `u`, respectively: `limit` and `usage` as `l` and `u`, respectively:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha [label="Alpha (u=0)"]; Alpha [label="Alpha (u=0)"];
Beta [label=" Beta (u=0)"]; Beta [label=" Beta (u=0)"];
@ -188,10 +189,11 @@ Each project may use up to 10 cores because of the registered limit and none of
the projects have an override. Using flat enforcement, you're allowed to the projects have an override. Using flat enforcement, you're allowed to
``UPDATE LIMIT on Alpha to 20``: ``UPDATE LIMIT on Alpha to 20``:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=20, u=0)", textcolor = "#00af00"]; Alpha [label="Alpha (l=20, u=0)", textcolor = "#00af00"];
Beta [label=" Beta (u=0)"]; Beta [label=" Beta (u=0)"];
@ -202,10 +204,11 @@ the projects have an override. Using flat enforcement, you're allowed to
You're also allowed to ``UPDATE LIMIT on Charlie to 30``, even though `Charlie` You're also allowed to ``UPDATE LIMIT on Charlie to 30``, even though `Charlie`
is a sub-project of both `Beta` and `Alpha`. is a sub-project of both `Beta` and `Alpha`.
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=20, u=0)"]; Alpha [label="Alpha (l=20, u=0)"];
Beta [label=" Beta (u=0)"]; Beta [label=" Beta (u=0)"];
@ -220,10 +223,11 @@ Conversely, you can simulate hierarchical enforcement by adjusting limits
through the project tree manually. For example, let's still assume 10 is the through the project tree manually. For example, let's still assume 10 is the
default limit imposed by an existing registered limit: default limit imposed by an existing registered limit:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha [label="Alpha (u=0)"]; Alpha [label="Alpha (u=0)"];
Beta [label=" Beta (u=0)"]; Beta [label=" Beta (u=0)"];
@ -232,10 +236,11 @@ default limit imposed by an existing registered limit:
You may set a project-specific override to ``UPDATE LIMIT on Alpha to 30``: You may set a project-specific override to ``UPDATE LIMIT on Alpha to 30``:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=30, u=0)", textcolor = "#00af00"]; Alpha [label="Alpha (l=30, u=0)", textcolor = "#00af00"];
Beta [label=" Beta (u=0)"]; Beta [label=" Beta (u=0)"];
@ -244,10 +249,11 @@ You may set a project-specific override to ``UPDATE LIMIT on Alpha to 30``:
Next you can ``UPDATE LIMIT on Beta to 20``: Next you can ``UPDATE LIMIT on Beta to 20``:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=30, u=0)"]; Alpha [label="Alpha (l=30, u=0)"];
Beta [label=" Beta (l=20, u=0)", textcolor = "#00af00"]; Beta [label=" Beta (l=20, u=0)", textcolor = "#00af00"];
@ -258,10 +264,11 @@ Theoretically, the entire project tree consisting of `Alpha`, `Beta`, and
`Charlie` is limited to 60 cores. If you'd like to ensure only 30 cores are `Charlie` is limited to 60 cores. If you'd like to ensure only 30 cores are
used within the entire hierarchy, you can ``UPDATE LIMIT on Alpha to 0``: used within the entire hierarchy, you can ``UPDATE LIMIT on Alpha to 0``:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=0, u=0)", textcolor = "#00af00"]; Alpha [label="Alpha (l=0, u=0)", textcolor = "#00af00"];
Beta [label=" Beta (l=20, u=0)"]; Beta [label=" Beta (l=20, u=0)"];
@ -322,10 +329,11 @@ is cores and the default registered limit for cores is 10. Also assume we have
the following project hierarchy where `Alpha` has a limit of 20 cores and its the following project hierarchy where `Alpha` has a limit of 20 cores and its
usage is currently 4: usage is currently 4:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -337,10 +345,11 @@ usage is currently 4:
Technically, both `Beta` and `Charlie` can use up to 8 cores each: Technically, both `Beta` and `Charlie` can use up to 8 cores each:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -356,10 +365,11 @@ and check the usage of each project in the hierarchy to see that the total
usage of `Alpha`, `Beta`, and `Charlie` is equal to the limit of the tree, set usage of `Alpha`, `Beta`, and `Charlie` is equal to the limit of the tree, set
by `Alpha.limit`: by `Alpha.limit`:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -372,10 +382,11 @@ by `Alpha.limit`:
Despite the usage of the tree being equal to the limit, we can still add Despite the usage of the tree being equal to the limit, we can still add
children to the tree: children to the tree:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -390,10 +401,11 @@ children to the tree:
Even though the project can be created, the current usage of cores across the Even though the project can be created, the current usage of cores across the
tree prevents `Delta` from claiming any cores: tree prevents `Delta` from claiming any cores:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -408,10 +420,11 @@ tree prevents `Delta` from claiming any cores:
Creating a grandchild of project `Alpha` is forbidden because it violates the Creating a grandchild of project `Alpha` is forbidden because it violates the
two-level hierarchical constraint: two-level hierarchical constraint:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -434,10 +447,11 @@ but may be implemented with a separate model.
Granting `Beta` the ability to claim more cores can be done by giving `Beta` a Granting `Beta` the ability to claim more cores can be done by giving `Beta` a
project-specific override for cores project-specific override for cores
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -451,10 +465,11 @@ Note that regardless of this update, any subsequent requests to claim more
cores in the tree will be rejected since the usage is equal to the limit of the cores in the tree will be rejected since the usage is equal to the limit of the
`Alpha`. `Beta` can claim cores if they are released from `Alpha` or `Charlie`: `Alpha`. `Beta` can claim cores if they are released from `Alpha` or `Charlie`:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -464,10 +479,11 @@ cores in the tree will be rejected since the usage is equal to the limit of the
Charlie [label="Charlie (u=6)", textcolor = "#00af00"]; Charlie [label="Charlie (u=6)", textcolor = "#00af00"];
} }
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -482,10 +498,11 @@ able to claim any more cores because the total usage of the tree is equal to
the limit of `Alpha`, thus preventing `Charlie` from reclaiming the cores it the limit of `Alpha`, thus preventing `Charlie` from reclaiming the cores it
had: had:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -502,10 +519,11 @@ is forbidden. Even though it is possible for the sum of all limits under
limit of the parent would result in strange user experience and be misleading limit of the parent would result in strange user experience and be misleading
since the total usage of the tree would be capped at the limit of the parent: since the total usage of the tree would be capped at the limit of the parent:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -515,10 +533,11 @@ since the total usage of the tree would be capped at the limit of the parent:
Charlie [label="Charlie (u=0)"]; Charlie [label="Charlie (u=0)"];
} }
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
Alpha -> Charlie; Alpha -> Charlie;
@ -533,10 +552,11 @@ since the total usage of the tree would be capped at the limit of the parent:
Finally, let's still assume the default registered limit for cores is 10, but Finally, let's still assume the default registered limit for cores is 10, but
we're going to create project `Alpha` with a limit of 6 cores. we're going to create project `Alpha` with a limit of 6 cores.
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha; Alpha;
@ -548,10 +568,11 @@ API ensures that project `Beta` doesn't assume the default of 10, despite the
registered limit of 10 cores. Instead, the child assumes the parent's limit registered limit of 10 cores. Instead, the child assumes the parent's limit
since no single child limit should exceed the limit of the parent: since no single child limit should exceed the limit of the parent:
.. blockdiag:: .. graphviz::
blockdiag { digraph {
orientation = portrait; orientation = portrait;
node [shape=box]
Alpha -> Beta; Alpha -> Beta;
@ -562,9 +583,10 @@ since no single child limit should exceed the limit of the parent:
This behavior is consistent regardless of the number of children added under This behavior is consistent regardless of the number of children added under
project `Alpha`. project `Alpha`.
.. blockdiag:: .. graphviz::
blockdiag { digraph {
node [shape=box]
orientation = portrait; orientation = portrait;
Alpha -> Beta; Alpha -> Beta;

View File

@ -34,6 +34,7 @@
# ones. # ones.
extensions = [ extensions = [
'sphinx.ext.coverage', 'sphinx.ext.coverage',
'sphinx.ext.graphviz',
'sphinx.ext.viewcode', 'sphinx.ext.viewcode',
'sphinx.ext.todo', 'sphinx.ext.todo',
'oslo_config.sphinxconfiggen', 'oslo_config.sphinxconfiggen',
@ -42,9 +43,7 @@ extensions = [
'openstackdocstheme', 'openstackdocstheme',
'oslo_policy.sphinxext', 'oslo_policy.sphinxext',
'sphinxcontrib.apidoc', 'sphinxcontrib.apidoc',
'sphinxcontrib.seqdiag',
'sphinx_feature_classification.support_matrix', 'sphinx_feature_classification.support_matrix',
'sphinxcontrib.blockdiag',
] ]
blockdiag_html_image_format = 'SVG' blockdiag_html_image_format = 'SVG'