Skip to content

Commit b90f7e8

Browse files
committed
Add base class implementation for local users' passwords reset
1 parent 0362460 commit b90f7e8

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'''
2+
local_users_passwords_reset_base.py
3+
4+
Abstract base class for implementing platform-specific
5+
local users' passwords reset base functionality for SONiC
6+
'''
7+
try:
8+
import json
9+
import subprocess
10+
11+
from sonic_py_common.logger import Logger
12+
except ImportError as e:
13+
raise ImportError (str(e) + "- required module not found")
14+
15+
# Global logger class instance
16+
logger = Logger()
17+
18+
19+
DEFAULT_USERS_FILEPATH = '/etc/sonic/default_users.json'
20+
21+
22+
class LocalUsersConfigurationResetBase(object):
23+
"""
24+
Abstract base class for resetting local users' passwords on the switch
25+
"""
26+
def should_trigger(self):
27+
'''
28+
define the condition to trigger
29+
'''
30+
# the condition to trigger start() method, the default implementation will be by checking if a long reboot press was detected.
31+
raise NotImplementedError
32+
33+
@staticmethod
34+
def reset_password(user, hashed_password, expire=False):
35+
'''
36+
This method is used to reset the user's password and expire it (optional) using Linux shell commands.
37+
'''
38+
# Use 'chpasswd' shell command to change password
39+
subprocess.call([f"echo '{user}:{hashed_password}' | sudo chpasswd -e"], shell=True)
40+
if expire:
41+
# Use 'passwd' shell command to expire password
42+
subprocess.call(['sudo', 'passwd', '-e', f'{user}'])
43+
44+
def start(self):
45+
'''
46+
The functionality defined is to restore original password and expire it for default local users.
47+
It is done by reading default users file and resetting passwords using Linux shell commands.
48+
'''
49+
default_users = {}
50+
51+
# Fetch local users information from default_users
52+
with open(DEFAULT_USERS_FILEPATH) as f:
53+
default_users = json.load(f)
54+
55+
logger.log_info('Restoring default users\' passwords and expiring them')
56+
for user in default_users.keys():
57+
hashed_password = default_users.get(user, {}).get('password')
58+
if hashed_password:
59+
self.reset_password(user, hashed_password, expire=True)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'''
2+
Test LocalUsersConfigurationResetBase module
3+
'''
4+
import subprocess
5+
import pytest
6+
from mock import patch, mock_open
7+
from sonic_platform_base.local_users_passwords_reset_base import LocalUsersConfigurationResetBase
8+
9+
10+
DEFAULT_USERS_JSON_EXAMPLE_OUTPUT = '''
11+
{
12+
"admin": {
13+
"expire": "false",
14+
"password": "HASHED_PASSWORD_123"
15+
}
16+
}
17+
'''
18+
19+
20+
class TestLocalUsersConfigurationResetBase:
21+
'''
22+
Collection of LocalUsersConfigurationResetBase test methods
23+
'''
24+
@staticmethod
25+
def test_local_users_passwords_reset_base():
26+
'''
27+
Verify unimplemented methods
28+
'''
29+
base = LocalUsersConfigurationResetBase()
30+
not_implemented_methods = [
31+
(base.should_trigger,)]
32+
33+
for method in not_implemented_methods:
34+
expected_exception = False
35+
try:
36+
func = method[0]
37+
args = method[1:]
38+
func(*args)
39+
except Exception as exc:
40+
expected_exception = isinstance(exc, NotImplementedError)
41+
assert expected_exception
42+
43+
@patch('subprocess.call')
44+
def test_reset_passwords_method(self, mock_subproc_call):
45+
'''
46+
Test the reset passwords static method
47+
'''
48+
LocalUsersConfigurationResetBase.reset_password(
49+
user='admin',
50+
hashed_password='HASHED_PASSWORD_123',
51+
expire=True)
52+
mock_subproc_call.assert_any_call(["echo 'admin:HASHED_PASSWORD_123' | sudo chpasswd -e"], shell=True)
53+
mock_subproc_call.assert_any_call(['sudo', 'passwd', '-e', 'admin'])
54+
55+
@patch('subprocess.call')
56+
@patch('sonic_platform.utils.read_int_from_file')
57+
@patch("builtins.open", new_callable=mock_open, read_data=DEFAULT_USERS_JSON_EXAMPLE_OUTPUT)
58+
def test_basic_flow_resetting_users_triggered(self, mock_open, mock_read_int, mock_subproc_call):
59+
'''
60+
Test the basic flow of resetting local users when long button press is detected
61+
'''
62+
# Mock long reset button press
63+
mock_read_int.return_value = int(1)
64+
LocalUsersConfigurationResetBase().start()
65+
mock_subproc_call.assert_any_call(["echo 'admin:HASHED_PASSWORD_123' | sudo chpasswd -e"], shell=True)
66+
mock_subproc_call.assert_any_call(['sudo', 'passwd', '-e', 'admin'])

0 commit comments

Comments
 (0)