Remove chfn and chsh and allow shell changes in the gui
The chsh and chfn programs were broken anyway.
This commit is contained in:
parent
1231cddf56
commit
9470a42998
140
bin/csc-chfn
140
bin/csc-chfn
|
@ -1,140 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
"""
|
||||
chfn - change real user name and information
|
||||
|
||||
This utility imitates chfn(1) from the shadow password suite, but makes its
|
||||
changes in the LDAP directory rather than in the passwd file.
|
||||
|
||||
When run from an unprivileged account, authentication will be performed
|
||||
before the account information is changed.
|
||||
"""
|
||||
import os, sys, pwd, getopt, PAM
|
||||
from ceo import accounts
|
||||
from ceo.excep import InvalidArgument
|
||||
|
||||
progname = os.path.basename(sys.argv[0])
|
||||
|
||||
OPTION_MAP = {
|
||||
'-f': 'fullname',
|
||||
'-r': 'roomnumber',
|
||||
'-w': 'workphone',
|
||||
'-h': 'homephone',
|
||||
'-o': 'other'
|
||||
}
|
||||
LONG_NAMES = [
|
||||
('fullname', 'Full Name'),
|
||||
('roomnumber', 'Room Number'),
|
||||
('workphone', 'Work Phone'),
|
||||
('homephone', 'Home Phone'),
|
||||
('other', 'Other')
|
||||
]
|
||||
READONLY_FIELDS = [ 'fullname', 'other' ]
|
||||
|
||||
def usage():
|
||||
umesg = "Usage: %s [-f full name] [-r room no] [-w work ph] " + \
|
||||
"[-h home ph] [-o other] [user]"
|
||||
print umesg % progname
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def whoami():
|
||||
uid = os.getuid()
|
||||
username = os.getlogin()
|
||||
if pwd.getpwnam(username).pw_uid != uid:
|
||||
username = pwd.getpwuid(uid).pw_name
|
||||
return (uid, username)
|
||||
|
||||
def authenticate(username):
|
||||
auth = PAM.pam()
|
||||
auth.start('chsh', username)
|
||||
try:
|
||||
auth.authenticate()
|
||||
auth.acct_mgmt()
|
||||
except PAM.error, resp:
|
||||
print "%s: %s" % (progname, resp.args[0])
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
|
||||
pwuid, pwnam = whoami()
|
||||
|
||||
euid = os.geteuid()
|
||||
os.setreuid(euid, euid)
|
||||
|
||||
gecos_params = {}
|
||||
|
||||
try:
|
||||
options, arguments = getopt.gnu_getopt(sys.argv[1:], 'f:r:w:h:o:')
|
||||
for opt, val in options:
|
||||
gecos_params[OPTION_MAP[opt]] = val
|
||||
if len(arguments) > 1:
|
||||
usage()
|
||||
elif len(arguments) == 1:
|
||||
username = arguments[0]
|
||||
else:
|
||||
username = pwnam
|
||||
except getopt.GetoptError, e:
|
||||
usage()
|
||||
|
||||
for field in READONLY_FIELDS:
|
||||
if field in gecos_params and pwuid:
|
||||
print "%s: Permission denied." % progname
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
if pwuid and pwd.getpwnam(username).pw_uid != pwuid:
|
||||
print "%s: Permission denied." % progname
|
||||
sys.exit(1)
|
||||
except KeyError:
|
||||
print "%s: unknown user %s" % (progname, username)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
accounts.connect()
|
||||
gecos_raw = accounts.get_gecos(username)
|
||||
gecos = accounts.parse_gecos(gecos_raw)
|
||||
|
||||
if pwuid:
|
||||
authenticate(username)
|
||||
|
||||
if not gecos_params:
|
||||
print "Changing the user information for %s" % username
|
||||
print "Enter the new value, or press ENTER for the default"
|
||||
for field, longname in LONG_NAMES:
|
||||
if pwuid and field == 'other' and 'other' in READONLY_FIELDS:
|
||||
continue
|
||||
if gecos[field] is None:
|
||||
gecos[field] = ""
|
||||
if field in READONLY_FIELDS and pwuid:
|
||||
print " %s: %s" % (longname, gecos[field])
|
||||
else:
|
||||
print " %s: [%s]:" % (longname, gecos[field]),
|
||||
new_value = raw_input()
|
||||
if new_value:
|
||||
gecos[field] = new_value.strip()
|
||||
else:
|
||||
gecos.update(gecos_params)
|
||||
|
||||
gecos_raw_new = accounts.build_gecos(**gecos)
|
||||
if gecos_raw != gecos_raw_new:
|
||||
accounts.update_gecos(username, gecos_raw_new)
|
||||
|
||||
except InvalidArgument, e:
|
||||
longnames = dict(LONG_NAMES)
|
||||
longname = longnames.get(e.argname, e.argname).lower()
|
||||
print "%s: invalid %s: %s" % (progname, longname, e.argval)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
exceps = ( accounts.ConfigurationException, accounts.LDAPException,
|
||||
accounts.KrbException, accounts.AccountException )
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(130)
|
||||
except IOError, e:
|
||||
print "%s: %s: %s" % (progname, e.filename, e.strerror)
|
||||
sys.exit(1)
|
||||
except exceps, e:
|
||||
print "%s: %s" % (progname, e)
|
||||
sys.exit(1)
|
105
bin/csc-chsh
105
bin/csc-chsh
|
@ -1,105 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
"""
|
||||
chsh - change login shell
|
||||
|
||||
This utility imitates chsh(1) from the shadow password suite, but makes its
|
||||
changes in the LDAP directory rather than in the passwd file.
|
||||
|
||||
When run from an unprivileged account, authentication will be performed
|
||||
before the shell is changed, and the new shell must be listed in /etc/shells.
|
||||
"""
|
||||
import os, sys, pwd, getopt, PAM
|
||||
from ceo import accounts
|
||||
from ceo.excep import InvalidArgument
|
||||
|
||||
progname = os.path.basename(sys.argv[0])
|
||||
|
||||
def usage():
|
||||
print "Usage: %s [-s shell] [username]" % progname
|
||||
sys.exit(2)
|
||||
|
||||
def whoami():
|
||||
uid = os.getuid()
|
||||
username = os.getlogin()
|
||||
if pwd.getpwnam(username).pw_uid != uid:
|
||||
username = pwd.getpwuid(uid).pw_name
|
||||
return (uid, username)
|
||||
|
||||
def authenticate(username):
|
||||
auth = PAM.pam()
|
||||
auth.start('chsh', username)
|
||||
try:
|
||||
auth.authenticate()
|
||||
auth.acct_mgmt()
|
||||
except PAM.error, resp:
|
||||
print "%s: %s" % (progname, resp.args[0])
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
|
||||
pwuid, pwnam = whoami()
|
||||
|
||||
euid = os.geteuid()
|
||||
os.setreuid(euid, euid)
|
||||
|
||||
try:
|
||||
options, arguments = getopt.gnu_getopt(sys.argv[1:], 's:')
|
||||
new_shell = None
|
||||
for opt, val in options:
|
||||
if opt == '-s':
|
||||
new_shell = val
|
||||
if len(arguments) > 1:
|
||||
usage()
|
||||
elif len(arguments) == 1:
|
||||
username = arguments[0]
|
||||
else:
|
||||
username = pwnam
|
||||
except getopt.GetoptError, e:
|
||||
usage()
|
||||
|
||||
try:
|
||||
if pwuid and pwd.getpwnam(username).pw_uid != pwuid:
|
||||
print "%s: You may not change the shell for %s." % (progname, username)
|
||||
sys.exit(1)
|
||||
except KeyError:
|
||||
print "%s: unknown user %s" % (progname, username)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
accounts.connect()
|
||||
current_shell = accounts.get_shell(username)
|
||||
|
||||
if pwuid:
|
||||
authenticate(username)
|
||||
|
||||
if not new_shell:
|
||||
print "Changing the login shell for %s" % username
|
||||
print "Enter the new value, or press ENTER for the default"
|
||||
print " Login Shell [%s]:" % current_shell,
|
||||
new_shell = raw_input()
|
||||
if not new_shell:
|
||||
new_shell = current_shell
|
||||
|
||||
if new_shell != current_shell:
|
||||
accounts.update_shell(username, new_shell, pwuid != 0)
|
||||
|
||||
except InvalidArgument, e:
|
||||
if e.argname == 'shell':
|
||||
print "%s: %s: invalid shell" % (progname, new_shell)
|
||||
sys.exit(1)
|
||||
else:
|
||||
raise
|
||||
|
||||
if __name__ == '__main__':
|
||||
exceps = ( accounts.ConfigurationException, accounts.LDAPException,
|
||||
accounts.KrbException, accounts.AccountException )
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(130)
|
||||
except IOError, e:
|
||||
print "%s: %s: %s" % (progname, e.filename, e.strerror)
|
||||
sys.exit(1)
|
||||
except exceps, e:
|
||||
print "%s: %s" % (progname, e)
|
||||
sys.exit(1)
|
|
@ -9,7 +9,7 @@ Transactions are used in each method that modifies the database.
|
|||
Future changes to the members database that need to be atomic
|
||||
must also be moved into this module.
|
||||
"""
|
||||
import re, subprocess, ldap
|
||||
import os, re, subprocess, ldap
|
||||
from ceo import conf, ldapi
|
||||
from ceo.excep import InvalidArgument
|
||||
|
||||
|
@ -307,6 +307,32 @@ def change_group_member(action, group, userid):
|
|||
|
||||
|
||||
|
||||
### Shells ###
|
||||
|
||||
def get_shell(userid):
|
||||
member = ldapi.lookup(ld, 'uid', userid, cfg['users_base'])
|
||||
if not member:
|
||||
raise NoSuchMember(userid)
|
||||
if 'loginShell' not in member:
|
||||
return
|
||||
return member['loginShell'][0]
|
||||
|
||||
|
||||
def get_shells():
|
||||
return [ sh for sh in open(cfg['shells_file']).read().split("\n")
|
||||
if sh
|
||||
and sh[0] == '/'
|
||||
and not '#' in sh
|
||||
and os.access(sh, os.X_OK) ]
|
||||
|
||||
|
||||
def set_shell(userid, shell):
|
||||
if not shell in get_shells():
|
||||
raise InvalidArgument("shell", shell, "is not in %s" % cfg['shells_file'])
|
||||
ldapi.modify(ld, 'uid', userid, cfg['users_base'], [ (ldap.MOD_REPLACE, 'loginShell', [ shell ]) ])
|
||||
|
||||
|
||||
|
||||
### Clubs ###
|
||||
|
||||
def create_club(username, name):
|
||||
|
|
|
@ -25,11 +25,13 @@ class InfoPage(WizardPanel):
|
|||
name = member.get('cn', [''])[0]
|
||||
userid = self.state['userid']
|
||||
program = member.get('program', [''])[0]
|
||||
shell = member.get('loginShell', [''])[0]
|
||||
terms = member.get('term', [])
|
||||
|
||||
self.name.set_text("Name: %s" % name)
|
||||
self.userid.set_text("User: %s" % userid)
|
||||
self.program.set_text("Program: %s" % program)
|
||||
self.program.set_text("Shell: %s" % shell)
|
||||
self.terms.set_text("Terms: %s" % ", ".join(terms))
|
||||
def check(self):
|
||||
pop_window()
|
||||
|
|
|
@ -2,7 +2,7 @@ import sys, random, ldap, urwid.curses_display
|
|||
from ceo import members, ldapi
|
||||
from ceo.urwid.widgets import *
|
||||
from ceo.urwid.window import *
|
||||
from ceo.urwid import newmember, renew, info, search, positions, groups
|
||||
from ceo.urwid import newmember, renew, info, search, positions, groups, shell
|
||||
|
||||
ui = urwid.curses_display.Screen()
|
||||
|
||||
|
@ -52,15 +52,13 @@ syscom_data = {
|
|||
"groups" : [ "office", "staff", "adm", "src" ],
|
||||
}
|
||||
|
||||
def menu_items(items):
|
||||
return [ urwid.AttrWrap( ButtonText( cb, data, txt ), 'menu', 'selected') for (txt, cb, data) in items ]
|
||||
|
||||
def main_menu():
|
||||
menu = [
|
||||
("New Member", new_member, None),
|
||||
("Renew Membership", renew_member, None),
|
||||
("Create Club Account", new_club, None),
|
||||
("Display Member", display_member, None),
|
||||
("Change Shell", change_shell, None),
|
||||
("Search", search_members, None),
|
||||
("Manage Club or Group Members", manage_group, None),
|
||||
("Manage Positions", manage_positions, None),
|
||||
|
@ -136,6 +134,14 @@ def manage_positions(data):
|
|||
positions.EndPage,
|
||||
], (50, 15))
|
||||
|
||||
def change_shell(data):
|
||||
push_wizard("Change Shell", [
|
||||
shell.IntroPage,
|
||||
shell.YouPage,
|
||||
shell.ShellPage,
|
||||
shell.EndPage
|
||||
], (50, 15))
|
||||
|
||||
def run():
|
||||
push_window( main_menu(), program_name() )
|
||||
event_loop( ui )
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import urwid, ldap, pwd, os
|
||||
from ceo import members, terms, ldapi
|
||||
from ceo.urwid.widgets import *
|
||||
from ceo.urwid.window import *
|
||||
|
||||
class IntroPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
self.widgets = [
|
||||
urwid.Text( "Changing Login Shell" ),
|
||||
urwid.Divider(),
|
||||
urwid.Text( "You can change your shell here. Request more shells "
|
||||
"by emailing systems-committee." )
|
||||
]
|
||||
def focusable(self):
|
||||
return False
|
||||
|
||||
class YouPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
you = pwd.getpwuid(os.getuid()).pw_name
|
||||
self.userid = WordEdit("Username: ", you)
|
||||
|
||||
self.widgets = [
|
||||
urwid.Text( "Member Information" ),
|
||||
urwid.Divider(),
|
||||
self.userid,
|
||||
]
|
||||
def check(self):
|
||||
self.state['userid'] = self.userid.get_edit_text()
|
||||
self.state['member'] = None
|
||||
if self.state['userid']:
|
||||
self.state['member'] = members.get(self.userid.get_edit_text())
|
||||
if not self.state['member']:
|
||||
set_status("Member not found")
|
||||
self.focus_widget(self.userid)
|
||||
return True
|
||||
|
||||
class ShellPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
self.midtext = urwid.Text("")
|
||||
|
||||
self.widgets = [
|
||||
urwid.Text("Choose a Shell"),
|
||||
urwid.Divider(),
|
||||
]
|
||||
|
||||
def set_shell(radio_button, new_state, shell):
|
||||
if new_state:
|
||||
self.state['shell'] = shell
|
||||
|
||||
radio_group = []
|
||||
self.shells = members.get_shells()
|
||||
self.shellw = [ urwid.RadioButton(radio_group, shell,
|
||||
on_state_change=set_shell, user_data=shell)
|
||||
for shell in self.shells ]
|
||||
|
||||
self.widgets.extend(self.shellw)
|
||||
def set_shell(self, shell):
|
||||
i = self.shells.index(shell)
|
||||
self.shellw[i].set_state(True)
|
||||
def focusable(self):
|
||||
return True
|
||||
def activate(self):
|
||||
self.set_shell(self.state['member']['loginShell'][0])
|
||||
|
||||
class EndPage(WizardPanel):
|
||||
def init_widgets(self):
|
||||
self.headtext = urwid.Text("")
|
||||
self.midtext = urwid.Text("")
|
||||
|
||||
self.widgets = [
|
||||
self.headtext,
|
||||
urwid.Divider(),
|
||||
self.midtext,
|
||||
]
|
||||
def focusable(self):
|
||||
return False
|
||||
def activate(self):
|
||||
problem = None
|
||||
try:
|
||||
user, shell = self.state['userid'], self.state['shell']
|
||||
members.set_shell(user, shell)
|
||||
self.headtext.set_text("Login Shell Changed")
|
||||
self.midtext.set_text("The shell for %s has been changed to %s."
|
||||
% (user, shell))
|
||||
except ldap.LDAPError, e:
|
||||
problem = ldapi.format_ldaperror(e)
|
||||
except members.MemberException, e:
|
||||
problem = str(e)
|
||||
if problem:
|
||||
self.headtext.set_text("Failed to Change Shell")
|
||||
self.midtext.set_text("Perhaps you don't have permission to change %s's shell? "
|
||||
"The error was:\n\n%s" % (user, problem))
|
||||
def check(self):
|
||||
pop_window()
|
|
@ -2,6 +2,9 @@ import urwid
|
|||
from ceo.urwid.ldapfilter import *
|
||||
from ceo.urwid.window import raise_back, push_window
|
||||
|
||||
def menu_items(items):
|
||||
return [ urwid.AttrWrap( ButtonText( cb, data, txt ), 'menu', 'selected') for (txt, cb, data) in items ]
|
||||
|
||||
def push_wizard(name, pages, dimensions=(50, 10)):
|
||||
state = {}
|
||||
wiz = Wizard()
|
||||
|
|
Loading…
Reference in New Issue