-
Notifications
You must be signed in to change notification settings - Fork 132
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds authn/root API endpoint (#1331)
Issues: Fixes #1330 Problem: The authn/root API was not available. This API is really weird because its more like a fire-and-forget API. It provides no kind or selfLink in its return value. Analysis: Had to work around the resource deficiencies. Introduced our own kind and selfLink values because BIG-IP does not provide them. All methods except create are invalid. Tests: functional unit
- Loading branch information
1 parent
4daeb23
commit 7d1285d
Showing
4 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# coding=utf-8 | ||
# | ||
# Copyright 2017 F5 Networks Inc. | ||
# | ||
# 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 f5.bigip.resource import Collection | ||
from f5.bigip.resource import OrganizingCollection | ||
from f5.bigip.resource import Resource | ||
from f5.sdk_exception import UnsupportedMethod | ||
from f5.sdk_exception import UnsupportedOperation | ||
from f5.sdk_exception import URICreationCollision | ||
|
||
|
||
class Authn(OrganizingCollection): | ||
def __init__(self, shared): | ||
super(Authn, self).__init__(shared) | ||
self._meta_data['allowed_lazy_attributes'] = [ | ||
Roots | ||
] | ||
|
||
|
||
class Roots(Collection): | ||
def __init__(self, authn): | ||
super(Roots, self).__init__(authn) | ||
self._meta_data['allowed_lazy_attributes'] = [Root] | ||
|
||
def get_collection(self, **kwargs): | ||
raise UnsupportedMethod( | ||
"%s does not support get_collection" % self.__class__.__name__ | ||
) | ||
|
||
|
||
class Root(Resource): | ||
def __init__(self, roots): | ||
super(Root, self).__init__(roots) | ||
self._meta_data['required_json_kind'] = 'shared:authn:authrootitemstate' | ||
|
||
# The required parameters are a little vague. It turns out that the "user" | ||
# value that is required is the "link" to the ID | ||
self._meta_data['required_creation_parameters'] = {'oldPassword', 'newPassword'} | ||
|
||
def _create(self, **kwargs): | ||
"""wrapped by `create` override that in subclasses to customize""" | ||
if 'uri' in self._meta_data: | ||
error = "There was an attempt to assign a new uri to this "\ | ||
"resource, the _meta_data['uri'] is %s and it should"\ | ||
" not be changed." % (self._meta_data['uri']) | ||
raise URICreationCollision(error) | ||
self._check_exclusive_parameters(**kwargs) | ||
requests_params = self._handle_requests_params(kwargs) | ||
self._minimum_one_is_missing(**kwargs) | ||
self._check_create_parameters(**kwargs) | ||
kwargs = self._check_for_python_keywords(kwargs) | ||
|
||
# Reduce boolean pairs as specified by the meta_data entry below | ||
for key1, key2 in self._meta_data['reduction_forcing_pairs']: | ||
kwargs = self._reduce_boolean_pair(kwargs, key1, key2) | ||
|
||
# Make convenience variable with short names for this method. | ||
_create_uri = self._meta_data['container']._meta_data['uri'] | ||
session = self._meta_data['bigip']._meta_data['icr_session'] | ||
|
||
kwargs = self._prepare_request_json(kwargs) | ||
|
||
# Invoke the REST operation on the device. | ||
response = session.post(_create_uri, json=kwargs, **requests_params) | ||
|
||
# Make new instance of self | ||
result = self._produce_instance(response) | ||
return result | ||
|
||
def _local_update(self, rdict): | ||
super(Root, self)._local_update(rdict) | ||
|
||
# This API returns no kind, so we need to make our own | ||
self.__dict__.update(dict(kind='shared:authn:authrootitemstate')) | ||
|
||
# This API returns no selfLink, so we need to make our own | ||
tmos_version = self._meta_data['bigip']._meta_data['tmos_version'] | ||
self_link = 'https://localhost/mgmt/shared/authn/root?ver={0}'.format(tmos_version) | ||
self.__dict__.update(dict(selfLink=self_link)) | ||
|
||
def update(self, **kwargs): | ||
raise UnsupportedOperation( | ||
"%s does not support update" % self.__class__.__name__ | ||
) | ||
|
||
def load(self, **kwargs): | ||
raise UnsupportedOperation( | ||
"%s does not support load" % self.__class__.__name__ | ||
) | ||
|
||
def modify(self, **kwargs): | ||
raise UnsupportedOperation( | ||
"%s does not support modify" % self.__class__.__name__ | ||
) | ||
|
||
def delete(self, **kwargs): | ||
raise UnsupportedOperation( | ||
"%s does not support delete" % self.__class__.__name__ | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Copyright 2017 F5 Networks Inc. | ||
# | ||
# 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 pytest | ||
|
||
from distutils.version import LooseVersion | ||
|
||
pytestmark = pytest.mark.skipif( | ||
LooseVersion(pytest.config.getoption('--release')) | ||
< LooseVersion('12.0.0'), | ||
reason='Needs v12 TMOS or greater to pass.' | ||
) | ||
|
||
|
||
@pytest.fixture(scope='function') | ||
def root_credentials(mgmt_root): | ||
result = mgmt_root.shared.authn.roots.root.create( | ||
oldPassword='default', | ||
newPassword='ChangeMyPassword1234' | ||
) | ||
yield result | ||
mgmt_root.shared.authn.roots.root.create( | ||
oldPassword='ChangeMyPassword1234', | ||
newPassword='default' | ||
) | ||
|
||
|
||
@pytest.mark.skipif( | ||
LooseVersion(pytest.config.getoption('--release')) >= LooseVersion('12.1.0'), | ||
reason='This fixture requires < 12.1.0.' | ||
) | ||
class TestAuthnV12(object): | ||
def test_create(self, root_credentials): | ||
assert root_credentials.newPassword == 'ChangeMyPassword1234' | ||
|
||
|
||
@pytest.mark.skipif( | ||
LooseVersion(pytest.config.getoption('--release')) < LooseVersion('12.1.0'), | ||
reason='This fixture requires >= 12.1.0.' | ||
) | ||
class TestAuthnPostV12(object): | ||
def test_create(self, root_credentials): | ||
assert root_credentials.generation == 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Copyright 2017 F5 Networks Inc. | ||
# | ||
# 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 f5.bigip.shared.authn import Root | ||
from f5.bigip.shared.authn import Roots | ||
from f5.sdk_exception import MissingRequiredCreationParameter | ||
from f5.sdk_exception import UnsupportedMethod | ||
from f5.sdk_exception import UnsupportedOperation | ||
|
||
import mock | ||
import pytest | ||
|
||
|
||
@pytest.fixture | ||
def FakeAuthnRoot(): | ||
mo = mock.MagicMock() | ||
fake = Root(mo) | ||
return fake | ||
|
||
|
||
@pytest.fixture | ||
def FakeAuthnRoots(): | ||
mo = mock.MagicMock() | ||
fake = Roots(mo) | ||
return fake | ||
|
||
|
||
class TestAuthnRoot(object): | ||
def test_update_raises(self, FakeAuthnRoot): | ||
with pytest.raises(UnsupportedOperation): | ||
FakeAuthnRoot.update() | ||
|
||
def test_modify_raises(self, FakeAuthnRoot): | ||
with pytest.raises(UnsupportedOperation): | ||
FakeAuthnRoot.modify() | ||
|
||
def test_load_raises(self, FakeAuthnRoot): | ||
with pytest.raises(UnsupportedOperation): | ||
FakeAuthnRoot.load() | ||
|
||
def test_delete_raises(self, FakeAuthnRoot): | ||
with pytest.raises(UnsupportedOperation): | ||
FakeAuthnRoot.delete() | ||
|
||
def test_create_no_args(self, FakeAuthnRoot): | ||
with pytest.raises(MissingRequiredCreationParameter): | ||
FakeAuthnRoot.create() | ||
|
||
|
||
class TestAuthnRoots(object): | ||
def test_collection_raises(self, FakeAuthnRoots): | ||
with pytest.raises(UnsupportedMethod): | ||
FakeAuthnRoots.get_collection() |