# This class is modified from gnome-lirc-properties project
# the original file is gnome_lirc_properties/backend.py
# GPL v2+
# Copyright (C) 2008 Fluendo Embedded S.L.
# Copyright (C) 2010 TualatriX <tualatrix@gmail.com>
# Copyright (C) 2013 ~ 2014 National University of Defense Technology(NUDT) & Kylin Ltd

import os

import dbus
import dbus.service

ALLOWED_CALLER_EXECUTABLES = frozenset({
    '/usr/bin/kylin-assistant',
})

class AccessDeniedException(dbus.DBusException):
    '''This exception is raised when some operation is not permitted.'''

    _dbus_error_name = 'com.kylin.assistant.systemdaemon.AccessDeniedException'


class PolicyKitService(dbus.service.Object):
    '''A D-BUS service that uses PolicyKit for authorization.'''

    def _get_sender_uid(self, sender):
        if not sender:
            raise ValueError('sender == None')
        bus = dbus.SystemBus()
        dbus_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
        dbus_iface = dbus.Interface(dbus_obj, 'org.freedesktop.DBus')
        return int(dbus_iface.GetConnectionUnixUser(sender))

    def _get_sender_pid(self, sender):
        if not sender:
            raise ValueError('sender == None')
        bus = dbus.SystemBus()
        dbus_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
        dbus_iface = dbus.Interface(dbus_obj, 'org.freedesktop.DBus')
        return int(dbus_iface.GetConnectionUnixProcessID(sender))

    def _get_trusted_sender_executable(self, sender):
        pid = self._get_sender_pid(sender)
        executable = os.path.realpath('/proc/%d/exe' % pid)
        try:
            executable_stat = os.stat(executable)
        except OSError:
            return None

        # A whitelisted executable must be installed by root and must not be
        # replaceable by its group or by an unprivileged local user.
        if executable_stat.st_uid != 0 or executable_stat.st_mode & 0o022:
            return None
        return executable

    def _check_caller_whitelist(self, sender):
        try:
            executable = self._get_trusted_sender_executable(sender)
        except (dbus.DBusException, OSError, ValueError):
            return False
        allowed_executables = {
            os.path.realpath(path) for path in ALLOWED_CALLER_EXECUTABLES
        }
        return executable in allowed_executables

    def _check_root_permission(self, sender):
        return self._get_sender_uid(sender) == 0

    def _check_permission(self, sender, action):
        if not sender: raise ValueError('sender == None')
        kit = dbus.SystemBus().get_object('org.freedesktop.PolicyKit1', '/org/freedesktop/PolicyKit1/Authority')
        kit = dbus.Interface(kit, 'org.freedesktop.PolicyKit1.Authority')
        (granted, _, details) = kit.CheckAuthorization(
                        ('system-bus-name', {'name': sender}),
                        action, {}, dbus.UInt32(1), '', timeout=600)
        return granted
