mirror of
https://github.com/openshift/community.okd.git
synced 2026-03-26 19:03:14 +00:00
openshift_auth - fix discard token (#178)
* openshift_auth: when revoking token, compute the right name of the openshift resource to delete from token name * conditional check to revoke token
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
bugfixes:
|
||||||
|
- openshift_auth - Review the way the discard process is working, add openshift algorithm to convert token to resource object name (https://github.com/openshift/community.okd/issues/176).
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
roleRef:
|
roleRef:
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
name: cluster-reader
|
name: cluster-admin
|
||||||
subjects:
|
subjects:
|
||||||
- apiGroup: rbac.authorization.k8s.io
|
- apiGroup: rbac.authorization.k8s.io
|
||||||
kind: User
|
kind: User
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
---
|
---
|
||||||
- block:
|
- block:
|
||||||
|
- set_fact:
|
||||||
|
admin_user: test
|
||||||
|
admin_pass: testing123
|
||||||
|
|
||||||
- name: Retrieve cluster info
|
- name: Retrieve cluster info
|
||||||
kubernetes.core.k8s_cluster_info:
|
kubernetes.core.k8s_cluster_info:
|
||||||
register: k8s_cluster
|
register: k8s_cluster
|
||||||
@@ -10,47 +14,98 @@
|
|||||||
|
|
||||||
- name: Log in (obtain access token)
|
- name: Log in (obtain access token)
|
||||||
community.okd.openshift_auth:
|
community.okd.openshift_auth:
|
||||||
username: test
|
username: "{{ admin_user }}"
|
||||||
password: testing123
|
password: "{{ admin_pass }}"
|
||||||
host: '{{ openshift_host }}'
|
host: '{{ openshift_host }}'
|
||||||
verify_ssl: false
|
verify_ssl: false
|
||||||
register: openshift_auth_results
|
register: openshift_auth_results
|
||||||
|
|
||||||
- name: Get the test User
|
- set_fact:
|
||||||
|
auth_api_key: "{{ openshift_auth_results.openshift_auth.api_key }}"
|
||||||
|
|
||||||
|
- name: "Get the {{ admin_user }} User"
|
||||||
kubernetes.core.k8s_info:
|
kubernetes.core.k8s_info:
|
||||||
api_key: "{{ openshift_auth_results.openshift_auth.api_key }}"
|
api_key: "{{ auth_api_key }}"
|
||||||
host: '{{ openshift_host }}'
|
host: '{{ openshift_host }}'
|
||||||
verify_ssl: false
|
verify_ssl: false
|
||||||
kind: User
|
kind: User
|
||||||
api_version: user.openshift.io/v1
|
api_version: user.openshift.io/v1
|
||||||
name: test
|
name: "{{ admin_user }}"
|
||||||
register: user_result
|
register: user_result
|
||||||
|
|
||||||
- name: assert that the user was found
|
- name: assert that the user was found
|
||||||
assert:
|
assert:
|
||||||
that: (user_result.resources | length) == 1
|
that: (user_result.resources | length) == 1
|
||||||
|
|
||||||
|
- name: list available tokens
|
||||||
|
kubernetes.core.k8s_info:
|
||||||
|
kind: UserOAuthAccessToken
|
||||||
|
version: oauth.openshift.io/v1
|
||||||
|
register: tokens
|
||||||
|
|
||||||
|
- debug: var=tokens
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
token_names: "{{ tokens.resources | map(attribute='metadata.name') | list }}"
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- debug: var=token_names
|
||||||
|
|
||||||
|
- name: Revoke access token
|
||||||
|
community.okd.openshift_auth:
|
||||||
|
state: absent
|
||||||
|
api_key: "{{ auth_api_key }}"
|
||||||
|
host: '{{ openshift_host }}'
|
||||||
|
verify_ssl: false
|
||||||
|
register: _revoke
|
||||||
|
|
||||||
|
- name: Ensure that token has been revoked
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _revoke is changed
|
||||||
|
|
||||||
|
- name: "Get the {{ admin_user }} User (after token deletion)"
|
||||||
|
kubernetes.core.k8s_info:
|
||||||
|
api_key: "{{ auth_api_key }}"
|
||||||
|
host: '{{ openshift_host }}'
|
||||||
|
verify_ssl: false
|
||||||
|
kind: User
|
||||||
|
api_version: user.openshift.io/v1
|
||||||
|
name: "{{ admin_user }}"
|
||||||
|
ignore_errors: true
|
||||||
|
retries: 50
|
||||||
|
until: user_result is failed
|
||||||
|
delay: 20
|
||||||
|
register: user_result
|
||||||
|
|
||||||
|
- name: Ensure that task has failed due to revoked token
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- user_result is failed
|
||||||
|
|
||||||
|
- name: Revoke access token once again (should fail)
|
||||||
|
community.okd.openshift_auth:
|
||||||
|
state: absent
|
||||||
|
api_key: "{{ auth_api_key }}"
|
||||||
|
host: '{{ openshift_host }}'
|
||||||
|
verify_ssl: false
|
||||||
|
register: _revoke
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Ensure that nothing changed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- _revoke is failed
|
||||||
|
- _revoke.msg.startswith("Couldn't delete user oauth access token")
|
||||||
|
|
||||||
|
when: token_names | length > 0
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: If login succeeded, try to log out (revoke access token)
|
- name: If login succeeded, try to log out (revoke access token)
|
||||||
when: openshift_auth_results.openshift_auth.api_key is defined
|
when: auth_api_key is defined
|
||||||
community.okd.openshift_auth:
|
community.okd.openshift_auth:
|
||||||
state: absent
|
state: absent
|
||||||
api_key: "{{ openshift_auth_results.openshift_auth.api_key }}"
|
api_key: "{{ auth_api_key }}"
|
||||||
host: '{{ openshift_host }}'
|
host: '{{ openshift_host }}'
|
||||||
verify_ssl: false
|
verify_ssl: false
|
||||||
|
ignore_errors: true
|
||||||
- name: Get the test user
|
|
||||||
kubernetes.core.k8s_info:
|
|
||||||
api_key: "{{ openshift_auth_results.openshift_auth.api_key }}"
|
|
||||||
host: '{{ openshift_host }}'
|
|
||||||
verify_ssl: false
|
|
||||||
kind: User
|
|
||||||
name: test
|
|
||||||
api_version: user.openshift.io/v1
|
|
||||||
register: failed_user_result
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
||||||
# TODO(fabianvf) determine why token is not being rejected, maybe add more info to return
|
|
||||||
# - name: assert that the user was not found
|
|
||||||
# assert:
|
|
||||||
# that: (failed_user_result.resources | length) == 0
|
|
||||||
|
|||||||
@@ -173,6 +173,9 @@ from ansible.module_utils.basic import AnsibleModule
|
|||||||
from ansible.module_utils.six.moves.urllib_parse import urlparse, parse_qs, urlencode
|
from ansible.module_utils.six.moves.urllib_parse import urlparse, parse_qs, urlencode
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
|
from base64 import urlsafe_b64encode
|
||||||
|
import hashlib
|
||||||
|
|
||||||
# 3rd party imports
|
# 3rd party imports
|
||||||
try:
|
try:
|
||||||
import requests
|
import requests
|
||||||
@@ -211,6 +214,20 @@ K8S_AUTH_ARG_SPEC = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_oauthaccesstoken_objectname_from_token(token_name):
|
||||||
|
|
||||||
|
"""
|
||||||
|
openshift convert the access token to an OAuthAccessToken resource name using the algorithm
|
||||||
|
https://github.com/openshift/console/blob/9f352ba49f82ad693a72d0d35709961428b43b93/pkg/server/server.go#L609-L613
|
||||||
|
"""
|
||||||
|
|
||||||
|
sha256Prefix = "sha256~"
|
||||||
|
content = token_name.strip(sha256Prefix)
|
||||||
|
|
||||||
|
b64encoded = urlsafe_b64encode(hashlib.sha256(content.encode()).digest()).rstrip(b'=')
|
||||||
|
return sha256Prefix + b64encoded.decode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
class OpenShiftAuthModule(AnsibleModule):
|
class OpenShiftAuthModule(AnsibleModule):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
AnsibleModule.__init__(
|
AnsibleModule.__init__(
|
||||||
@@ -250,6 +267,8 @@ class OpenShiftAuthModule(AnsibleModule):
|
|||||||
# Get needed info to access authorization APIs
|
# Get needed info to access authorization APIs
|
||||||
self.openshift_discover()
|
self.openshift_discover()
|
||||||
|
|
||||||
|
changed = False
|
||||||
|
result = dict()
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
new_api_key = self.openshift_login()
|
new_api_key = self.openshift_login()
|
||||||
result = dict(
|
result = dict(
|
||||||
@@ -260,11 +279,10 @@ class OpenShiftAuthModule(AnsibleModule):
|
|||||||
username=self.auth_username,
|
username=self.auth_username,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.openshift_logout()
|
changed = self.openshift_logout()
|
||||||
result = dict()
|
|
||||||
|
|
||||||
# return k8s_auth as well for backwards compatibility
|
# return k8s_auth as well for backwards compatibility
|
||||||
self.exit_json(changed=False, openshift_auth=result, k8s_auth=result)
|
self.exit_json(changed=changed, openshift_auth=result, k8s_auth=result)
|
||||||
|
|
||||||
def openshift_discover(self):
|
def openshift_discover(self):
|
||||||
url = urljoin(self.con_host, '.well-known/oauth-authorization-server')
|
url = urljoin(self.con_host, '.well-known/oauth-authorization-server')
|
||||||
@@ -328,19 +346,29 @@ class OpenShiftAuthModule(AnsibleModule):
|
|||||||
return ret.json()['access_token']
|
return ret.json()['access_token']
|
||||||
|
|
||||||
def openshift_logout(self):
|
def openshift_logout(self):
|
||||||
url = '{0}/apis/oauth.openshift.io/v1/oauthaccesstokens/{1}'.format(self.con_host, self.auth_api_key)
|
|
||||||
|
name = get_oauthaccesstoken_objectname_from_token(self.auth_api_key)
|
||||||
headers = {
|
headers = {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': 'Bearer {0}'.format(self.auth_api_key)
|
'Authorization': "Bearer {0}".format(self.auth_api_key)
|
||||||
}
|
|
||||||
json = {
|
|
||||||
"apiVersion": "oauth.openshift.io/v1",
|
|
||||||
"kind": "DeleteOptions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requests.delete(url, headers=headers, json=json, verify=self.con_verify_ca)
|
url = "{0}/apis/oauth.openshift.io/v1/useroauthaccesstokens/{1}".format(self.con_host, name)
|
||||||
# Ignore errors, the token will time out eventually anyway
|
json = {
|
||||||
|
"apiVersion": "oauth.openshift.io/v1",
|
||||||
|
"kind": "DeleteOptions",
|
||||||
|
"gracePeriodSeconds": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = requests.delete(url, json=json, verify=self.con_verify_ca, headers=headers)
|
||||||
|
if ret.status_code != 200:
|
||||||
|
self.fail_json(
|
||||||
|
msg="Couldn't delete user oauth access token '{0}' due to: {1}".format(name, ret.json().get("message")),
|
||||||
|
status_code=ret.status_code
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def fail(self, msg=None):
|
def fail(self, msg=None):
|
||||||
self.fail_json(msg=msg)
|
self.fail_json(msg=msg)
|
||||||
|
|||||||
Reference in New Issue
Block a user