Initial commit of the keystone-kerberos charm
Original authors: Camille Rodriguez Aymen Frikha
This commit is contained in:
commit
bebba096e3
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
.tox
|
||||
.stestr
|
||||
*__pycache__*
|
||||
*.pyc
|
||||
build
|
||||
src/tests/bundles/overlays/local-charm-overlay.yaml.j2
|
||||
interfaces
|
||||
layers
|
||||
.idea
|
4
.gitreview
Normal file
4
.gitreview
Normal file
@ -0,0 +1,4 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/charm-keystone-kerberos.git
|
3
.stestr.conf
Normal file
3
.stestr.conf
Normal file
@ -0,0 +1,3 @@
|
||||
[DEFAULT]
|
||||
test_path=./unit_tests
|
||||
top_dir=./
|
6
.travis.yml
Normal file
6
.travis.yml
Normal file
@ -0,0 +1,6 @@
|
||||
language: python
|
||||
python:
|
||||
- "3.6"
|
||||
install: pip install tox-travis
|
||||
script:
|
||||
- tox -e pep8,py3
|
4
.zuul.yaml
Normal file
4
.zuul.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
- project:
|
||||
templates:
|
||||
- python35-charm-jobs
|
||||
- openstack-python3-train-jobs
|
201
LICENSE
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# This file is managed centrally by release-tools and should not be modified
|
||||
# within individual charm repos. See the 'global' dir contents for available
|
||||
# choices of *requirements.txt files for OpenStack Charms:
|
||||
# https://github.com/openstack-charmers/release-tools
|
||||
#
|
||||
# Build requirements
|
||||
charm-tools>=2.4.4
|
||||
simplejson
|
111
src/README.md
Normal file
111
src/README.md
Normal file
@ -0,0 +1,111 @@
|
||||
# keystone-kerberos
|
||||
|
||||
This subordinate charm provides a way to authenticate in Openstack for
|
||||
a specific domain with a Kerberos ticket. This provides an additional
|
||||
security layer. An external Kerberos server is necessary.
|
||||
|
||||
The following documentation is useful to understand better the charm
|
||||
implementation:
|
||||
|
||||
* https://www.objectif-libre.com/fr/blog/2018/02/26/kerberos-authentication-for-keystone/
|
||||
* https://jaosorior.dev/2018/keberos-for-keystone-with-mod_auth_gssapi/
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
Use this charm with the Keystone and Keystone-LDAP charms:
|
||||
|
||||
juju deploy keystone
|
||||
juju deploy keystone-ldap
|
||||
juju deploy openstack-dashboard
|
||||
juju deploy keystone-kerberos
|
||||
juju add-relation keystone keystone-ldap
|
||||
juju add-relation keystone openstack-dashboard
|
||||
juju add-relation keystone keystone-kerberos
|
||||
|
||||
In a bundle:
|
||||
|
||||
```
|
||||
applications
|
||||
# ...
|
||||
keystone-kerberos:
|
||||
charm: ../../../keystone-kerberos
|
||||
num_units: 0
|
||||
options:
|
||||
kerberos-realm: "PROJECT.SERVERSTACK"
|
||||
kerberos-server: "freeipa.project.serverstack"
|
||||
kerberos-domain: "k8s"
|
||||
resources:
|
||||
keystone_keytab: "/home/ubuntu/keystone.keytab"
|
||||
relations:
|
||||
# ...
|
||||
- - keystone
|
||||
- keystone-kerberos
|
||||
```
|
||||
|
||||
# Prerequisites
|
||||
|
||||
To authenticate against Keystone and Kerberos from a host, the following
|
||||
librairies need to be installed :
|
||||
- sudo apt install krb5-user gcc python-dev libkrb5-dev python-pip
|
||||
- pip install keystoneauth1[kerberos]
|
||||
|
||||
# Configuration
|
||||
|
||||
In the Kerberos server, a service must be created for the Keystone Principal.
|
||||
For example, first find the hostname of the keystone server :
|
||||
|
||||
ubuntu@keystone-server$ hostname -f
|
||||
keystone-server.project.serverstack
|
||||
|
||||
Note 1 : make sure that your keystone server can resolve the Kerberos server
|
||||
hostname. If if can't, consider adding an entry to /etc/hosts.
|
||||
|
||||
Then, in the Kerberos server, create the host and service (this example is
|
||||
based on a FreeIPA Kerberos Server):
|
||||
|
||||
ipa host-add keystone-server.project.serverstack --ip-adress=10.0.0.2
|
||||
ipa service-add HTTP/keystone-server.project.serverstack
|
||||
ipa service-add-host HTTP/keystone-server.project.serverstack --hosts=keystone-server.project.serverstack
|
||||
|
||||
Note 2 : If you have multiple keystone servers, you should add each host to
|
||||
the principal with the command
|
||||
|
||||
ipa host-add-principal keystone-server HTTP/<keystone-other-hostname>@PROJECT.SERVERSTACK
|
||||
|
||||
Retrieve the keytab associated with this service:
|
||||
|
||||
ipa-getkeytab -p HTTP/keystone-server.project.serverstack -k keystone.keytab
|
||||
|
||||
This is the keytab needed in the resources of the keystone-kerberos charm. If
|
||||
you retrieved it post-deploy, you can attach it with a command to keystone:
|
||||
|
||||
juju attach-resource keystone-kerberos/0 keystone_keytab=new_keytab.keytab
|
||||
|
||||
# Authentication from a host
|
||||
|
||||
To use the Openstack cli, two steps are required.
|
||||
1) Retrieve a token for an existing user in the Kerberos/LDAP directory:
|
||||
```
|
||||
kinit <username>
|
||||
```
|
||||
2) Source the openstack rc file with the correct information:
|
||||
```
|
||||
cat k8s-user.rc
|
||||
export OS_AUTH_URL=http://kerberos-server.project.serverstack:5000/krb/v3
|
||||
export OS_PROJECT_ID=<projectID>
|
||||
export OS_PROJECT_NAME=<kerberos_domain> # i.e k8s
|
||||
export OS_PROJECT_DOMAIN_ID=<domainID>
|
||||
export OS_REGION_NAME="RegionOne"
|
||||
export OS_INTERFACE=public
|
||||
export OS_IDENTITY_API_VERSION=3
|
||||
export OS_AUTH_TYPE=v3kerberos
|
||||
|
||||
source k8s-user.rc
|
||||
openstack token issue
|
||||
```
|
||||
|
||||
# Bugs
|
||||
Please report bugs on [Launchpad](link missing).
|
||||
|
||||
For general questions please refer to the OpenStack [Charm Guide](https://docs.openstack.org/charm-guide/latest/).
|
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
25
src/config.yaml
Normal file
25
src/config.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
options:
|
||||
display-name:
|
||||
type: string
|
||||
default: 'keystone-kerberos'
|
||||
description: Custom name for UI
|
||||
kerberos-realm:
|
||||
type: string
|
||||
default: ""
|
||||
description: |
|
||||
The domain over which a Kerberos authentication server has the authority
|
||||
to authenticate a user, host or service. It is often the upper case
|
||||
version fo the name of the DNS domain over which is presides,
|
||||
i.e REALM.COM. Must be in caps.
|
||||
kerberos-server:
|
||||
type: string
|
||||
default: ""
|
||||
description: |
|
||||
Kerberos server name. Typically, the server name is composed of a name
|
||||
followed by the realm name, i.e, krb5.realm.com
|
||||
kerberos-domain:
|
||||
type: string
|
||||
default: 'k8s'
|
||||
description: |
|
||||
The Openstack domain against which Kerberos authentication should be
|
||||
used.
|
279
src/icon.svg
Normal file
279
src/icon.svg
Normal file
@ -0,0 +1,279 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg6517"
|
||||
version="1.1"
|
||||
inkscape:version="0.48+devel r12274"
|
||||
sodipodi:docname="Juju_charm_icon_template.svg">
|
||||
<defs
|
||||
id="defs6519">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#Background"
|
||||
id="linearGradient6461"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="0"
|
||||
y1="970.29498"
|
||||
x2="144"
|
||||
y2="970.29498"
|
||||
gradientTransform="matrix(0,-0.66666669,0.6660448,0,-866.25992,731.29077)" />
|
||||
<linearGradient
|
||||
id="Background">
|
||||
<stop
|
||||
id="stop4178"
|
||||
offset="0"
|
||||
style="stop-color:#b8b8b8;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4180"
|
||||
offset="1"
|
||||
style="stop-color:#c9c9c9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Inner Shadow"
|
||||
id="filter1121">
|
||||
<feFlood
|
||||
flood-opacity="0.59999999999999998"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1123" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="out"
|
||||
result="composite1"
|
||||
id="feComposite1125" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="1"
|
||||
result="blur"
|
||||
id="feGaussianBlur1127" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="2"
|
||||
result="offset"
|
||||
id="feOffset1129" />
|
||||
<feComposite
|
||||
in="offset"
|
||||
in2="SourceGraphic"
|
||||
operator="atop"
|
||||
result="composite2"
|
||||
id="feComposite1131" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter950">
|
||||
<feFlood
|
||||
flood-opacity="0.25"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood952" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite954" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="1"
|
||||
result="blur"
|
||||
id="feGaussianBlur956" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="1"
|
||||
result="offset"
|
||||
id="feOffset958" />
|
||||
<feComposite
|
||||
in="SourceGraphic"
|
||||
in2="offset"
|
||||
operator="over"
|
||||
result="composite2"
|
||||
id="feComposite960" />
|
||||
</filter>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath873">
|
||||
<g
|
||||
transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)"
|
||||
id="g875"
|
||||
inkscape:label="Layer 1"
|
||||
style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline">
|
||||
<path
|
||||
style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"
|
||||
d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z"
|
||||
id="path877"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssssss" />
|
||||
</g>
|
||||
</clipPath>
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter891"
|
||||
inkscape:label="Badge Shadow">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.71999962"
|
||||
id="feGaussianBlur893" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4.0745362"
|
||||
inkscape:cx="18.514671"
|
||||
inkscape:cy="49.018169"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1029"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
showborder="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:showpageshadow="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid821" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="16,48"
|
||||
id="guide823" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="64,80"
|
||||
id="guide825" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="80,40"
|
||||
id="guide827" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="64,16"
|
||||
id="guide829" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata6522">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="BACKGROUND"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(268,-635.29076)"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="fill:url(#linearGradient6461);fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)"
|
||||
d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z"
|
||||
id="path6455"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssssss" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="PLACE YOUR PICTOGRAM HERE"
|
||||
style="display:inline" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="BADGE"
|
||||
style="display:none"
|
||||
sodipodi:insensitive="true">
|
||||
<g
|
||||
style="display:inline"
|
||||
transform="translate(-340.00001,-581)"
|
||||
id="g4394"
|
||||
clip-path="none">
|
||||
<g
|
||||
id="g855">
|
||||
<g
|
||||
inkscape:groupmode="maskhelper"
|
||||
id="g870"
|
||||
clip-path="url(#clipPath873)"
|
||||
style="opacity:0.6;filter:url(#filter891)">
|
||||
<path
|
||||
transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)"
|
||||
d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
|
||||
sodipodi:ry="12"
|
||||
sodipodi:rx="12"
|
||||
sodipodi:cy="552.36218"
|
||||
sodipodi:cx="252"
|
||||
id="path844"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:type="arc" />
|
||||
</g>
|
||||
<g
|
||||
id="g862">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path4398"
|
||||
sodipodi:cx="252"
|
||||
sodipodi:cy="552.36218"
|
||||
sodipodi:rx="12"
|
||||
sodipodi:ry="12"
|
||||
d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
|
||||
transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" />
|
||||
<path
|
||||
transform="matrix(1.25,0,0,1.25,33,-100.45273)"
|
||||
d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
|
||||
sodipodi:ry="12"
|
||||
sodipodi:rx="12"
|
||||
sodipodi:cy="552.36218"
|
||||
sodipodi:cx="252"
|
||||
id="path4400"
|
||||
style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path4459"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="666.19574"
|
||||
sodipodi:cy="589.50385"
|
||||
sodipodi:r1="7.2431178"
|
||||
sodipodi:r2="4.3458705"
|
||||
sodipodi:arg1="1.0471976"
|
||||
sodipodi:arg2="1.6755161"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0.1"
|
||||
inkscape:randomized="0"
|
||||
d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 C 669.9821,591.68426 670.20862,595.55064 669.8173,595.77657 Z"
|
||||
transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.6 KiB |
6
src/layer.yaml
Normal file
6
src/layer.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
includes: ['layer:openstack', 'interface:keystone-fid-service-provider', 'interface:juju-info']
|
||||
repo: https://github.com/camille-rodriguez/keystone-kerberos.git
|
||||
options:
|
||||
basic:
|
||||
use_venv: True
|
||||
include_system_packages: False
|
0
src/lib/charm/openstack/__init__.py
Normal file
0
src/lib/charm/openstack/__init__.py
Normal file
203
src/lib/charm/openstack/keystone_kerberos.py
Normal file
203
src/lib/charm/openstack/keystone_kerberos.py
Normal file
@ -0,0 +1,203 @@
|
||||
#
|
||||
# Copyright 2017 Canonical Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import charmhelpers.core as core
|
||||
import charmhelpers.core.host as ch_host
|
||||
import charmhelpers.core.hookenv as hookenv
|
||||
|
||||
import charmhelpers.contrib.openstack.templating as os_templating
|
||||
import charmhelpers.contrib.openstack.utils as os_utils
|
||||
|
||||
import charms_openstack.charm
|
||||
import charms_openstack.adapters
|
||||
# release detection is done via keystone package given that
|
||||
# openstack-origin is not present in the subordinate charm
|
||||
# see https://github.com/juju/charm-helpers/issues/83
|
||||
import charmhelpers.core.unitdata as unitdata
|
||||
from charms_openstack.charm.core import (
|
||||
register_os_release_selector
|
||||
)
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
OPENSTACK_RELEASE_KEY = 'charmers.openstack-release-version'
|
||||
APACHE_CONF_TEMPLATE = "apache-kerberos.conf"
|
||||
APACHE_LOCATION = '/etc/apache2/kerberos'
|
||||
KERBEROS_CONF_TEMPLATE = "krb5.conf"
|
||||
KEYTAB_DESTINATION_PATH = "/etc/keystone.keytab"
|
||||
|
||||
|
||||
@register_os_release_selector
|
||||
def select_release():
|
||||
"""Determine the release based on the keystone package version.
|
||||
|
||||
Note that this function caches the release after the first install so
|
||||
that it doesn't need to keep going and getting it from the package
|
||||
information.
|
||||
"""
|
||||
release_version = unitdata.kv().get(OPENSTACK_RELEASE_KEY, None)
|
||||
if release_version is None:
|
||||
release_version = os_utils.os_release('keystone')
|
||||
unitdata.kv().set(OPENSTACK_RELEASE_KEY, release_version)
|
||||
return release_version
|
||||
|
||||
|
||||
class KeystoneKerberosConfigurationAdapter(
|
||||
charms_openstack.adapters.ConfigurationAdapter):
|
||||
|
||||
def __init__(self, charm_instance=None):
|
||||
super().__init__(charm_instance=charm_instance)
|
||||
self._keytab_path = None
|
||||
self._protocol_name = None
|
||||
|
||||
@property
|
||||
def keytab_path(self):
|
||||
"""Path for they keytab file"""
|
||||
keytab_file = hookenv.resource_get('keystone_keytab')
|
||||
if keytab_file:
|
||||
shutil.copy(keytab_file, KEYTAB_DESTINATION_PATH)
|
||||
self._keytab_path = keytab_file
|
||||
return self._keytab_path
|
||||
|
||||
@property
|
||||
def protocol_name(self):
|
||||
"""Protocol name to be used in the auth methods via
|
||||
fid-service-provider interface
|
||||
|
||||
:returns: string: containing the protocol name
|
||||
"""
|
||||
self._protocol_name = 'kerberos'
|
||||
return self._protocol_name
|
||||
|
||||
|
||||
class KeystoneKerberosCharm(charms_openstack.charm.OpenStackCharm):
|
||||
|
||||
# Internal name of charm
|
||||
|
||||
service_name = name = 'keystone-kerberos'
|
||||
|
||||
# Package to derive application version from
|
||||
|
||||
version_package = 'keystone'
|
||||
|
||||
# First release supported
|
||||
|
||||
release = 'queens'
|
||||
|
||||
release_pkg = 'keystone-common'
|
||||
|
||||
# Required relations
|
||||
|
||||
required_relations = [
|
||||
'keystone-fid-service-provider']
|
||||
|
||||
# List of packages to install for this charm
|
||||
|
||||
packages = ['libapache2-mod-auth-kerb']
|
||||
|
||||
configuration_class = KeystoneKerberosConfigurationAdapter
|
||||
|
||||
restart_map = {
|
||||
APACHE_CONF_TEMPLATE: [],
|
||||
KERBEROS_CONF_TEMPLATE: [],
|
||||
KEYTAB_DESTINATION_PATH: [],
|
||||
}
|
||||
|
||||
@property
|
||||
def kerberos_realm(self):
|
||||
"""Realm name for the running application
|
||||
|
||||
:returns: string: containing the realm name for the application
|
||||
"""
|
||||
return hookenv.config('kerberos-realm')
|
||||
|
||||
@property
|
||||
def kerberos_server(self):
|
||||
"""Server name for the running application
|
||||
|
||||
:returns: string: containing the server name for the application
|
||||
"""
|
||||
return hookenv.config('kerberos-server')
|
||||
|
||||
@property
|
||||
def kerberos_domain(self):
|
||||
"""Server name for the running application
|
||||
|
||||
:returns: string: containing the server name for the application
|
||||
"""
|
||||
return hookenv.config('kerberos-domain')
|
||||
|
||||
def configuration_complete(self):
|
||||
"""Determine whether sufficient configuration has been provided
|
||||
to configure keystone for use with a Kerberos server
|
||||
|
||||
:returns: boolean indicating whether configuration is complete
|
||||
"""
|
||||
required_config = {
|
||||
'kerberos_realm': self.options.kerberos_realm,
|
||||
'kerberos_server': self.options.kerberos_server,
|
||||
'kerberos_domain': self.options.kerberos_domain,
|
||||
'keytab_path': self.options.keytab_path,
|
||||
}
|
||||
return all(required_config.values())
|
||||
|
||||
@property
|
||||
def kerb_conf_path(self):
|
||||
return '/kerberos'
|
||||
|
||||
def assess_status(self):
|
||||
"""Determine the current application status for the charm"""
|
||||
if not self.configuration_complete():
|
||||
hookenv.status_set('blocked',
|
||||
'Kerberos configuration incomplete')
|
||||
elif os_utils.is_unit_upgrading_set():
|
||||
hookenv.status_set('blocked',
|
||||
'Ready for do-release-upgrade and reboot. '
|
||||
'Set complete when finished.')
|
||||
else:
|
||||
hookenv.status_set('active',
|
||||
'Unit is ready')
|
||||
|
||||
def render_config(self, *args):
|
||||
"""
|
||||
Render Kerberos configuration file and Apache configuration to be used
|
||||
by Keystone.
|
||||
"""
|
||||
# ensure that a directory we need is there
|
||||
ch_host.mkdir(APACHE_LOCATION)
|
||||
|
||||
self.render_configs(self.string_templates.keys())
|
||||
|
||||
core.templating.render(
|
||||
source=APACHE_CONF_TEMPLATE,
|
||||
template_loader=os_templating.get_loader(
|
||||
'templates/', self.release),
|
||||
target='{}/{}'.format(APACHE_LOCATION, APACHE_CONF_TEMPLATE),
|
||||
context=self.adapters_class(args, charm_instance=self),
|
||||
)
|
||||
|
||||
core.templating.render(
|
||||
source=KERBEROS_CONF_TEMPLATE,
|
||||
template_loader=os_templating.get_loader(
|
||||
'templates/', self.release),
|
||||
target="/etc/krb5.conf",
|
||||
context=self.adapters_class(args, charm_instance=self),
|
||||
)
|
||||
|
||||
def remove_config(self):
|
||||
for f in self.restart_map.keys():
|
||||
if os.path.exists(f):
|
||||
os.unlink(f)
|
28
src/metadata.yaml
Normal file
28
src/metadata.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
name: keystone-kerberos
|
||||
summary: Keystone backend for kerberos authentication
|
||||
maintainer: OpenStack Charmers <openstack-charmers@lists.ubuntu.com>
|
||||
description: |
|
||||
This charm supports the use of Kerberos as a security mechanism
|
||||
for authentication through keystone.
|
||||
tags:
|
||||
- openstack
|
||||
- identity
|
||||
- kerberos
|
||||
- ldap
|
||||
series:
|
||||
- bionic
|
||||
- eoan
|
||||
subordinate: true
|
||||
provides:
|
||||
keystone-fid-service-provider:
|
||||
interface: keystone-fid-service-provider
|
||||
scope: container
|
||||
requires:
|
||||
container:
|
||||
interface: juju-info
|
||||
scope: container
|
||||
resources:
|
||||
keystone_keytab:
|
||||
type: file
|
||||
filename: keystone.keytab
|
||||
description: Path to the kerberos keytab for keystone service
|
61
src/reactive/keystone_kerberos_handlers.py
Normal file
61
src/reactive/keystone_kerberos_handlers.py
Normal file
@ -0,0 +1,61 @@
|
||||
#
|
||||
# Copyright 2020 Canonical Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import charms_openstack.bus
|
||||
charms_openstack.bus.discover()
|
||||
|
||||
|
||||
import charms_openstack.charm as charm
|
||||
import charms.reactive as reactive
|
||||
|
||||
charm.use_defaults(
|
||||
'charm.installed',
|
||||
'update-status',
|
||||
'upgrade-charm')
|
||||
|
||||
|
||||
@reactive.when_not('keystone-fid-service-provider.connected')
|
||||
def keystone_departed():
|
||||
"""
|
||||
Service restart should be handled on the keystone side
|
||||
in this case.
|
||||
"""
|
||||
with charm.provide_charm_instance() as charm_instance:
|
||||
charm_instance.remove_config()
|
||||
|
||||
|
||||
@reactive.when('keystone-fid-service-provider.connected')
|
||||
def publish_sp_fid(fid_sp):
|
||||
with charm.provide_charm_instance() as charm_instance:
|
||||
fid_sp.publish(charm_instance.options.protocol_name,
|
||||
charm_instance.options.kerberos_server)
|
||||
|
||||
|
||||
@reactive.when('keystone-fid-service-provider.available')
|
||||
def render_config(fid_sp):
|
||||
with charm.provide_charm_instance() as charm_instance:
|
||||
if charm_instance.configuration_complete():
|
||||
charm_instance.render_config(fid_sp)
|
||||
# Trigger keystone restart. The relation is container-scoped
|
||||
# so a per-unit db of a remote unit will only contain a nonce
|
||||
# of a single subordinate
|
||||
if reactive.any_file_changed(charm_instance.restart_map.keys()):
|
||||
fid_sp.request_restart()
|
||||
|
||||
|
||||
@reactive.when_not('always.run')
|
||||
def assess_status():
|
||||
with charm.provide_charm_instance() as charm_instance:
|
||||
charm_instance.assess_status()
|
15
src/templates/apache-kerberos.conf
Normal file
15
src/templates/apache-kerberos.conf
Normal file
@ -0,0 +1,15 @@
|
||||
<Location "/krb/v3/auth/tokens">
|
||||
LogLevel trace8
|
||||
AuthType Kerberos
|
||||
AuthName "Kerberos Login"
|
||||
KrbMethodNegotiate On
|
||||
KrbMethodK5Passwd Off
|
||||
KrbServiceName HTTP
|
||||
KrbAuthRealms {{ options.kerberos_realm }}
|
||||
Krb5KeyTab {{ options.keytab_path }}
|
||||
KrbVerifyKDC Off
|
||||
KrbLocalUserMapping On
|
||||
KrbAuthoritative On
|
||||
Require valid-user
|
||||
SetEnv REMOTE_DOMAIN {{ options.kerberos_domain }}
|
||||
</Location>
|
21
src/templates/krb5.conf
Normal file
21
src/templates/krb5.conf
Normal file
@ -0,0 +1,21 @@
|
||||
[libdefaults]
|
||||
default_realm = {{ options.kerberos_realm }}
|
||||
|
||||
# The following krb5.conf variables are only for MIT Kerberos.
|
||||
kdc_timesync = 1
|
||||
ccache_type = 4
|
||||
forwardable = true
|
||||
proxiable = true
|
||||
|
||||
|
||||
fcc-mit-ticketflags = true
|
||||
|
||||
[realms]
|
||||
{{ options.kerberos_realm }} = {
|
||||
kdc = {{ options.kerberos_server }}
|
||||
admin_server = {{ options.kerberos_server }}
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
.{% filter lower %}{{ options.kerberos_realm }}{% endfilter %} = {{ options.kerberos_realm }}
|
||||
{% filter lower %}{{ options.kerberos_realm }}{% endfilter %} = {{ options.kerberos_realm }}
|
8
src/test-requirements.txt
Normal file
8
src/test-requirements.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# This file is managed centrally by release-tools and should not be modified
|
||||
# within individual charm repos. See the 'global' dir contents for available
|
||||
# choices of *requirements.txt files for OpenStack Charms:
|
||||
# https://github.com/openstack-charmers/release-tools
|
||||
#
|
||||
# Functional Test Requirements (let Zaza's dependencies solve all dependencies here!)
|
||||
git+https://github.com/openstack-charmers/zaza.git#egg=zaza
|
||||
git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack
|
50
src/tox.ini
Normal file
50
src/tox.ini
Normal file
@ -0,0 +1,50 @@
|
||||
# Source charm (with zaza): ./src/tox.ini
|
||||
# This file is managed centrally by release-tools and should not be modified
|
||||
# within individual charm repos. See the 'global' dir contents for available
|
||||
# choices of tox.ini for OpenStack Charms:
|
||||
# https://github.com/openstack-charmers/release-tools
|
||||
|
||||
[tox]
|
||||
envlist = pep8
|
||||
skipsdist = True
|
||||
# NOTE: Avoid build/test env pollution by not enabling sitepackages.
|
||||
sitepackages = False
|
||||
# NOTE: Avoid false positives by not skipping missing interpreters.
|
||||
skip_missing_interpreters = False
|
||||
|
||||
[testenv]
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
PYTHONHASHSEED=0
|
||||
whitelist_externals = juju
|
||||
passenv = HOME TERM CS_* OS_* TEST_*
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
install_command =
|
||||
pip install {opts} {packages}
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
deps=charm-tools
|
||||
commands = charm-proof
|
||||
|
||||
[testenv:func-noop]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --help
|
||||
|
||||
[testenv:func]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --keep-model
|
||||
|
||||
[testenv:func-smoke]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --keep-model --smoke
|
||||
|
||||
[testenv:func-target]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --keep-model --bundle {posargs}
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
13
test-requirements.txt
Normal file
13
test-requirements.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# This file is managed centrally. If you find the need to modify this as a
|
||||
# one-off, please don't. Intead, consult #openstack-charms and ask about
|
||||
# requirements management in charms via bot-control. Thank you.
|
||||
#
|
||||
# Lint and unit test requirements
|
||||
flake8>=2.2.4,<=2.4.1
|
||||
stestr>=2.2.0
|
||||
requests>=2.18.4
|
||||
charms.reactive
|
||||
mock>=1.2
|
||||
nose>=1.3.7
|
||||
coverage>=3.6
|
||||
git+https://github.com/openstack/charms.openstack.git#egg=charms.openstack
|
92
tox.ini
Normal file
92
tox.ini
Normal file
@ -0,0 +1,92 @@
|
||||
# Source charm: ./tox.ini
|
||||
# This file is managed centrally by release-tools and should not be modified
|
||||
# within individual charm repos. See the 'global' dir contents for available
|
||||
# choices of tox.ini for OpenStack Charms:
|
||||
# https://github.com/openstack-charmers/release-tools
|
||||
|
||||
[tox]
|
||||
skipsdist = True
|
||||
envlist = pep8,py3
|
||||
# NOTE: Avoid build/test env pollution by not enabling sitepackages.
|
||||
sitepackages = False
|
||||
# NOTE: Avoid false positives by not skipping missing interpreters.
|
||||
skip_missing_interpreters = False
|
||||
|
||||
[testenv]
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
PYTHONHASHSEED=0
|
||||
TERM=linux
|
||||
LAYER_PATH={toxinidir}/layers
|
||||
INTERFACE_PATH={toxinidir}/interfaces
|
||||
JUJU_REPOSITORY={toxinidir}/build
|
||||
passenv = http_proxy https_proxy INTERFACE_PATH LAYER_PATH JUJU_REPOSITORY
|
||||
install_command =
|
||||
pip install {opts} {packages}
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
|
||||
[testenv:build]
|
||||
basepython = python3
|
||||
commands =
|
||||
charm-build --log-level DEBUG -o {toxinidir}/build src {posargs}
|
||||
|
||||
[testenv:py3]
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:py35]
|
||||
basepython = python3.5
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:py36]
|
||||
basepython = python3.6
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:py37]
|
||||
basepython = python3.7
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = flake8 {posargs} src unit_tests
|
||||
|
||||
[testenv:cover]
|
||||
# Technique based heavily upon
|
||||
# https://github.com/openstack/nova/blob/master/tox.ini
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
setenv =
|
||||
{[testenv]setenv}
|
||||
PYTHON=coverage run
|
||||
commands =
|
||||
coverage erase
|
||||
stestr run --slowest {posargs}
|
||||
coverage combine
|
||||
coverage html -d cover
|
||||
coverage xml -o cover/coverage.xml
|
||||
coverage report
|
||||
|
||||
[coverage:run]
|
||||
branch = True
|
||||
concurrency = multiprocessing
|
||||
parallel = True
|
||||
source =
|
||||
.
|
||||
omit =
|
||||
.tox/*
|
||||
*/charmhelpers/*
|
||||
unit_tests/*
|
||||
|
||||
[testenv:venv]
|
||||
basepython = python3
|
||||
commands = {posargs}
|
||||
|
||||
[flake8]
|
||||
# E402 ignore necessary for path append before sys module import in actions
|
||||
ignore = E402,W504
|
33
unit_tests/__init__.py
Normal file
33
unit_tests/__init__.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright 2019 Canonical Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.append('src')
|
||||
sys.path.append('src/lib')
|
||||
sys.path.append('src/actions')
|
||||
|
||||
# Mock out charmhelpers so that we can test without it.
|
||||
# NOTE: The bellow mocks are to avoid side effects at import time.
|
||||
# Any module that requires testing must be re-mocked before usage
|
||||
# in a unit test.
|
||||
import charms_openstack.test_mocks # noqa
|
||||
charms_openstack.test_mocks.mock_charmhelpers()
|
||||
|
||||
import mock
|
||||
import charms
|
||||
keystoneauth1 = mock.MagicMock()
|
||||
sys.modules['keystoneauth1'] = keystoneauth1
|
||||
charms.leadership = mock.MagicMock()
|
||||
sys.modules['charms.leadership'] = charms.leadership
|
118
unit_tests/test_keystone_kerberos_handlers.py
Normal file
118
unit_tests/test_keystone_kerberos_handlers.py
Normal file
@ -0,0 +1,118 @@
|
||||
# Copyright 2019 Canonical Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
|
||||
import mock
|
||||
|
||||
import charm.openstack.keystone_kerberos as keystone_kerberos
|
||||
import reactive.keystone_kerberos_handlers as handlers
|
||||
|
||||
import charms_openstack.test_utils as test_utils
|
||||
|
||||
|
||||
class TestRegisteredHooks(test_utils.TestRegisteredHooks):
|
||||
|
||||
def test_hooks(self):
|
||||
defaults = [
|
||||
'charm.installed',
|
||||
'update-status']
|
||||
hook_set = {
|
||||
'hook': {
|
||||
'default_upgrade_charm': ('upgrade-charm',),
|
||||
},
|
||||
'when': {
|
||||
'publish_sp_fid': (
|
||||
'keystone-fid-service-provider.connected',),
|
||||
'render_config': (
|
||||
'keystone-fid-service-provider.available',),
|
||||
},
|
||||
'when_not': {
|
||||
'keystone_departed': (
|
||||
'keystone-fid-service-provider.connected',),
|
||||
'assess_status': ('always.run',),
|
||||
},
|
||||
}
|
||||
# test that the hooks were registered via the
|
||||
# reactive.keystone_kerberos_handlers
|
||||
self.registered_hooks_test_helper(handlers, hook_set, defaults)
|
||||
|
||||
|
||||
class TestKeystoneKerberosHandlers(test_utils.PatchHelper):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.patch_release(
|
||||
keystone_kerberos.KeystoneKerberosCharm.release)
|
||||
self.keystone_kerberos_charm = mock.MagicMock()
|
||||
self.patch_object(handlers.charm, 'provide_charm_instance',
|
||||
new=mock.MagicMock())
|
||||
self.provide_charm_instance().__enter__.return_value = (
|
||||
self.keystone_kerberos_charm)
|
||||
self.provide_charm_instance().__exit__.return_value = None
|
||||
|
||||
self.patch_object(handlers.reactive, 'any_file_changed',
|
||||
new=mock.MagicMock())
|
||||
|
||||
self.endpoint = mock.MagicMock()
|
||||
|
||||
self.protocol_name = "kerberos"
|
||||
self.kerberos_realm = "project.serverstack"
|
||||
self.kerberos_server = "freeipa.project.serverstack"
|
||||
self.kerberos_domain = "k8s"
|
||||
self.keystone_kerberos_charm.options.protocol_name = (
|
||||
self.protocol_name)
|
||||
self.keystone_kerberos_charm.options.kerberos_realm = (
|
||||
self.kerberos_realm)
|
||||
self.keystone_kerberos_charm.options.kerberos_server = (
|
||||
self.kerberos_server)
|
||||
self.keystone_kerberos_charm.options.kerberos_domain = (
|
||||
self.kerberos_domain)
|
||||
|
||||
self.all_joined_units = []
|
||||
for i in range(0, 2):
|
||||
unit = mock.MagicMock()
|
||||
unit.name = "keystone-{}".format(i)
|
||||
unit.received = {"hostname": unit.name}
|
||||
self.all_joined_units.append(unit)
|
||||
|
||||
def test_keystone_departed(self):
|
||||
handlers.keystone_departed()
|
||||
self.keystone_kerberos_charm.remove_config.assert_called_once_with()
|
||||
|
||||
def test_publish_sp_fid(self):
|
||||
handlers.publish_sp_fid(self.endpoint)
|
||||
self.endpoint.publish.assert_called_once_with(
|
||||
self.protocol_name, self.kerberos_server)
|
||||
|
||||
def test_render_config(self):
|
||||
# No restart
|
||||
self.any_file_changed.return_value = False
|
||||
(self.keystone_kerberos_charm
|
||||
.configuration_complete.return_value) = True
|
||||
|
||||
handlers.render_config(self.endpoint)
|
||||
self.keystone_kerberos_charm.render_config.assert_called_once_with(
|
||||
self.endpoint)
|
||||
self.endpoint.request_restart.assert_not_called()
|
||||
|
||||
# Restart
|
||||
self.any_file_changed.return_value = True
|
||||
handlers.render_config(self.endpoint)
|
||||
self.endpoint.request_restart.assert_called_once_with()
|
||||
|
||||
def test_assess_status(self):
|
||||
handlers.assess_status()
|
||||
self.keystone_kerberos_charm.assess_status.assert_called_once_with()
|
145
unit_tests/test_lib_charm_openstack_keystone_kerberos.py
Normal file
145
unit_tests/test_lib_charm_openstack_keystone_kerberos.py
Normal file
@ -0,0 +1,145 @@
|
||||
# Copyright 2019 Canonical Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
|
||||
import mock
|
||||
|
||||
import charms_openstack.test_utils as test_utils
|
||||
|
||||
import charm.openstack.keystone_kerberos as keystone_kerberos
|
||||
|
||||
|
||||
def FakeConfig(init_dict):
|
||||
|
||||
def _config(key=None):
|
||||
return init_dict[key] if key else init_dict
|
||||
|
||||
return _config
|
||||
|
||||
|
||||
def FakeResourceGet(init_dict):
|
||||
|
||||
def _config(key=None):
|
||||
return init_dict[key] if key else init_dict
|
||||
|
||||
return _config
|
||||
|
||||
|
||||
class Helper(test_utils.PatchHelper):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.patch_release(
|
||||
keystone_kerberos.KeystoneKerberosCharm.release)
|
||||
|
||||
self.endpoint = mock.MagicMock()
|
||||
|
||||
self.kerberos_realm = "project.serverstack"
|
||||
self.kerberos_server = "freeipa.project.serverstack"
|
||||
self.kerberos_domain = "k8s"
|
||||
self.test_config = {
|
||||
"kerberos-realm": self.kerberos_realm,
|
||||
"kerberos-server": self.kerberos_server,
|
||||
"kerberos-domain": self.kerberos_domain,
|
||||
}
|
||||
self.resources = {
|
||||
"keystone_keytab": "/path/to/keystone.keytab",
|
||||
}
|
||||
self.patch_object(keystone_kerberos.hookenv, 'config',
|
||||
side_effect=FakeConfig(self.test_config))
|
||||
self.patch_object(keystone_kerberos.hookenv, 'resource_get',
|
||||
side_effect=FakeResourceGet(self.resources))
|
||||
self.patch_object(
|
||||
keystone_kerberos.hookenv, 'application_version_set')
|
||||
self.patch_object(keystone_kerberos.hookenv, 'status_set')
|
||||
self.patch_object(keystone_kerberos.ch_host, 'mkdir')
|
||||
self.patch_object(keystone_kerberos.core.templating, 'render')
|
||||
|
||||
self.template_loader = mock.MagicMock()
|
||||
self.patch_object(keystone_kerberos.os_templating, 'get_loader',
|
||||
return_value=self.template_loader)
|
||||
self.patch_object(
|
||||
keystone_kerberos.KeystoneKerberosCharm,
|
||||
'application_version',
|
||||
return_value="1.0.0")
|
||||
|
||||
self.patch_object(
|
||||
keystone_kerberos.KeystoneKerberosCharm, 'render_configs')
|
||||
self.patch_object(keystone_kerberos, 'os')
|
||||
self.patch_object(keystone_kerberos, 'shutil')
|
||||
|
||||
self.patch(
|
||||
"builtins.open", new_callable=mock.mock_open(), name="open")
|
||||
self.file = mock.MagicMock()
|
||||
self.fileobj = mock.MagicMock()
|
||||
self.fileobj.__enter__.return_value = self.file
|
||||
self.open.return_value = self.fileobj
|
||||
|
||||
|
||||
class TestKeystoneKerberosConfigurationAdapter(Helper):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.protocol_name = "kerberos"
|
||||
|
||||
def test_keytab_path(self):
|
||||
self.os.path.exists.return_value = True
|
||||
kkca = keystone_kerberos.KeystoneKerberosConfigurationAdapter()
|
||||
self.assertEqual(
|
||||
kkca.keytab_path, self.resources['keystone_keytab'])
|
||||
|
||||
def test_protocol_name(self):
|
||||
kkca = keystone_kerberos.KeystoneKerberosConfigurationAdapter()
|
||||
self.assertEqual(
|
||||
kkca.protocol_name, self.protocol_name)
|
||||
|
||||
|
||||
class TestKeystoneKerberosCharm(Helper):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.patch_object(
|
||||
keystone_kerberos.KeystoneKerberosConfigurationAdapter,
|
||||
'keytab_path')
|
||||
self.keytab_path.return_value = self.resources["keystone_keytab"]
|
||||
self.keytab_path.__bool__.return_value = True
|
||||
|
||||
def test_configuration_complete(self):
|
||||
kk = keystone_kerberos.KeystoneKerberosCharm()
|
||||
self.assertTrue(kk.configuration_complete())
|
||||
|
||||
# One option not ready
|
||||
self.keytab_path.__bool__.return_value = False
|
||||
self.assertFalse(kk.configuration_complete())
|
||||
|
||||
def test_custom_assess_status_check(self):
|
||||
kk = keystone_kerberos.KeystoneKerberosCharm()
|
||||
self.assertEqual(
|
||||
kk.custom_assess_status_check(),
|
||||
(None, None))
|
||||
|
||||
def test_render_config(self):
|
||||
kk = keystone_kerberos.KeystoneKerberosCharm()
|
||||
kk.render_config()
|
||||
self.assertEqual(self.render_configs.call_count, 1)
|
||||
self.assertEqual(self.render.call_count, 2)
|
||||
|
||||
def test_remove_config(self):
|
||||
self.os.path.exists.return_value = True
|
||||
kk = keystone_kerberos.KeystoneKerberosCharm()
|
||||
kk.remove_config()
|
||||
self.assertEqual(self.os.path.exists.call_count, 3)
|
||||
self.assertEqual(self.os.unlink.call_count, 3)
|
Loading…
Reference in New Issue
Block a user