@@ -282,6 +282,29 @@ def register_command_info(aliases, command_info):
282
282
user_report_parser .exit = suppress_exit
283
283
284
284
285
+ _DEFAULT_PASSWORD_COMPLEXITY = """[
286
+ {
287
+ "domains": ["_default_"],
288
+ "length": 10,
289
+ "lower-use": true,
290
+ "lower-min": 2,
291
+ "upper-use": true,
292
+ "upper-min": 2,
293
+ "digit-use": true,
294
+ "digit-min": 2,
295
+ "special-use": true,
296
+ "special-min": 2,
297
+ "special": "!@#$%^?();'\\ \" ,.=+[]<>{}&",
298
+ "passphrase-allow": true,
299
+ "passphrase-length": 5,
300
+ "passphrase-capitalize": false,
301
+ "passphrase-number": false,
302
+ "passphrase-separator": "-",
303
+ "apply-privacy-screen": true
304
+ }
305
+ ]"""
306
+
307
+
285
308
def get_user_status_dict (user ):
286
309
287
310
def lock_text (lock ):
@@ -1988,9 +2011,15 @@ class EnterpriseRoleCommand(EnterpriseCommand):
1988
2011
def get_parser (self ):
1989
2012
return enterprise_role_parser
1990
2013
2014
+ @staticmethod
2015
+ def expand_file_path (filepath ): # type: (str) -> string
2016
+ if not os .path .isfile (filepath ):
2017
+ filepath = os .path .expanduser (filepath )
2018
+ return filepath
2019
+
1991
2020
@staticmethod
1992
2021
def enforcement_value_from_file (filepath ):
1993
- filepath = os . path . expanduser (filepath )
2022
+ filepath = EnterpriseRoleCommand . expand_file_path (filepath )
1994
2023
if os .path .isfile (filepath ):
1995
2024
with open (filepath , 'r' , encoding = 'utf-8' ) as f :
1996
2025
enforcement_value = f .read ()
@@ -2130,17 +2159,44 @@ def execute(self, params, **kwargs):
2130
2159
logging .warning ('Enforcement \" %s\" does not exist' , key )
2131
2160
continue
2132
2161
enforcement_value = tokens [1 ].strip ()
2162
+ if enforcement_type == 'password_complexity' and enforcement_value :
2163
+ if not enforcement_value .startswith (file_prefix ):
2164
+ logging .warning ('Enforcement "%s" can be set only from a file.' , key )
2165
+ logging .warning ('Use the following syntax "%s:%s<FILEPATH>" to set enforcement value' , key , file_prefix )
2166
+ logging .warning ('If file does not exist it will be created with the current or default values' )
2167
+ continue
2133
2168
if enforcement_value .startswith (file_prefix ):
2134
2169
# Get value from file
2135
2170
filepath = enforcement_value [len (file_prefix ):]
2136
2171
if filepath :
2137
- enforcement_value = self .enforcement_value_from_file (filepath )
2138
- if enforcement_value is None :
2139
- logging .warning (f'Could not load enforcement value from "{ filepath } "' )
2172
+ filepath = self .expand_file_path (filepath )
2173
+ if os .path .isfile (filepath ):
2174
+ enforcement_value = self .enforcement_value_from_file (filepath )
2175
+ if enforcement_value is None :
2176
+ logging .warning (f'Could not load enforcement value from "{ filepath } "' )
2177
+ continue
2178
+ else :
2179
+ template_value = ''
2180
+ if enforcement_type == 'password_complexity' :
2181
+ if len (matched_roles ) == 1 :
2182
+ role_id = matched_roles [0 ]['role_id' ]
2183
+ role_enforcements = params .enterprise .get ('role_enforcements' ) or []
2184
+ enforcements = next ((x ['enforcements' ] for x in role_enforcements if x ['role_id' ] == role_id ), None )
2185
+ template_value = enforcements .get (key ) if enforcements else None
2186
+ if template_value :
2187
+ try :
2188
+ _ = json .loads (template_value )
2189
+ except :
2190
+ template_value = ''
2191
+ if not template_value :
2192
+ template_value = _DEFAULT_PASSWORD_COMPLEXITY
2193
+ if template_value :
2194
+ with open (filepath , 'wt' ) as fd :
2195
+ fd .write (template_value )
2196
+ logging .warning ('Enforcement "%s" value has been stored to file "%s"' , key , filepath )
2197
+ else :
2198
+ logging .warning ('Enforcement "%s" is skipped. Expected format: KEY:$FILE=<FILEPATH>' , key )
2140
2199
continue
2141
- else :
2142
- logging .warning (f'Enforcement { key } is skipped. Expected format: KEY:$FILE=<FILEPATH>' )
2143
- continue
2144
2200
if enforcement_value :
2145
2201
if enforcement_type == 'long' :
2146
2202
try :
@@ -2286,6 +2342,40 @@ def execute(self, params, **kwargs):
2286
2342
else :
2287
2343
logging .warning ('Enforcement \" %s\" . Role \" %s\" does not have \" TRANSFER_ACCOUNT\" privilege' , key , role ['data' ].get ('displayname' , '' ))
2288
2344
continue
2345
+ elif enforcement_type == 'password_complexity' :
2346
+ try :
2347
+ complexity = json .loads (enforcement_value )
2348
+ except Exception as e :
2349
+ logging .warning ('Error parsing "%s" enforcement JSON value: %s' , key , e )
2350
+ continue
2351
+ if not isinstance (complexity , list ):
2352
+ if isinstance (complexity , dict ):
2353
+ complexity = [complexity ]
2354
+ else :
2355
+ logging .warning ('Enforcement "%s" should be a JSON array of complexity objects' , key )
2356
+ continue
2357
+
2358
+ dc = json .loads (_DEFAULT_PASSWORD_COMPLEXITY )
2359
+ default_complexity = dc [0 ]
2360
+ errors = []
2361
+ for pc in complexity :
2362
+ if not isinstance (pc , dict ):
2363
+ errors .append (f'Enforcement "{ key } " should be a JSON array of complexity objects' )
2364
+ break
2365
+ for c_key , c_value in pc .items ():
2366
+ if c_key in default_complexity :
2367
+ default_type = type (default_complexity [c_key ])
2368
+ if not isinstance (c_value , default_type ):
2369
+ errors .append (f'Property "{ c_key } " of complexity objects should be a { default_type .__name__ } ' )
2370
+
2371
+ domains = pc .get ('domains' )
2372
+ if not isinstance (domains , list ):
2373
+ errors .append (f'Enforcement complexity object should contain "domains" property: a list of domains or ["_default_"]' )
2374
+ break
2375
+ if len (errors ) > 0 :
2376
+ for error in errors :
2377
+ logging .warning (error )
2378
+ continue
2289
2379
else :
2290
2380
logging .warning ('Enforcement \" %s\" . Value type \" %s\" is not supported' , key , enforcement_type )
2291
2381
continue
0 commit comments