Skip to content

Commit 44eb5f0

Browse files
Merge pull request #398 from OpenKMIP/feat/add-auth-plugins
Add an authentication plugin framework
2 parents 7743c4e + 98f5ba3 commit 44eb5f0

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)