Skip to content

Commit 6a2ed17

Browse files
TheFrogPadddsharpe
authored andcommitted
WDT-353: Add WLSRoles section to domainInfo (#411)
* Adds WebLogic roles to domainInfo a section * Adds WebLogic users and groups to Security section
1 parent 436381b commit 6a2ed17

File tree

11 files changed

+540
-10
lines changed

11 files changed

+540
-10
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ Many organizations are using WebLogic Server, with or without other Oracle Fusio
1616
- [Model Names](#model-names)
1717
- [Model Semantics](#model-semantics)
1818
- [Administration Server Configuration](site/admin_server.md)
19-
- [Modeling Security Providers](site/security_providers.md)
20-
- [JRF Trust Service Identity Asserter](site/security_providers.md#trust-service-identity-asserter)
21-
- [Custom Security Providers](site/security_providers.md#custom-security-providers)
19+
- [Model Security](site/security.md)
20+
- [Modeling Security Providers](site/security_providers.md)
21+
- [JRF Trust Service Identity Asserter](site/security_providers.md#trust-service-identity-asserter)
22+
- [Custom Security Providers](site/security_providers.md#custom-security-providers)
23+
- [Modeling WebLogic Users, Groups, and Roles](site/security_users_groups_roles.md)
2224
- [Variable Injection](site/variable_injection.md)
2325
- [Model Filters](site/tool_filters.md)
2426
- [Downloading and Installing](#downloading-and-installing-the-software)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ class AliasEntries(object):
186186
# servers in the domain.
187187
'ServerGroupTargetingLimits': 'dict',
188188
'RCUDbInfo': 'dict',
189-
'OPSSSecrets': 'string'
189+
'OPSSSecrets': 'password',
190+
'WLSRoles': 'dict'
190191
}
191192

192193
__domain_name_token = 'DOMAIN'

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
ADMIN_USERNAME = 'AdminUserName'
1818
APP_DEPLOYMENTS = 'appDeployments'
1919
APP_DIR = 'AppDir'
20+
APPEND = 'append'
2021
APPLICATION = 'Application'
2122
RCU_DB_INFO = 'RCUDbInfo'
2223
OPSS_SECRETS = 'OPSSSecrets'
@@ -95,6 +96,7 @@
9596
DYNAMIC_SERVERS = 'DynamicServers'
9697
ERROR_DESTINATION = 'ErrorDestination'
9798
EXECUTE_QUEUE = 'ExecuteQueue'
99+
EXPRESSION = 'Expression'
98100
FAIR_SHARE_REQUEST_CLASS = 'FairShareRequestClass'
99101
FILE_OPEN = 'FileOpen'
100102
FILE_STORE = 'FileStore'
@@ -183,11 +185,13 @@
183185
PKI_CREDENTIAL_MAPPER = 'PKICredentialMapper'
184186
PLAN_DIR = 'PlanDir'
185187
PLAN_PATH = 'PlanPath'
188+
PREPEND = 'prepend'
186189
PROPERTIES = 'Properties'
187190
QUEUE = 'Queue'
188191
QUOTA = 'Quota'
189192
REALM = 'Realm'
190193
RECOVER_ONLY_ONCE = 'RecoverOnlyOnce'
194+
REPLACE = 'replace'
191195
RESOURCE_GROUP = 'ResourceGroup'
192196
RESOURCE_GROUP_TEMPLATE = 'ResourceGroupTemplate'
193197
RESOURCES = 'resources'
@@ -248,6 +252,7 @@
248252
UNIFORM_DISTRIBUTED_QUEUE = 'UniformDistributedQueue'
249253
UNIFORM_DISTRIBUTED_TOPIC = 'UniformDistributedTopic'
250254
UNIX_MACHINE = 'UnixMachine'
255+
UPDATE_MODE = 'UpdateMode'
251256
USER = 'User'
252257
VIRTUAL_TARGET = 'VirtualTarget'
253258
VIRTUAL_USER_AUTHENTICATOR = 'VirtualUserAuthenticator'
@@ -268,6 +273,7 @@
268273
WLDF_INSTRUMENTATION_MONITOR = "WLDFInstrumentationMonitor"
269274
WLDF_RESOURCE = "WLDFResource"
270275
WLDF_SYSTEM_RESOURCE = "WLDFSystemResource"
276+
WLS_ROLES = "WLSRoles"
271277
WS_RELIABLE_DELIVERY_POLICY = 'WSReliableDeliveryPolicy'
272278
XACML_AUTHORIZER = 'XACMLAuthorizer'
273279
XACML_ROLE_MAPPER = 'XACMLRoleMapper'

core/src/main/python/wlsdeploy/tool/create/domain_creator.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
from wlsdeploy.tool.create.creator import Creator
7878
from wlsdeploy.tool.create.rcudbinfo_helper import RcuDbInfo
7979
from wlsdeploy.tool.create.security_provider_creator import SecurityProviderCreator
80+
from wlsdeploy.tool.create.wlsroles_helper import WLSRoles
8081
from wlsdeploy.tool.deploy import deployer_utils
8182
from wlsdeploy.tool.deploy import model_deployer
8283
from wlsdeploy.tool.util.archive_helper import ArchiveHelper
@@ -148,6 +149,9 @@ def __init__(self, model_dictionary, model_context, aliases):
148149
self.target_helper = TargetHelper(self.model, self.model_context, self.aliases, ExceptionType.CREATE,
149150
self.logger)
150151

152+
self.wlsroles_helper = WLSRoles(self._domain_info, self._domain_home, self.wls_helper,
153+
ExceptionType.CREATE, self.logger)
154+
151155
#
152156
# This list gets modified as the domain is being created so do use this list for anything else...
153157
#
@@ -338,6 +342,7 @@ def __create_domain(self):
338342

339343
self.library_helper.install_domain_libraries()
340344
self.library_helper.extract_classpath_libraries()
345+
self.wlsroles_helper.process_roles()
341346
self.logger.exiting(class_name=self.__class_name, method_name=_method_name)
342347
return
343348

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
"""
2+
Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
3+
Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
4+
"""
5+
from java.io import File
6+
from wlsdeploy.aliases.model_constants import APPEND
7+
from wlsdeploy.aliases.model_constants import EXPRESSION
8+
from wlsdeploy.aliases.model_constants import PREPEND
9+
from wlsdeploy.aliases.model_constants import REPLACE
10+
from wlsdeploy.aliases.model_constants import UPDATE_MODE
11+
from wlsdeploy.aliases.model_constants import WLS_ROLES
12+
from wlsdeploy.util import dictionary_utils
13+
from wlsdeploy.util import string_utils
14+
from wlsdeploy.util.weblogic_roles_helper import WebLogicRolesHelper
15+
16+
WLS_GLOBAL_ROLES = {
17+
'Admin': '?weblogic.entitlement.rules.AdministrativeGroup(Administrators)',
18+
'Deployer': '?weblogic.entitlement.rules.AdministrativeGroup(Deployers)',
19+
'Operator': '?weblogic.entitlement.rules.AdministrativeGroup(Operators)',
20+
'Monitor': '?weblogic.entitlement.rules.AdministrativeGroup(Monitors)',
21+
'Anonymous': 'Grp(everyone)',
22+
'AppTester': '?weblogic.entitlement.rules.OwnerIDDGroup(AppTesters)',
23+
'CrossDomainConnector': '?weblogic.entitlement.rules.OwnerIDDGroup(CrossDomainConnectors)',
24+
'AdminChannelUser': '?weblogic.entitlement.rules.OwnerIDDGroup(AdminChannelUsers)'
25+
}
26+
WLS_ROLE_UPDATE_OPERAND = '|'
27+
28+
class WLSRoles(object):
29+
"""
30+
Handle the WLSRoles section from the model domainInfo
31+
"""
32+
__class_name = 'WLSRoles'
33+
34+
def __init__(self, domain_info, domain_home, wls_helper, exception_type, logger, validation_roles_map = None):
35+
self.logger = logger
36+
self._wls_helper = wls_helper
37+
self._wls_roles_map = None
38+
self._domain_security_folder = None
39+
self._validation_roles_map = validation_roles_map
40+
41+
if domain_info is not None:
42+
if not dictionary_utils.is_empty_dictionary_element(domain_info, WLS_ROLES):
43+
self._wls_roles_map = domain_info[WLS_ROLES]
44+
self._domain_security_folder = File(domain_home, 'security').getPath()
45+
self._weblogic_roles_helper = WebLogicRolesHelper(logger, exception_type, self._domain_security_folder)
46+
return
47+
48+
def process_roles(self):
49+
"""
50+
Process the WebLogic roles contained in the domainInfo section of the model when specified
51+
Support for the WebLogic roles handling is when using WebLogic Server 12.2.1 or greater
52+
"""
53+
_method_name = 'process_roles'
54+
self.logger.entering(self._domain_security_folder, self._wls_roles_map, class_name=self.__class_name, method_name=_method_name)
55+
if self._wls_roles_map is not None:
56+
# Process the WLSRoles section after checking for proper version support
57+
if self._wls_helper is not None and not self._wls_helper.is_weblogic_version_or_above('12.2.1'):
58+
self.logger.warning('WLSDPLY-12504', self._wls_helper.get_actual_weblogic_version(), class_name=self.__class_name, method_name=_method_name)
59+
else:
60+
role_expressions = self._process_roles_map(self._wls_roles_map, None)
61+
if role_expressions is not None and len(role_expressions) > 0:
62+
self.logger.info('WLSDPLY-12500', role_expressions.keys(), class_name=self.__class_name, method_name=_method_name)
63+
self._update_xacml_role_mapper(role_expressions)
64+
self.logger.exiting(class_name=self.__class_name, method_name=_method_name)
65+
return
66+
67+
def validate_roles(self, validation_result):
68+
"""
69+
Validate WLSRoles section of the domainInfo independent of any domain home location
70+
"""
71+
_method_name = 'validate_roles'
72+
self.logger.entering(self._validation_roles_map, class_name=self.__class_name, method_name=_method_name)
73+
if self._validation_roles_map is not None and len(self._validation_roles_map) > 0:
74+
self._validate_update_mode(self._validation_roles_map, validation_result)
75+
self._process_roles_map(self._validation_roles_map, validation_result)
76+
self.logger.exiting(class_name=self.__class_name, method_name=_method_name)
77+
return validation_result
78+
79+
def _process_roles_map(self, roles_map, validation_result):
80+
"""
81+
Loop through the WebLogic roles listed in the domainInfo and create a map of the role to the expression
82+
"""
83+
_method_name = '_process_roles_map'
84+
self.logger.entering(class_name=self.__class_name, method_name=_method_name)
85+
result = {}
86+
for role in roles_map.keys():
87+
# Get the role expression and if the role should be an update to the default set of roles
88+
expression = self._get_role_expression(role, roles_map)
89+
if string_utils.is_empty(expression):
90+
self.logger.finer('WLSDPLY-12501', role, class_name=self.__class_name, method_name=_method_name)
91+
if validation_result is not None:
92+
validation_result.add_warning('WLSDPLY-12501', role)
93+
continue
94+
update_role = self._is_role_update_mode(role, roles_map)
95+
if update_role and role not in WLS_GLOBAL_ROLES:
96+
self.logger.finer('WLSDPLY-12502', role, class_name=self.__class_name, method_name=_method_name)
97+
if validation_result is not None:
98+
validation_result.add_warning('WLSDPLY-12502', role)
99+
update_role = False
100+
# Add the role and expression to the map of roles to be processed
101+
if update_role:
102+
expression = self._update_role_epression(role, expression, roles_map)
103+
result[role] = expression
104+
105+
self.logger.exiting(class_name=self.__class_name, method_name=_method_name)
106+
return result
107+
108+
def _update_xacml_role_mapper(self, role_expression_map):
109+
"""
110+
Update the XACML role mapper based on the supplied map of WebLogic roles with expressions
111+
"""
112+
_method_name = '_update_xacml_role_mapper'
113+
self.logger.entering(role_expression_map, class_name=self.__class_name, method_name=_method_name)
114+
self._weblogic_roles_helper.update_xacml_role_mapper(role_expression_map)
115+
self.logger.exiting(class_name=self.__class_name, method_name=_method_name)
116+
return
117+
118+
def _get_role_expression(self, role_name, roles_map):
119+
"""
120+
Determine if the role has an expression defined in the model
121+
:return: the expression if the model value is present
122+
"""
123+
result = None
124+
role_map = roles_map[role_name]
125+
if EXPRESSION in role_map:
126+
result = role_map[EXPRESSION]
127+
return result
128+
129+
def _is_role_update_mode(self, role_name, roles_map):
130+
"""
131+
Determine if the role update mode indicates that a role update is specified
132+
:return: True if the update mode value is present and set to append or prepend mode
133+
"""
134+
result = False
135+
role_map = roles_map[role_name]
136+
if UPDATE_MODE in role_map:
137+
mode = role_map[UPDATE_MODE]
138+
if not string_utils.is_empty(mode):
139+
mode = mode.lower()
140+
if APPEND == mode or PREPEND == mode:
141+
result = True
142+
return result
143+
144+
def _update_role_epression(self, role_name, expression_value, roles_map):
145+
"""
146+
Lookup the default role definition and logically OR the expression
147+
Based on the update mode the expression is appended or prepended
148+
:return: the updated role expression
149+
"""
150+
result = expression_value
151+
role_map = roles_map[role_name]
152+
if UPDATE_MODE in role_map:
153+
mode = role_map[UPDATE_MODE]
154+
if not string_utils.is_empty(mode):
155+
mode = mode.lower()
156+
if APPEND == mode:
157+
result = WLS_GLOBAL_ROLES[role_name] + WLS_ROLE_UPDATE_OPERAND + expression_value
158+
elif PREPEND == mode:
159+
result = expression_value + WLS_ROLE_UPDATE_OPERAND + WLS_GLOBAL_ROLES[role_name]
160+
return result
161+
162+
def _validate_update_mode(self, roles_map, validation_result):
163+
"""
164+
Check that the UpdateMode value of a role is one of append, prepend or replace
165+
Provide a warning for other values as these will be treated as the default of replace
166+
"""
167+
_method_name = '_validate_update_mode'
168+
self.logger.entering(class_name=self.__class_name, method_name=_method_name)
169+
170+
for role_name in roles_map.keys():
171+
role_map = roles_map[role_name]
172+
if UPDATE_MODE in role_map:
173+
mode = role_map[UPDATE_MODE]
174+
if not string_utils.is_empty(mode):
175+
mode = mode.lower()
176+
if APPEND == mode or PREPEND == mode or REPLACE == mode:
177+
continue
178+
self.logger.finer('WLSDPLY-12503', role_name, class_name=self.__class_name, method_name=_method_name)
179+
if validation_result is not None:
180+
validation_result.add_warning('WLSDPLY-12503', role_name)
181+
182+
self.logger.exiting(class_name=self.__class_name, method_name=_method_name)
183+
return
184+
185+
def validator(roles_map, logger):
186+
"""
187+
Obtain a WLSRoles helper only for the validation of the WLSRoles section
188+
"""
189+
return WLSRoles(None, None, None, None, logger, validation_roles_map = roles_map)

core/src/main/python/wlsdeploy/tool/validate/validator.py

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from wlsdeploy.exception.expection_types import ExceptionType
1717
from wlsdeploy.exception import exception_helper
1818
from wlsdeploy.logging.platform_logger import PlatformLogger
19+
from wlsdeploy.tool.create import wlsroles_helper
1920
from wlsdeploy.tool.util.alias_helper import AliasHelper
2021
from wlsdeploy.tool.util.archive_helper import ArchiveHelper
2122
from wlsdeploy.tool.validate import validation_utils
@@ -32,6 +33,7 @@
3233
from wlsdeploy.aliases.model_constants import NAME
3334
from wlsdeploy.aliases.model_constants import SERVER_GROUP_TARGETING_LIMITS
3435
from wlsdeploy.aliases.model_constants import TOPOLOGY
36+
from wlsdeploy.aliases.model_constants import WLS_ROLES
3537

3638
_class_name = 'Validator'
3739
_logger = PlatformLogger('wlsdeploy.validate')
@@ -210,6 +212,17 @@ def print_usage(self, model_path, control_option=None):
210212
#
211213
####################################################################################
212214

215+
def __get_attribute_log_value(self, attribute_name, attribute_value, attribute_infos):
216+
"""
217+
Get the log output for an attribute value to protect sensitive data
218+
"""
219+
result = attribute_value
220+
if attribute_name in attribute_infos:
221+
expected_data_type = dictionary_utils.get_element(attribute_infos, attribute_name)
222+
if expected_data_type == 'password':
223+
result = '<masked>'
224+
return result
225+
213226
def __validate_model_file(self, model_dict, variables_file_name, archive_file_name):
214227
_method_name = '__validate_model_file'
215228

@@ -387,7 +400,8 @@ def __validate_domain_info_section(self, model_section_key, model_dict, validati
387400
if '${' in section_dict_key:
388401
validation_result.add_error('WLSDPLY-05035', model_folder_path, section_dict_key)
389402

390-
self._logger.finer('WLSDPLY-05011', section_dict_key, section_dict_value,
403+
log_value = self.__get_attribute_log_value(section_dict_key, section_dict_value, valid_attr_infos)
404+
self._logger.finer('WLSDPLY-05011', section_dict_key, log_value,
391405
class_name=_class_name, method_name=_method_name)
392406

393407
if section_dict_key in valid_attr_infos:
@@ -405,6 +419,14 @@ def __validate_domain_info_section(self, model_section_key, model_dict, validati
405419
model_folder_path,
406420
validation_location,
407421
validation_result)
422+
elif section_dict_key == WLS_ROLES:
423+
validation_result = self.__validate_wlsroles_section(section_dict_key,
424+
section_dict_value,
425+
valid_attr_infos,
426+
path_tokens_attr_keys,
427+
model_folder_path,
428+
validation_location,
429+
validation_result)
408430
else:
409431
validation_result = self.__validate_attribute(section_dict_key,
410432
section_dict_value,
@@ -740,11 +762,7 @@ def __validate_attribute(self, attribute_name, attribute_value, valid_attr_infos
740762
model_folder_path, validation_location, validation_result):
741763
_method_name = '__validate_attribute'
742764

743-
log_value = attribute_value
744-
expected_data_type = dictionary_utils.get_element(valid_attr_infos, attribute_name)
745-
if expected_data_type == 'password':
746-
log_value = '<masked>'
747-
765+
log_value = self.__get_attribute_log_value(attribute_name, attribute_value, valid_attr_infos)
748766
self._logger.entering(attribute_name, log_value, str(valid_attr_infos), str(path_tokens_attr_keys),
749767
model_folder_path, str(validation_location),
750768
class_name=_class_name, method_name=_method_name)
@@ -980,6 +998,27 @@ def __validate_server_group_targeting_limits(self, attribute_name, attribute_val
980998
self._logger.exiting(class_name=_class_name, method_name=__method_name)
981999
return validation_result
9821000

1001+
def __validate_wlsroles_section(self, attribute_name, attribute_value, valid_attr_infos, path_tokens_attr_keys,
1002+
model_folder_path, validation_location, validation_result):
1003+
__method_name = '__validate_wlsroles_section'
1004+
self._logger.entering(class_name=_class_name, method_name=__method_name)
1005+
1006+
# Validate the basics of the WLSRoles section definition
1007+
validation_result = self.__validate_attribute(attribute_name,
1008+
attribute_value,
1009+
valid_attr_infos,
1010+
path_tokens_attr_keys,
1011+
model_folder_path,
1012+
validation_location,
1013+
validation_result)
1014+
1015+
# Validate WebLogic role content using WLSRoles helper
1016+
wlsroles_validator = wlsroles_helper.validator(attribute_value, self._logger)
1017+
validation_result = wlsroles_validator.validate_roles(validation_result)
1018+
1019+
self._logger.exiting(class_name=_class_name, method_name=__method_name)
1020+
return validation_result
1021+
9831022

9841023
def _validate_single_server_group_target_limits_value(key, value, model_folder_path, validation_result):
9851024
if type(value) is str:

0 commit comments

Comments
 (0)