Skip to content

Commit 98f5ba3

Browse files
committed
Add an authentication plugin framework
This change adds an authentication plugin framework to be used by the PyKMIP server. This framework will allow the server to query third-party authentication systems for user identity information, improving the access control model for the server. The initial plugin provided queries an instance of the new SLUGS library.
1 parent 3bc9a61 commit 98f5ba3

File tree

8 files changed

+798
-0
lines changed

8 files changed

+798
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright (c) 2018 The Johns Hopkins University/Applied Physics Laboratory
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
from kmip.services.server.auth.api import AuthAPI
17+
from kmip.services.server.auth.slugs import SLUGSConnector
18+
19+
from kmip.services.server.auth.utils import get_certificate_from_connection
20+
from kmip.services.server.auth.utils import \
21+
get_client_identity_from_certificate
22+
from kmip.services.server.auth.utils import get_common_names_from_certificate
23+
from kmip.services.server.auth.utils import \
24+
get_extended_key_usage_from_certificate
25+
26+
27+
__all__ = [
28+
'AuthAPI',
29+
'SLUGSConnector',
30+
'get_certificate_from_connection',
31+
'get_client_identity_from_certificate',
32+
'get_common_names_from_certificate',
33+
'get_extended_key_usage_from_certificate'
34+
]

kmip/services/server/auth/api.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright (c) 2018 The Johns Hopkins University/Applied Physics Laboratory
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
import abc
17+
import six
18+
19+
20+
@six.add_metaclass(abc.ABCMeta)
21+
class AuthAPI:
22+
"""
23+
The base class for an authentication API connector.
24+
"""
25+
26+
@abc.abstractmethod
27+
def authenticate(self,
28+
connection_certificate=None,
29+
connection_info=None,
30+
request_credentials=None):
31+
"""
32+
Query the configured authentication service with the given credentials.
33+
34+
Args:
35+
connection_certificate (cryptography.x509.Certificate): An X.509
36+
certificate object obtained from the connection being
37+
authenticated. Optional, defaults to None.
38+
connection_info (tuple): A tuple of information pertaining to the
39+
connection being authenticated, including the source IP address
40+
and a timestamp (e.g., ('127.0.0.1', 1519759267.467451)).
41+
Optional, defaults to None.
42+
request_credentials (list): A list of KMIP Credential structures
43+
containing credential information to use for authentication.
44+
Optional, defaults to None.
45+
"""

kmip/services/server/auth/slugs.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Copyright (c) 2018 The Johns Hopkins University/Applied Physics Laboratory
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
import requests
17+
import six
18+
19+
from kmip.core import exceptions
20+
from kmip.services.server.auth import api
21+
from kmip.services.server.auth import utils
22+
23+
24+
class SLUGSConnector(api.AuthAPI):
25+
"""
26+
An authentication API connector for a SLUGS service.
27+
"""
28+
29+
def __init__(self, url=None):
30+
"""
31+
Construct a SLUGSConnector.
32+
33+
Args:
34+
url (string): The base URL for the remote SLUGS instance. Optional,
35+
defaults to None. Required for authentication.
36+
"""
37+
self._url = None
38+
self.users_url = None
39+
self.groups_url = None
40+
41+
self.url = url
42+
43+
@property
44+
def url(self):
45+
return self._url
46+
47+
@url.setter
48+
def url(self, value):
49+
if value is None:
50+
self._url = None
51+
self.users_url = None
52+
self.groups_url = None
53+
elif isinstance(value, six.string_types):
54+
self._url = value
55+
if not self._url.endswith("/"):
56+
self._url += "/"
57+
self.users_url = self._url + "users/{}"
58+
self.groups_url = self.users_url + "/groups"
59+
else:
60+
raise TypeError("URL must be a string.")
61+
62+
def authenticate(self,
63+
connection_certificate=None,
64+
connection_info=None,
65+
request_credentials=None):
66+
"""
67+
Query the configured SLUGS service with the provided credentials.
68+
69+
Args:
70+
connection_certificate (cryptography.x509.Certificate): An X.509
71+
certificate object obtained from the connection being
72+
authenticated. Required for SLUGS authentication.
73+
connection_info (tuple): A tuple of information pertaining to the
74+
connection being authenticated, including the source IP address
75+
and a timestamp (e.g., ('127.0.0.1', 1519759267.467451)).
76+
Optional, defaults to None. Ignored for SLUGS authentication.
77+
request_credentials (list): A list of KMIP Credential structures
78+
containing credential information to use for authentication.
79+
Optional, defaults to None. Ignored for SLUGS authentication.
80+
"""
81+
if (self.users_url is None) or (self.groups_url is None):
82+
raise exceptions.ConfigurationError(
83+
"The SLUGS URL must be specified."
84+
)
85+
86+
user_id = utils.get_client_identity_from_certificate(
87+
connection_certificate
88+
)
89+
90+
try:
91+
response = requests.get(self.users_url.format(user_id))
92+
except Exception:
93+
raise exceptions.ConfigurationError(
94+
"A connection could not be established using the SLUGS URL."
95+
)
96+
if response.status_code == 404:
97+
raise exceptions.PermissionDenied(
98+
"Unrecognized user ID: {}".format(user_id)
99+
)
100+
101+
response = requests.get(self.groups_url.format(user_id))
102+
if response.status_code == 404:
103+
raise exceptions.PermissionDenied(
104+
"Group information could not be retrieved for user ID: "
105+
"{}".format(user_id)
106+
)
107+
108+
return user_id, response.json().get('groups')

kmip/services/server/auth/utils.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright (c) 2018 The Johns Hopkins University/Applied Physics Laboratory
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
from cryptography import x509
17+
from cryptography.hazmat import backends
18+
19+
from kmip.core import exceptions
20+
21+
22+
def get_certificate_from_connection(connection):
23+
"""
24+
Extract an X.509 certificate from a socket connection.
25+
"""
26+
certificate = connection.getpeercert(binary_form=True)
27+
if certificate:
28+
return x509.load_der_x509_certificate(
29+
certificate,
30+
backends.default_backend()
31+
)
32+
return None
33+
34+
35+
def get_extended_key_usage_from_certificate(certificate):
36+
"""
37+
Given an X.509 certificate, extract and return the extendedKeyUsage
38+
extension.
39+
"""
40+
try:
41+
return certificate.extensions.get_extension_for_oid(
42+
x509.oid.ExtensionOID.EXTENDED_KEY_USAGE
43+
).value
44+
except x509.ExtensionNotFound:
45+
return None
46+
47+
48+
def get_common_names_from_certificate(certificate):
49+
"""
50+
Given an X.509 certificate, extract and return all common names.
51+
"""
52+
53+
common_names = certificate.subject.get_attributes_for_oid(
54+
x509.oid.NameOID.COMMON_NAME
55+
)
56+
return [common_name.value for common_name in common_names]
57+
58+
59+
def get_client_identity_from_certificate(certificate):
60+
"""
61+
Given an X.509 certificate, extract and return the client identity.
62+
"""
63+
client_ids = get_common_names_from_certificate(certificate)
64+
65+
if len(client_ids) > 0:
66+
if len(client_ids) > 1:
67+
raise exceptions.PermissionDenied(
68+
"Multiple client identities found."
69+
)
70+
return client_ids[0]
71+
else:
72+
raise exceptions.PermissionDenied(
73+
"The certificate does not define any subject common names. "
74+
"Client identity unavailable."
75+
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright (c) 2018 The Johns Hopkins University/Applied Physics Laboratory
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.

0 commit comments

Comments
 (0)