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]
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
sphinx>=2.0.0,!=2.1.0 # BSD
sphinxcontrib-apidoc>=0.2.0 # BSD
sphinxcontrib-seqdiag>=0.8.4 # BSD
sphinx-feature-classification>=0.3.2 # Apache-2.0
sphinxcontrib-blockdiag>=1.5.5 # BSD
reno>=3.1.0 # Apache-2.0
os-api-ref>=1.4.0 # Apache-2.0
python-ldap>=3.0.0 # PSF

View File

@ -117,20 +117,51 @@ process is key to being able to debug later on.
Normal keystone
---------------
.. seqdiag::
.. graphviz::
:name: normal-keystone
:alt: Diagram of keystone's normal auth flow, in which a user agent
authenticates and authorizes themself with keystone and obtains a
scoped token to pass to an OpenStack service.
seqdiag {
default_fontsize = 13;
useragent [label = "User Agent"]; keystone [label = "Keystone"]; openstack [label = "OpenStack"];
useragent -> keystone [label = "GET /v3/auth/tokens"];
keystone -> keystone [label = "Authenticate"];
keystone -> keystone [label = "Authorize"];
useragent <- keystone [label = "Scoped token"];
useragent -> openstack [label = "GET /v2.1/servers"];
digraph {
nodesep=1
node [shape=point]
// first column
useragent_top [label = "User Agent" shape="box"]
useragent_bottom [label = "User Agent" shape="box"]
// 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
@ -147,26 +178,61 @@ SAML2.0
SAML2.0 WebSSO
~~~~~~~~~~~~~~
.. seqdiag::
.. graphviz::
:name: saml2-websso
:alt: Diagram of a standard WebSSO authentication flow.
seqdiag {
edge_length = 325;
default_fontsize = 13;
useragent [label = "User Agent"]; sp [label = "Service Provider"]; idp [label = "Identity Provider"];
useragent -> sp [label = "GET /secure"];
useragent <- sp [label = "HTTP 302
Location: https://idp/auth?
SAMLRequest=req"];
useragent -> idp [label = "GET /auth?SAMLRequest=req"];
idp -> idp [label = "Authenticate"];
useragent <- idp [label = "HTTP 200
SAMLResponse in HTML form"];
useragent -> sp [label = "POST /assertionconsumerservice"];
sp -> sp [label = "Validate"];
useragent <- sp [label = "HTTP 302; Location: /secure"];
useragent -> sp [label = "GET /secure"];
digraph {
nodesep=1
node [shape=point]
// first column
useragent_top [label = "User Agent" shape="box"]
useragent_bottom [label = "User Agent" shape="box"]
// chain rows of the column
useragent_top -> u0 [arrowhead="none" style="dashed"]
u0 -> u1 -> u2 -> u3 -> u4 -> u5 -> u6 [arrowhead="none" style="bold"]
u6 -> useragent_bottom [arrowhead="none" style="dashed"]
// second column
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 -> 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
@ -194,24 +260,59 @@ redirect back to the original resource the user had requested.
SAML2.0 ECP
~~~~~~~~~~~
.. seqdiag::
.. graphviz::
:name: saml2-ecp
:alt: Diagram of a standard ECP authentication flow.
seqdiag {
default_fontsize = 13;
useragent [label = "User Agent"]; sp [label = "Service Provider"]; idp [label = "Identity Provider"];
useragent -> sp [label = "GET /secure"];
useragent <- sp [label = "HTTP 200
SAML Request"];
useragent -> idp [label = "POST /auth
SAML Request"];
idp -> idp [label = "Authenticate"];
useragent <- idp [label = "HTTP 200
SAMLResponse in SOAP"];
useragent -> sp [label = "POST /responseconsumer"];
sp -> sp [label = "Validate"];
useragent <- sp [label = "HTTP 200 /secure"];
digraph {
nodesep=1
node [shape=point]
// first column
useragent_top [label = "User Agent" shape="box"]
useragent_bottom [label = "User Agent" shape="box"]
// chain rows of the column
useragent_top -> u0 [arrowhead="none" style="dashed"]
u0 -> u1 -> u2 -> u3 -> u4 -> u5 [arrowhead="none" style="bold"]
u5 -> useragent_bottom [arrowhead="none" style="dashed"]
// second column
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
@ -231,36 +332,89 @@ WebSSO with keystone and horizon
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. seqdiag::
.. graphviz::
:name: saml2-keystone-horizon
:alt: Diagram of the SAML2.0 WebSSO auth flow specific to horizon, keystone, and the
HTTPD module acting as service provider.
seqdiag {
default_fontsize = 13;
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"];
useragent <- horizon [label = "HTTP 302
Location:
/v3/auth/OS-FEDERATION
/websso/saml2"];
useragent -> httpd [label = "GET /v3/auth/OS-FEDERATION/websso/saml2"];
useragent <- httpd [label = "HTTP 302
Location: https://idp/auth?SAMLRequest=req"];
useragent -> idp [label = "GET /auth"];
idp -> idp [label = "Authenticate"];
useragent <- idp [label = "HTTP 200
SAMLResponse in HTML form"];
useragent -> httpd [label = "POST /assertionconsumerservice"];
httpd -> httpd [label = "Validate"];
useragent <- httpd [label = "HTTP 302
Location: /v3/auth/OS-FEDERATION/websso/saml2"];
useragent -> keystone [label = "GET /v3/auth/OS-FEDERATION/websso/saml2"];
keystone -> keystone [label = "Issue token"];
useragent <- keystone [label = "HTTP 200
HTML form containing unscoped token"];
useragent -> horizon [label = "POST /auth/websso"];
useragent <- horizon [label = "successful login"];
digraph {
nodesep=1
node [shape=point]
// first column
useragent_top [label = "User Agent" shape="box"]
useragent_bottom [label = "User Agent" shape="box"]
// chain rows of the column
useragent_top -> u0 [arrowhead="none" style="dashed"]
u0 -> u1 -> u2 -> u3 -> u4 -> u5 -> u6 -> u7 -> u8 -> u9 -> u10 -> u11 [arrowhead="none" style="bold"]
u11 -> useragent_bottom [arrowhead="none" style="bold"]
// second column
h_top [label = "Horizon" shape="box"]
h_bottom [label = "Horizon" shape="box"]
h_top -> h0 [arrowhead="none" style="dashed"]
h0 -> h1 [arrowhead="none" style="bold"]
h1 -> h2 [arrowhead="none" style="dashed"]
h2 -> h3 [arrowhead="none" style="bold"]
h3 -> h_bottom [arrowhead="none" style="dashed"]
// second column
http_top [label = "Httpd" shape="box"]
http_bottom [label = "Httpd" shape="box"]
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
@ -292,29 +446,68 @@ secure resource it requests from keystone.
Keystone to Keystone
~~~~~~~~~~~~~~~~~~~~
.. seqdiag::
.. graphviz::
:name: keystone-to-keystone
:alt: Diagram of the IdP-initiated auth flow in a keystone-to-keystone model.
seqdiag {
edge_length = 240;
default_fontsize = 13;
useragent [label = "User Agent"]; sp [label = "Service Provider"]; idp [label = "Identity Provider"];
useragent -> idp [label = "POST /v3/auth/tokens"];
idp -> idp [label = "Authenticate"];
useragent <- idp [label = "HTTP 201
X-Subject-Token: token"];
useragent -> idp [label = "POST /v3/auth/OS-FEDERATION/saml2/ecp"];
useragent <- idp [label = "HTTP 201
SAMLResponse in SOAP envelope"];
useragent -> sp [label = "POST /PAOS-url"];
sp -> sp [label = "Validate"];
useragent <- sp [label = "HTTP 302"];
useragent -> sp [label = "GET /v3/OS-FED/.../auth"];
useragent <- sp [label = "HTTP 201
X-Subject-Token: unscoped token"];
useragent -> sp [label = "POST /v3/auth/tokens
(request scoped token)"];
digraph {
nodesep=1
node [shape=point]
// first column
useragent_top [label = "User Agent" shape="box"]
useragent_bottom [label = "User Agent" shape="box"]
// chain rows of the column
useragent_top -> u0 [arrowhead="none" style="dashed"]
u0 -> u1 -> u2 -> u3 -> u4 -> u5 -> u6 -> u7 -> u8 [arrowhead="none" style="bold"]
u8 -> useragent_bottom [arrowhead="none" style="dashed"]
// second column
sp_top [label = "Service Provider" shape="box"]
sp_bottom [label = "Service Provider" shape="box"]
sp_top -> sp0 [arrowhead="none" style="dashed"]
sp0 -> sp1 -> sp2 [arrowhead="none" style="bold"]
sp2 -> sp3 [arrowhead="none" style="dashed"]
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
@ -339,31 +532,67 @@ OpenID Connect
OpenID Connect Authentication Flow
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. seqdiag::
.. graphviz::
:name: openidc
:alt: Diagram of a standard OpenID Connect authentication flow
:align: left
seqdiag {
edge_length = 330;
default_fontsize = 13;
useragent [label = "User Agent"]; sp [label = "Service Provider"]; idp [label = "Identity Provider"];
useragent -> sp [label = "GET /secure"];
useragent <- sp [label = "HTTP 302
Location: https://idp/auth?
client_id=XXX&redirect_uri=https://sp/secure"];
useragent -> idp [label = "GET /auth?client_id=XXX&redirect_uri=https://sp/secure"];
idp -> idp [label = "Authenticate"];
useragent <- idp [label = "HTTP 302
Location: https://sp/auth?code=XXX"];
useragent -> sp [label = "GET /auth?code=XXX"];
sp -> idp [label = "POST https://idp/token
code=XXX&redirect_uri=https://sp/secure"];
sp <- idp [label = "HTTP 200
{\"access_code\": \"XXX\",
\"id_token\": \"XXX\"}"];
useragent <- sp [label = "HTTP 302; Location: /secure"];
useragent -> sp [label = "GET /secure"];
digraph auth {
nodesep=1
node [shape=point]
// first column
useragent_top [label = "User Agent" shape="box"]
useragent_bottom [label = "User Agent" shape="box"]
// chain rows of the column
useragent_top -> u0 [arrowhead="none" style="dashed"]
u0 -> u1 -> u2 -> u3 -> u4 -> u5 -> u6 [arrowhead="none" style="bold"]
u6 -> useragent_bottom [arrowhead="none" style="dashed"]
// second column
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 -> sp5 [arrowhead="none" style="bold"]
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
@ -382,46 +611,92 @@ and exchange it for an ID token.
OpenID Connect with keystone and horizon
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. seqdiag::
.. graphviz::
:name: oidc-keystone-horizon
:alt: Diagram of the OpenID Connect WebSSO auth flow specific to horizon,
keystone, and the HTTPD module acting as service provider.
seqdiag {
edge_length = 200
default_fontsize = 13;
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"];
useragent <- horizon [label = "HTTP 302
Location:
/v3/auth/OS-FEDERATION
/websso/openid"];
useragent -> httpd [label = "GET /v3/auth/OS-FEDERATION/websso/openid"];
useragent <- httpd [label = "HTTP 302
Location:
https://idp/auth?
client_id=XXX&
redirect_uri=https://sp/v3/auth/OS-FEDERATION/websso"];
useragent -> idp [label = "GET /auth?client_id=XXX&
redirect_uri=https://sp/v3/auth/OS-FEDERATION/websso"];
idp -> idp [label = "Authenticate"];
useragent <- idp [label = "HTTP 302
Location: https://sp/v3/auth/OS-FEDERATION/websso"];
useragent -> httpd [label = "GET /v3/auth/OS-FEDERATION/websso"];
httpd -> idp [label = "POST https://idp/token
code=XXX&
redirect_uri=https://sp/v3/auth/OS-FEDERATION/websso"];
httpd <- idp [label = "HTTP 200
{\"access_code\": \"XXX\",
\"id_token\": \"XXX\"}"];
useragent <- httpd [label = "HTTP 302
Location: /v3/auth/OS-FEDERATION/websso/mapped"];
useragent -> keystone [label = "GET /v3/auth/OS-FEDERATION/websso/mapped"];
keystone -> keystone [label = "Issue token"];
useragent <- keystone [label = "HTTP 200
HTML form containing unscoped token"];
useragent -> horizon [label = "POST /auth/websso"];
useragent <- horizon [label = "successful login"];
digraph {
nodesep=1
node [shape=point]
// first column
useragent_top [label = "User Agent" shape="box"]
useragent_bottom [label = "User Agent" shape="box"]
// chain rows of the column
useragent_top -> u0 [arrowhead="none" style="dashed"]
u0 -> u1 -> u2 -> u3 -> u4 -> u5 -> u6 -> u7 -> u8 -> u9 -> u10 -> u11 [arrowhead="none" style="bold"]
u11 -> useragent_bottom [arrowhead="none" style="dashed"]
// second column
h_top [label = "Horizon" shape="box"]
h_bottom [label = "Horizon" shape="box"]
h_top -> h0 [arrowhead="none" style="dashed"]
h0 -> h1 [arrowhead="none" style="bold"]
h1 -> h2 [arrowhead="none" style="dashed"]
h2 -> h3 [arrowhead="none" style="bold"]
h3 -> h_bottom [arrowhead="none" style="dashed"]
http_top [label = "Httpd" shape="box"]
http_bottom [label = "Httpd" shape="box"]
http_top -> http0 [arrowhead="none" style="dashed"]
http0 -> http1 [arrowhead="none" style="bold"]
http1 -> http2 [arrowhead="none" style="dashed"]
http2 -> http3 -> http4 -> http5 [arrowhead="none" style="bold"]
http5 -> http_bottom [arrowhead="none" style="dashed"]
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 -> 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

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
`limit` and `usage` as `l` and `u`, respectively:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (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
``UPDATE LIMIT on Alpha to 20``:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=20, u=0)", textcolor = "#00af00"];
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`
is a sub-project of both `Beta` and `Alpha`.
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=20, 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
default limit imposed by an existing registered limit:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (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``:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=30, u=0)", textcolor = "#00af00"];
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``:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=30, u=0)"];
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
used within the entire hierarchy, you can ``UPDATE LIMIT on Alpha to 0``:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha [label="Alpha (l=0, u=0)", textcolor = "#00af00"];
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
usage is currently 4:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
@ -337,10 +345,11 @@ usage is currently 4:
Technically, both `Beta` and `Charlie` can use up to 8 cores each:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
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
by `Alpha.limit`:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
@ -372,10 +382,11 @@ by `Alpha.limit`:
Despite the usage of the tree being equal to the limit, we can still add
children to the tree:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
Alpha -> Charlie;
@ -390,10 +401,11 @@ children to the tree:
Even though the project can be created, the current usage of cores across the
tree prevents `Delta` from claiming any cores:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
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
two-level hierarchical constraint:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
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
project-specific override for cores
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
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
`Alpha`. `Beta` can claim cores if they are released from `Alpha` or `Charlie`:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
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"];
}
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
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
had:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
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
since the total usage of the tree would be capped at the limit of the parent:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
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)"];
}
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
Alpha -> Beta;
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
we're going to create project `Alpha` with a limit of 6 cores.
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
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
since no single child limit should exceed the limit of the parent:
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
orientation = portrait;
node [shape=box]
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
project `Alpha`.
.. blockdiag::
.. graphviz::
blockdiag {
digraph {
node [shape=box]
orientation = portrait;
Alpha -> Beta;

View File

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