You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
input-remapper/tests/testcases/test_permissions.py

167 lines
5.3 KiB
Python

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# key-mapper - GUI for device specific keyboard mappings
# Copyright (C) 2020 sezanzeb <proxima@hip70890b.de>
#
# This file is part of key-mapper.
#
# key-mapper is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# key-mapper is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with key-mapper. If not, see <https://www.gnu.org/licenses/>.
import os
import grp
import getpass
import subprocess
import unittest
from keymapper.dev.permissions import check_injection_rights, check_group, \
can_read_devices
from keymapper.paths import USER
from keymapper.daemon import is_service_running
original_access = os.access
original_getgrnam = grp.getgrnam
original_check_output = subprocess.check_output
original_stat = os.stat
oringal_getuser = getpass.getuser
class TestPermissions(unittest.TestCase):
def tearDown(self):
# reset all fakes
os.access = original_access
grp.getgrnam = original_getgrnam
subprocess.check_output = original_check_output
os.stat = original_stat
getpass.getuser = oringal_getuser
def test_check_injection_rights(self):
can_access = False
os.access = lambda *args: can_access
self.assertIsNotNone(check_injection_rights())
can_access = True
self.assertIsNone(check_injection_rights())
def fake_setup(self):
"""Patch some functions to have the following fake environment:
Groups
------
input: id: 0, members: $USER, used in /dev, set up
plugdev: id: 1, members: $USER, used in /dev, not in `groups`
foobar: id: 2, no members, used in /dev
a_unused: id: 0, members: $USER, not used in /dev, set up
b_unused: id: 1, members: $USER, not used in /dev, not in `groups`
c_unused: id: 2, no members, not used in /dev
"""
gr_mems = {
'input': (0, [USER]),
'plugdev': (1, [USER]),
'foobar': (2, []),
'a_unused': (3, [USER]),
'b_unused': (4, [USER]),
'c_unused': (5, [])
}
stat_counter = 0
class stat:
def __init__(self, path):
nonlocal stat_counter
stat_counter += 1
# make sure stat returns all of those groups at some point.
# only works if there are more than three files in /dev, which
# should be the case
self.st_gid = [0, 1, 2][stat_counter % 3]
os.stat = stat
class getgrnam:
def __init__(self, group):
if group not in gr_mems:
raise KeyError()
self.gr_gid = gr_mems[group][0]
self.gr_mem = gr_mems[group][1]
grp.getgrnam = getgrnam
def fake_check_output(cmd):
# fake the `groups` output to act like the current session only
# has input and a_unused active
if cmd[0] == 'groups':
return b'foo input a_unused bar'
return original_check_output(cmd)
subprocess.check_output = fake_check_output
def test_can_read_devices(self):
self.fake_setup()
self.assertFalse(is_service_running())
# root user doesn't need this stuff
getpass.getuser = lambda: 'root'
self.assertEqual(len(can_read_devices()), 0)
getpass.getuser = lambda: USER
os.access = lambda *args: False
# plugdev not yet setup correctly and cannot write
self.assertEqual(len(can_read_devices()), 2)
os.access = lambda *args: True
self.assertEqual(len(can_read_devices()), 1)
subprocess.check_output = lambda cmd: b'plugdev input'
self.assertEqual(len(can_read_devices()), 0)
def test_check_group(self):
self.fake_setup()
# correctly setup
self.assertIsNone(check_group('input'))
# session restart required, usermod already done
self.assertIsNotNone(check_group('plugdev'))
self.assertIn('plugdev', check_group('plugdev'))
self.assertIn('session', check_group('plugdev'))
# usermod required
self.assertIsNotNone(check_group('foobar'))
self.assertIn('foobar', check_group('foobar'))
self.assertIn('group', check_group('foobar'))
# don't exist in /dev
self.assertIsNone(check_group('a_unused'))
self.assertIsNone(check_group('b_unused'))
self.assertIsNone(check_group('c_unused'))
# group doesn't exist
self.assertIsNone(check_group('qux'))
def file_not_found_error(cmd):
raise FileNotFoundError()
subprocess.check_output = file_not_found_error
# groups command doesn't exist, so cannot check this suff
self.assertIsNone(check_group('plugdev'))
# which doesn't affect the grp lib
self.assertIsNotNone(check_group('foobar'))
if __name__ == "__main__":
unittest.main()