File: endUser.py

package info (click to toggle)
subuser 0.6.2-3
  • links: PTS
  • area: main
  • in suites: bookworm, bullseye, buster, forky, sid, trixie
  • size: 4,208 kB
  • sloc: python: 5,201; sh: 380; makefile: 73
file content (156 lines) | stat: -rw-r--r-- 4,869 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# -*- coding: utf-8 -*-

"""
The ``EndUser`` object the object that represents the user account owned by the human user of the system. It is possible to run subuser using a different user account, in order to isolate root from the end user's user account.
"""
#external imports
import getpass
import os
import sys
import pwd
import subprocess
#internal imports
from subuserlib import test
from subuserlib import paths
from subuserlib.classes.userOwnedObject import UserOwnedObject

def timeit(func):
    import functools,time
    @functools.wraps(func)
    def newfunc(*args, **kwargs):
        startTime = time.time()
        ret = func(*args, **kwargs)
        elapsedTime = time.time() - startTime
        print('function [{}] finished in {} ms'.format(
            func.__name__, int(elapsedTime * 1000)))
        return ret
    return newfunc


class EndUser(UserOwnedObject,object):
  def __init__(self,user,name=None):
    UserOwnedObject.__init__(self,user)
    self.proxiedByOtherUser = False
    self.sudo = False
    self.name = name
    try:
      self.name = self.user.config["user"]
      self.proxiedByOtherUser = True
    except KeyError:
      try:
        self.name = os.environ["SUDO_USER"]
        self.sudo = True
        self.proxiedByOtherUser = True
      except KeyError:
        try:
          self.name = getpass.getuser()
        except KeyError:
          # We use a broken setup when generating documentation...
          self.name = "I have no name!"
    self.uid = 1000
    self.gid = 1000
    if not test.testing:
      if self.sudo:
        self.uid = int(os.environ.get('SUDO_UID'))
        self.gid = int(os.environ.get('SUDO_GID'))
      else:
        try:
          self.uid = pwd.getpwnam(self.name)[2]
          self.gid = pwd.getpwnam(self.name)[3]
        except KeyError:
          pass
    if not self.uid == 0:
      self.homeDir = os.path.join("/home/",self.name)
    else:
      self.homeDir = "/root/"

  def chown(self,path):
    """
    Make this user own the given file if subuser is running as root.
    """
    if self.proxiedByOtherUser:
      os.chown(path,self.uid,self.gid)

  #Thanks https://stackoverflow.com/questions/25791311/creating-a-file-with-python-using-sudo-makes-its-owner-root
  def create_file(self,path):
    #create containing folder
    directory,_ = os.path.split(path)
    self.makedirs(directory)
    #create file normally
    open(path, 'a').close()
    # then fix the ownership
    self.chown(path)

  def get_file(self,path, mode="a+"):
    """Create a file if it does not exists, fix ownership and return it open"""
    self.create_file(path)
    # open the file and return it
    return open(path, mode)

  def makedirs(self,path):
    """
    Create directory + parents, if the directory does not yet exist. Newly created directories will be owned by the user.
    """
    # Taken from https://stackoverflow.com/questions/3167154/how-to-split-a-dos-path-into-its-components-in-python
    folders = []
    path = os.path.realpath(path)
    while 1:
      path, folder = os.path.split(path)
      if folder:
        folders.append(folder)
      else:
        if path:
          folders.append(path)
        break
    pathBeingBuilt = "/"
    for folder in reversed(folders):
      pathBeingBuilt = os.path.join(pathBeingBuilt,folder)
      if not os.path.exists(pathBeingBuilt):
        self.mkdir(pathBeingBuilt)

  def mkdir(self,path):
    os.mkdir(path)
    self.chown(path)

  def getSudoArgs(self):
    if self.proxiedByOtherUser:
      return ["sudo","--user",self.name]
    else:
      return []

  def call(self,command,cwd=None):
    process = subprocess.Popen(self.getSudoArgs()+command,cwd=cwd)
    (stdout,stderr) = process.communicate()
    return process.returncode
  
  #@timeit
  def callCollectOutput(self,args,cwd=None):
    """
    Run the command and return a tuple with: (returncode,the output to stdout as a string,stderr as a string).
    """
    args = self.getSudoArgs() + args
    #print(args)
    process = subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.PIPE,cwd=cwd)
    (stdout,stderr) = process.communicate()
    return (process.returncode,stdout.decode("utf-8"),stderr.decode("utf-8"))

  def Popen(self,command,*args,**kwargs):
    return subprocess.Popen(self.getSudoArgs()+command,*args,**kwargs)

  def runEditor(self,filePath):
    """
    Launch a file editor and edit the given filePath.
    """
    try:
      editor = os.environ["EDITOR"]
    except KeyError:
      editor = "/usr/bin/nano"
    def actuallyRunEditor(editor,filePath):
      try:
        self.call([editor,filePath])
      except FileNotFoundError:
        if test.testing:
          return
        editor = input(editor+" not found. Please enter the name of your favorite editor:")
        actuallyRunEditor(editor,filePath)
    actuallyRunEditor(editor,filePath)