Comprobar permisos de Administrador en una ejecuci贸n (estilo 馃悕 Python)

Habitualmente nos encontramos con la situaci贸n en la que debemos ejecutar ciertos programas o flujos de ejecuci贸n y necesitamos permisos de Administrador. Estas situaciones se nos presentan cuando tenemos que manejar carpetas y ficheros en acciones que impliquen lectura, escritura o ejecuci贸n en directorios protegidos del sistema. Para ello se requiere una elevaci贸n de permisos que deberemos comprobar si existe antes de lanzar nuestra ejecuci贸n.

Aprovechando que seguramente tengamos que hacer esto varias veces, creo que la mejor opci贸n de hacerlo es con el mejor lenguaje pegamento que existe hasta la fecha, que no es ni m谩s ni menos que Python. Por ello la soluci贸n final que vamos a plantear es excelente tanto para administradores de sistemas como para desarrolladores, siendo adem谩s multiplataforma GNU/Linux y Windows.

Tenemos que tener en cuenta que GNU/Linux y Windows no funcionan igual para la gesti贸n de permisos. En Windows preguntaremos desde la API disponible si el usuario es administrador, mientras que en GNU/Linux preguntaremos por si el usuario tiene permisos root.

import ctypes, os
from sys import exit


def is_admin():
    is_admin = False
    try:
        is_admin = os.getuid() == 0
    except AttributeError:
        is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0

    print ("Admin privileges: {}".format(is_admin))
    return is_admin

Para mirar los permisos en Windows usaremos la librer铆a ctypes y para los permisos en GNU/Linux usaremos la librer铆a os. Ambas est谩n incluida en Python. Por una raz贸n arbitrar铆a se ha elegido comprobar primero GNU/Linux y despu茅s Windows.

Cuando se comprueba en GNU/Linux invocamos a la funci贸n os.getuid que nos devolver谩 0 en el caso de ser root. Si llamamos a la funci贸n desde Windows con Python nos elevar谩 una excepci贸n del tipo AttributeError, que aprovecharemos a capturar para hacer la comprobaci贸n en Windows invocando a la funci贸n ctypes.windll.shell32.IsUserAnAdmin. En ambos casos guardaremos el booleano de la evaluaci贸n y lo devolveremos al final de la funci贸n.

Hasta aqu铆 sencillo, pero esto es c贸digo Python y es necesario tener el interprete de Python, con el que obliga al sistema a tener instalado el int茅rprete de Python. Para solucionarlo, deber铆amos convertir nuestro c贸digo en un programa a帽adiendo un punto de entrada y devolviendo un c贸digo de retorno tras la ejecuci贸n.

import ctypes, os
from sys import exit


def is_admin():
    is_admin = False
    is_win = False
    try:
        is_admin = os.getuid() == 0
    except AttributeError:
        is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
        is_win = True

    print ("Admin privileges: {}".format(is_admin))
    return is_admin, is_win

Antes modificaremos un poco la funci贸n is_admin()a帽adiendo un valor is_win que usaremos para saber que estamos comprobando en GNU/Linux o Windows los permisos de administrador, devolviendo el valor como un segundo valor en el retorno de la funci贸n.

Recordad que Python es posible recoger m煤ltiples valores de retorno en una variable como una tupla, en variables separadas o incluso tirar los valores que no nos interesen.

def my_function():
    a = 2
    b = 3
    return a, b

a = my_function()
print(a)  # out: (2, 3)

a, b = my_function()
print(a)  # out: 2
print(b)  # out: 3

a, _ = my_function()  #  b es ignorado
print(a)  # out: 2

Adem谩s a帽adimos un punto de entrada debajo de la funci贸n is_admin().

if __name__ == "__main__":
    is_admin, is_win = is_admin()
    # Converting boolean to integer 
    ret = int(is_admin == False)
    if is_win:
        exit(ret)
    else:
        exit(ret * -1)

Comprobar si __name__ es __main__, es una excelente forma de hacer que cada unidad de fichero Python sea ejecutable si se quiere. El nombre de __main__ s贸lo es dado al fichero Python que es dado al int茅rprete para ser ejecutado y en caso de no serlo, siempre puede ser importado en otro fichero Python, sin que el punto de entrada afecte.

Como vemos, ahora estamos recogiendo en el retorno is_admin e is_win. Necesitamos el segundo valor para decidir el valor de retorno, ya que los c贸digos de error en Windows son enteros positivos y en GNU/Linux son enteros negativos. El 煤nico punto en com煤n es que devolver un 0 es se帽al de que la ejecuci贸n fue correcta. Sabiendo esto debemos adaptar el retorno de los errores dependiendo el sistema.

馃捑 Binarizando nuestra aplicaci贸n Python

Para convertirlo en un ejecutable usaremos PyInstaller, el cual pod茅is instalar con un simple pip install pyinstaller. Para binarizar nuestra aplicaci贸n en un ejecutable autocontenido con el interprete de Python ejecutaremos:

pyinstaller -F is_admin.py

De esta forma resultar谩, mucho mas sencilla la distribuci贸n de nuestra aplicaci贸n.

馃憯 Puesta en Producci贸n

Ahora vamos a plantear los dos casos de uso para Windows y GNU/Linux. Como aclaraci贸n, decir que los ejemplos expuestos se pueden usar tanto con la versi贸n binarizada o con la de c贸digo invocando al int茅rprete de Python.

Windows

@echo off

cd
cd /D "%~dp0"
cd

#REM python is_admin.py
is_admin.exe

if errorlevel 1 (
   echo Exit Code is %errorlevel%
   echo.
   echo Admin privileges required
   echo Run it again as Administrator
   echo.
   pause
   exit /b %errorlevel%
)

#REM [your_executable].exe

pause

馃憖 Puedes descargar la versi贸n binarizada de is_admin.exe

GNU/Linux

#!/bin/bash

pwd
cd `dirname $0`
SCRIPTDIR=`pwd`
pwd

#python is_admin.py
is_admin


if [ $? -eq 0 ]
then
  # [your_executable]
else
  echo Exit Code is $?
  echo.
  echo Admin privileges required
  echo Run it again as Administrator
  echo.
  read -rsp $'Press any key to continue...\n' -n 1 key
  exit $?
fi

read -rsp $'Press any key to continue...\n' -n 1 key

Si no est谩s tan familiarizado con el Bash te invito a que visites el proyecto ExplainShell para que puedas obtener la explicaci贸n de los diferentes comandos y argumentos.

馃 Conclusiones

La soluci贸n presentada, sin ser seguramente perfecta, asegura tanto a administradores de sistemas como desarrolladores, comprobar si existen los permisos de administrador necesarios de manera sencilla; pudiendo hacerlo con una versi贸n binarizada autocontenida que incluya el int茅rpretete Python, ejecut谩ndolo usando el int茅rprete de Python del sistema gracias a ser todo dependencias internas, o llamando directamente a la funci贸n is_admin() si se quiere integrar en un desarrollo.

El c贸digo completo puedes encontrarlo en el siguiente gist de GitHub:

Espero que esta forma de trabajar te resulte 煤til y puedas extrapolarla y usar para otros casos Python como tu lenguaje pegamento generando c贸digo y aplicaciones altamente reutilizables.





309 Visitas Totales 3 Visitas para Hoy

Deja un comentario