Skip to content

Commit 42a63c0

Browse files
committed
Issue#96 - Use dual-password methods to resolve attribute name issues
1 parent 84dc601 commit 42a63c0

File tree

6 files changed

+214
-4
lines changed

6 files changed

+214
-4
lines changed

core/src/main/python/wlsdeploy/aliases/alias_constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
WLST_PATH = 'wlst_path'
4141
WLST_PATHS = 'wlst_paths'
4242
WLST_READ_TYPE = 'wlst_read_type'
43+
WLST_SKIP_NAMES = '__wlst_skip_names__'
4344
WLST_SUBFOLDERS_PATH = 'wlst_subfolders_path'
4445
WLST_TYPE = 'wlst_type'
4546

@@ -106,6 +107,7 @@
106107
ALIAS_DATA_TYPES.extend(ALIAS_LIST_TYPES)
107108
ALIAS_DATA_TYPES.extend(ALIAS_MAP_TYPES)
108109

110+
109111
def __build_security_provider_data_structures(name_map, base_path):
110112
"""
111113
Populate the security provider data structures for the given provider type.
@@ -119,6 +121,7 @@ def __build_security_provider_data_structures(name_map, base_path):
119121
SECURITY_PROVIDER_MBEAN_NAME_MAP[mbean_name] = key
120122
return
121123

124+
122125
ADJUDICATION_PROVIDER_NAME_MAP = {
123126
'DefaultAdjudicator': 'weblogic.security.providers.authorization.DefaultAdjudicator'
124127
}

core/src/main/python/wlsdeploy/aliases/alias_entries.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
from oracle.weblogic.deploy.util import FileUtils
1212

1313
import wlsdeploy.aliases.alias_utils as alias_utils
14+
from wlsdeploy.aliases import password_utils
1415
from wlsdeploy.aliases.alias_constants import ChildFoldersTypes
1516
from wlsdeploy.aliases.location_context import LocationContext
1617
from wlsdeploy.aliases.validation_codes import ValidationCodes
1718
from wlsdeploy.aliases.wlst_modes import WlstModes
1819
from wlsdeploy.exception import exception_helper
1920
from wlsdeploy.logging.platform_logger import PlatformLogger
21+
from wlsdeploy.util import dictionary_utils
2022
from wlsdeploy.util.weblogic_helper import WebLogicHelper
2123

2224
from wlsdeploy.aliases.alias_constants import ATTRIBUTES
@@ -46,6 +48,7 @@
4648
from wlsdeploy.aliases.alias_constants import WLST_NAMES_MAP
4749
from wlsdeploy.aliases.alias_constants import WLST_PATH
4850
from wlsdeploy.aliases.alias_constants import WLST_PATHS
51+
from wlsdeploy.aliases.alias_constants import WLST_SKIP_NAMES
4952
from wlsdeploy.aliases.alias_constants import WLST_SUBFOLDERS_PATH
5053
from wlsdeploy.aliases.alias_constants import WLST_TYPE
5154

@@ -730,7 +733,9 @@ def get_alias_attribute_entry_by_wlst_name(self, location, wlst_attribute_name):
730733

731734
_logger.entering(str(location), class_name=_class_name, method_name=_method_name)
732735
folder_dict = self.__get_dictionary_for_location(location, False)
733-
if folder_dict is not None and WLST_NAMES_MAP in folder_dict:
736+
if self._is_wlst_attribute_skipped(folder_dict, wlst_attribute_name):
737+
result = None
738+
elif folder_dict is not None and WLST_NAMES_MAP in folder_dict:
734739
if wlst_attribute_name in folder_dict[WLST_NAMES_MAP]:
735740
result = copy.deepcopy(folder_dict[WLST_NAMES_MAP][wlst_attribute_name])
736741
if WLST_PATH in result:
@@ -1149,6 +1154,7 @@ def __apply_wlst_context_changes(self, path_name, alias_dict, parent_dict):
11491154
result_model_attrs = dict()
11501155
result_wlst_attrs = dict()
11511156
unresolved_attrs = dict()
1157+
wlst_skip_attrs = list()
11521158

11531159
model_attrs = alias_dict[ATTRIBUTES]
11541160
for model_attr in model_attrs:
@@ -1184,9 +1190,15 @@ def __apply_wlst_context_changes(self, path_name, alias_dict, parent_dict):
11841190
result_model_attrs[model_attr] = model_attr_dict
11851191
result_wlst_attrs[wlst_name] = model_attr_dict
11861192

1193+
# if attribute is dual-password, add its WLST skip name to the skip list
1194+
skip_name = password_utils.get_wlst_skip_name(model_attr_dict, self._wlst_mode)
1195+
if skip_name is not None:
1196+
wlst_skip_attrs.append(skip_name)
1197+
11871198
result[ATTRIBUTES] = result_model_attrs
11881199
result[WLST_NAMES_MAP] = result_wlst_attrs
11891200
result[UNRESOLVED_ATTRIBUTES_MAP] = unresolved_attrs
1201+
result[WLST_SKIP_NAMES] = wlst_skip_attrs
11901202

11911203
return result
11921204

@@ -1364,6 +1376,12 @@ def __get_valid_version_range_for_folder(self, location):
13641376
_logger.exiting(class_name=_class_name, method_name=_method_name, result=version_range)
13651377
return version_range
13661378

1379+
def _is_wlst_attribute_skipped(self, folder_dict, wlst_attribute_name):
1380+
skip_names = dictionary_utils.get_element(folder_dict, WLST_SKIP_NAMES) # type: list
1381+
if skip_names is not None:
1382+
return wlst_attribute_name in skip_names
1383+
return False
1384+
13671385
def __get_path_for_location(self, location, path_type=WLST_ATTRIBUTES_PATH):
13681386
"""
13691387
Get the tokenized path of the specified type for the location. This method is used by all path-related methods.

core/src/main/python/wlsdeploy/aliases/aliases.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from wlsdeploy.exception import exception_helper
1717
from wlsdeploy.logging.platform_logger import PlatformLogger
1818
from wlsdeploy.aliases import alias_utils
19+
from wlsdeploy.aliases import password_utils
1920
from wlsdeploy.util import string_utils
2021
from wlsdeploy.util.weblogic_helper import WebLogicHelper
2122

@@ -340,7 +341,14 @@ def get_wlst_attribute_name_and_value(self, location, model_attribute_name, mode
340341
attribute_info = module_folder[ATTRIBUTES][model_attribute_name]
341342

342343
if attribute_info and not self.__is_model_attribute_read_only(location, attribute_info):
343-
wlst_attribute_name = attribute_info[WLST_NAME]
344+
password_attribute_name = \
345+
password_utils.get_wlst_attribute_name(attribute_info, model_attribute_value, self._wlst_mode)
346+
347+
if password_attribute_name is not None:
348+
wlst_attribute_name = password_attribute_name
349+
else:
350+
wlst_attribute_name = attribute_info[WLST_NAME]
351+
344352
if USES_PATH_TOKENS in attribute_info and string_utils.to_boolean(attribute_info[USES_PATH_TOKENS]):
345353
model_attribute_value = self._model_context.replace_token_string(model_attribute_value)
346354

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
3+
The Universal Permissive License (UPL), Version 1.0
4+
5+
Contains methods for special processing of dual-password attributes.
6+
Dual-password attributes follow a common pattern in WLST, with the following rules.
7+
These examples will use a prefix of Password*, but there are others throughout WLST.
8+
9+
Offline:
10+
1. There is a single attribute name that includes the suffix, such as "PasswordEncrypted".
11+
2. The attribute can be set to an encrypted ("{AES}...") or unencrypted value.
12+
3. When the value is read, it is always encrypted.
13+
14+
Online:
15+
1. There are two attributes, with and without the prefix, such as "Password" and "PasswordEncrypted". Both
16+
represent the same underlying value.
17+
2. The encrypted attribute can only be set to an encrypted value.
18+
3. The unencrypted attribute can only be set to an unencrypted value.
19+
4. Only the unencrypted value can be read, and that value is encrypted.
20+
21+
In the alias file, these fields are flagged with the "password" WLST type, and the WLST name ends with "Encrypted".
22+
23+
The discovery code makes special provisions to skip the unencrypted online attribute.
24+
25+
The create and deploy code makes special provisions to set either the encrypted unencrypted online attribute,
26+
depending on whether the model value is encrypted.
27+
"""
28+
29+
from oracle.weblogic.deploy.encrypt import EncryptionUtils
30+
31+
from wlsdeploy.aliases.alias_constants import WLST_NAME
32+
from wlsdeploy.aliases.alias_constants import WLST_TYPE
33+
from wlsdeploy.aliases.wlst_modes import WlstModes
34+
from wlsdeploy.logging.platform_logger import PlatformLogger
35+
from wlsdeploy.util import dictionary_utils
36+
37+
_class_name = 'password_utils'
38+
_logger = PlatformLogger('wlsdeploy.aliases')
39+
_dual_password_suffix = 'Encrypted'
40+
41+
42+
def get_wlst_skip_name(attribute_info, wlst_mode):
43+
"""
44+
Returns a WLST attribute name, derived from the the specified attribute information, that should be ignored.
45+
For example, the model attribute PasswordEncrypted may indicate that the WLST attribute Password should be ignored.
46+
:param attribute_info: the attribute information to be checked
47+
:param wlst_mode: the offline or online type to be checked
48+
:return: a single name to be skipped, or None
49+
"""
50+
if _is_dual_password(attribute_info) and (wlst_mode == WlstModes.ONLINE):
51+
return _get_non_encrypted_wlst_name(attribute_info)
52+
return None
53+
54+
55+
def get_wlst_attribute_name(attribute_info, attribute_value, wlst_mode):
56+
"""
57+
Returns the corrected WLST attribute name for the specified parameters.
58+
The "Encrypted" suffix is removed from online dual-password attributes for use with unencrypted values.
59+
:param attribute_info: the attribute information to be checked
60+
:param attribute_value: the vaue to be checked for encryption
61+
:param wlst_mode: the offline or online type to be checked
62+
:return: the corrected value, or None if no correction was required
63+
"""
64+
if _is_dual_password(attribute_info) and (wlst_mode == WlstModes.ONLINE) \
65+
and not EncryptionUtils.isEncryptedString(attribute_value):
66+
return _get_non_encrypted_wlst_name(attribute_info)
67+
return None
68+
69+
70+
def _is_dual_password(attribute_info):
71+
"""
72+
Returns true if the specified attribute information indicates a dual-password field.
73+
This means the WLST type is password, and the WLST name ends with "Encrypted".
74+
:param attribute_info: the attribute information to be checked
75+
:return: True if this is a dual-password field, False otherwise
76+
"""
77+
wlst_name = dictionary_utils.get_element(attribute_info, WLST_NAME) # type: str
78+
wlst_type = dictionary_utils.get_element(attribute_info, WLST_TYPE)
79+
return (wlst_type == 'password') and wlst_name.endswith(_dual_password_suffix)
80+
81+
82+
def _get_non_encrypted_wlst_name(attribute_info):
83+
"""
84+
Returns the prefix portion of the WLST name from the specified attribute information.
85+
It is assumed that the attribute information is confirmed to be dual-password.
86+
:param attribute_info: the attribute information to be checked
87+
:return: the prefix portion of the WLST name
88+
"""
89+
wlst_name = dictionary_utils.get_element(attribute_info, WLST_NAME) # type: str
90+
return wlst_name[0:-len(_dual_password_suffix)]

core/src/main/resources/oracle/weblogic/deploy/aliases/category_modules/JDBCSystemResource.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,7 @@
103103
"attributes": {
104104
"DriverName": [ {"version": "[10,)", "wlst_mode": "both", "wlst_name": "DriverName", "wlst_path": "WP001", "value": {"default": "None" }, "wlst_type": "string" } ],
105105
"URL": [ {"version": "[10,)", "wlst_mode": "both", "wlst_name": "${URL:Url}", "wlst_path": "WP001", "value": {"default": "None" }, "wlst_type": "string" } ],
106-
"Password": [ {"version": "[10,)", "wlst_mode": "online", "wlst_name": "Password", "wlst_path": "WP001", "value": {"default": "None" }, "wlst_type": "password", "access": "RO"} ],
107-
"PasswordEncrypted": [ {"version": "[10,)", "wlst_mode": "both", "wlst_name": "Password${Encrypted:}", "wlst_path": "WP001", "value": {"default": "None" }, "wlst_type": "password", "get_method": "GET"} ],
106+
"PasswordEncrypted": [ {"version": "[10,)", "wlst_mode": "both", "wlst_name": "PasswordEncrypted", "wlst_path": "WP001", "value": {"default": "None" }, "wlst_type": "password", "get_method": "GET"} ],
108107
"UsePasswordIndirection": [ {"version": "[10,)", "wlst_mode": "both", "wlst_name": "UsePasswordIndirection", "wlst_path": "WP001", "value": {"default": "false"}, "wlst_type": "boolean", "get_method": "LSA"} ],
109108
"UseXaDataSourceInterface": [ {"version": "[10,)", "wlst_mode": "both", "wlst_name": "UseXaDataSourceInterface", "wlst_path": "WP001", "value": {"default": "false"}, "wlst_type": "boolean", "get_method": "LSA"} ]
110109
},
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
3+
The Universal Permissive License (UPL), Version 1.0
4+
"""
5+
import jarray
6+
import unittest
7+
8+
from wlsdeploy.aliases.aliases import Aliases
9+
from wlsdeploy.aliases.location_context import LocationContext
10+
from wlsdeploy.aliases.model_constants import JDBC_DRIVER_PARAMS
11+
from wlsdeploy.aliases.model_constants import JDBC_RESOURCE
12+
from wlsdeploy.aliases.model_constants import JDBC_SYSTEM_RESOURCE
13+
from wlsdeploy.aliases.model_constants import PASSWORD_ENCRYPTED
14+
from wlsdeploy.aliases.wlst_modes import WlstModes
15+
from wlsdeploy.logging.platform_logger import PlatformLogger
16+
from wlsdeploy.util.model_context import ModelContext
17+
18+
19+
class AliasPasswordTestCase(unittest.TestCase):
20+
21+
_logger = PlatformLogger('wlsdeploy.aliases')
22+
_wls_version = '12.2.1.3'
23+
_wlst_password_name = "Password"
24+
_wlst_password_encrypted_name = "PasswordEncrypted"
25+
26+
_password = 'welcome1'
27+
_encrypted_password = '{AES}BR5Lw+UuwM4ZmFcTu2GX5C2w0Jcr6E30uhZvhoyXjYU='
28+
_encrypted_password_bytes = jarray.array(_encrypted_password, 'b')
29+
30+
def setUp(self):
31+
model_context = ModelContext("test", {})
32+
self.aliases = Aliases(model_context, wlst_mode=WlstModes.OFFLINE, wls_version=self._wls_version)
33+
self.online_aliases = Aliases(model_context, wlst_mode=WlstModes.ONLINE, wls_version=self._wls_version)
34+
35+
self.location = LocationContext()
36+
self.location.append_location(JDBC_SYSTEM_RESOURCE)
37+
self.location.add_name_token(self.aliases.get_name_token(self.location), "Mine")
38+
self.location.append_location(JDBC_RESOURCE)
39+
self.location.append_location(JDBC_DRIVER_PARAMS)
40+
41+
def testOfflineModelNames(self):
42+
# Using offline WLST, only PasswordEncrypted is an attribute, and its value is encrypted.
43+
# The model name should be PasswordEncrypted.
44+
model_name, model_value = \
45+
self.aliases.get_model_attribute_name_and_value(self.location, self._wlst_password_encrypted_name,
46+
self._encrypted_password)
47+
self.assertEquals(model_name, PASSWORD_ENCRYPTED)
48+
49+
def testOnlineModelNames(self):
50+
# Using online WLST, both Password and PasswordEncrypted are WLST attributes.
51+
52+
# The PasswordEncrypted WLST attribute should translate to the PasswordEncrypted model attribute.
53+
model_name, model_value = \
54+
self.online_aliases.get_model_attribute_name_and_value(self.location, self._wlst_password_encrypted_name,
55+
self._encrypted_password)
56+
self.assertEqual(model_name, PASSWORD_ENCRYPTED)
57+
58+
# The Password WLST attribute should be skipped, since its value cannot be retrieved.
59+
# This is accomplished by returning a model name of None.
60+
model_name, model_value = \
61+
self.online_aliases.get_model_attribute_name_and_value(self.location, self._wlst_password_name,
62+
self._encrypted_password)
63+
self.assertEquals(model_name, None)
64+
65+
def testOfflineWlstNames(self):
66+
# Using offline WLST, the PasswordEncrypted model attribute should translate to the PasswordEncrypted WLST
67+
# attribute, regardless of whether the password is encrypted.
68+
69+
# using encrypted password
70+
wlst_name, wlst_value = \
71+
self.aliases.get_wlst_attribute_name_and_value(self.location, PASSWORD_ENCRYPTED, self._encrypted_password)
72+
self.assertEqual(wlst_name, self._wlst_password_encrypted_name)
73+
74+
# using unencrypted password
75+
wlst_name, wlst_value = \
76+
self.aliases.get_wlst_attribute_name_and_value(self.location, PASSWORD_ENCRYPTED, self._password)
77+
self.assertEquals(wlst_name, self._wlst_password_encrypted_name)
78+
79+
def testOnlineWlstNames(self):
80+
# Using online WLST, the PasswordEncrypted model attribute should translate to the PasswordEncrypted WLST
81+
# attribute if the password is encrypted, otherwise to Password.
82+
83+
# using encrypted password
84+
wlst_name, wlst_value = \
85+
self.online_aliases.get_wlst_attribute_name_and_value(self.location, PASSWORD_ENCRYPTED,
86+
self._encrypted_password)
87+
self.assertEqual(wlst_name, self._wlst_password_encrypted_name)
88+
89+
# using unencrypted password
90+
wlst_name, wlst_value = \
91+
self.online_aliases.get_wlst_attribute_name_and_value(self.location, PASSWORD_ENCRYPTED, self._password)
92+
self.assertEquals(wlst_name, self._wlst_password_name)

0 commit comments

Comments
 (0)