diff --git a/fas/client/fas.conf b/fas/client/fas.conf index cf4fbcf..7e18c20 100644 --- a/fas/client/fas.conf +++ b/fas/client/fas.conf @@ -11,6 +11,9 @@ login = admin ; password - password for login name password = admin +; prefix - Install db files, etc, to a prefix (like a chroot for example) +prefix = / + [host] ; Group hierarchy is 1) groups, 2) restricted_groups 3) ssh_restricted_groups ; so if someone is in all 3, the client behaves the same as if they were just diff --git a/fas/client/fasClient b/fas/client/fasClient index 8acfa62..3e3097c 100755 --- a/fas/client/fasClient +++ b/fas/client/fasClient @@ -78,6 +78,11 @@ parser.add_option('-s', '--server', default = None, metavar = 'FAS_URL', help = _('Specify URL of fas server.')) +parser.add_option('-p', '--prefix', + dest = 'prefix', + default = None, + metavar = 'prefix', + help = _('Specify install prefix. Useful for testing')) parser.add_option('-e', '--enable', dest = 'enable', default = False, @@ -114,6 +119,10 @@ except ConfigParser.MissingSectionHeaderError, e: sys.exit(6) FAS_URL = config.get('global', 'url').strip('"') +if opts.prefix: + prefix = opts.prefix +else: + prefix = config.get('global', 'prefix').strip('"') def _chown(arg, dir_name, files): os.chown(dir_name, arg[0], arg[1]) @@ -131,7 +140,7 @@ class MakeShellAccounts(BaseClient): usernames = {} def mk_tempdir(self): - self.temp = tempfile.mkdtemp('-tmp', 'fas-', config.get('global', 'temp').strip('"')) + self.temp = tempfile.mkdtemp('-tmp', 'fas-', os.path.join(prefix + config.get('global', 'temp').strip('"'))) def rm_tempdir(self): rmtree(self.temp) @@ -235,7 +244,7 @@ class MakeShellAccounts(BaseClient): return '/sbin/nologin' def install_aliases_txt(self): - move(self.temp + '/aliases', '/etc/aliases') + move(self.temp + '/aliases', prefix + '/etc/aliases') def passwd_text(self, people=None): i = 0 @@ -381,25 +390,25 @@ class MakeShellAccounts(BaseClient): def install_passwd_db(self): try: - move(self.temp + '/passwd.db', '/var/db/passwd.db') + move(self.temp + '/passwd.db', os.path.join(prefix + '/var/db/passwd.db')) except IOError, e: print "ERROR: Could not write passwd db - %s" % e def install_shadow_db(self): try: - move(self.temp + '/shadow.db', '/var/db/shadow.db') + move(self.temp + '/shadow.db', os.path.join(prefix + '/var/db/shadow.db')) except IOError, e: print "ERROR: Could not write shadow db - %s" % e def install_group_db(self): try: - move(self.temp + '/group.db', '/var/db/group.db') + move(self.temp + '/group.db', os.path.join(prefix + '/var/db/group.db')) except IOError, e: print "ERROR: Could not write group db - %s" % e def create_homedirs(self): ''' Create homedirs and home base dir if they do not exist ''' - home_base = config.get('users', 'home').strip('"') + home_base = os.path.join(prefix + config.get('users', 'home').strip('"')) if not os.path.exists(home_base): os.makedirs(home_base, mode=0755) for person in self.people: @@ -411,7 +420,7 @@ class MakeShellAccounts(BaseClient): def remove_stale_homedirs(self): ''' Remove homedirs of users that no longer have access ''' - home_base = config.get('users', 'home').strip('"') + home_base = os.path.join(prefix + config.get('users', 'home').strip('"')) try: home_backup_dir = config.get('users', 'home_backup_dir').strip('"') except ConfigParser.NoOptionError: @@ -423,7 +432,7 @@ class MakeShellAccounts(BaseClient): os.makedirs(home_backup_dir) syslog.syslog('Backed up %s to %s' % (user, home_backup_dir)) target = '%s-%s' % (user, time.mktime(datetime.datetime.now().timetuple())) - move(os.path.join(home_base, user), os.path.join(home_backup_dir, target)) + move(os.path.join(home_base, user), os.path.join(prefix + home_backup_dir, target)) def create_ssh_keys(self): ''' Create ssh keys ''' diff --git a/fas/fas2.sql b/fas/fas2.sql index a419238..feedd0c 100644 --- a/fas/fas2.sql +++ b/fas/fas2.sql @@ -45,6 +45,9 @@ CREATE TABLE people ( password VARCHAR(127) NOT NULL, passwordtoken text null, password_changed TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + email TEXT not null unique, + emailtoken TEXT, + unverified_email TEXT, comments TEXT, postal_address TEXT, telephone TEXT, @@ -70,35 +73,6 @@ CREATE TABLE people ( create index people_status_idx on people(status); cluster people_status_idx on people; -CREATE TABLE person_emails ( - id serial primary key, - email text not null, - person_id INTEGER NOT NULL references people(id), - validtoken text, - description text, - verified boolean NOT NULL DEFAULT false, - creation TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - unique (id, person_id), - unique (email, verified) --You can't "claim" an email before you verify it first -); - -create index person_emails_person_id_idx on person_emails(person_id); -cluster person_emails_person_id_idx on person_emails; - -CREATE TABLE email_purposes ( - email_id INTEGER NOT NULL references person_emails(id), - person_id INTEGER NOT NULL references people(id), - purpose text NOT NULL, - primary key (person_id, purpose), - foreign key (email_id, person_id) references person_emails(id, - person_id) on update cascade, - check (purpose ~ ('(bugzilla|primary|cla|pending|other[0-9]+)')) -); - -create index email_purposes_email_id_idx on email_purposes(email_id); -create index email_purposes_person_id_idx on email_purposes(person_id); -cluster email_purposes_person_id_idx on email_purposes; - CREATE TABLE configs ( id SERIAL PRIMARY KEY, person_id integer references people(id), @@ -123,6 +97,10 @@ CREATE TABLE groups ( name VARCHAR(32) UNIQUE NOT NULL, -- tg_group::display_name display_name TEXT, + -- Unlike users, groups can share email addresses + email TEXT not null, + emailtoken TEXT, + unverified_email TEXT, owner_id INTEGER NOT NULL REFERENCES people(id), group_type VARCHAR(16), needs_sponsor BOOLEAN DEFAULT FALSE, @@ -136,41 +114,9 @@ CREATE TABLE groups ( ); create index groups_group_type_idx on groups(group_type); +create index groups_email_idx on groups(email); cluster groups_group_type_idx on groups; --- --- Group Emails are slightly different than person emails. --- We are much more relaxed about email "ownership". A group can share an --- email address with another group. (For instance, xen-maint and --- kernel-maint might share the same email address for mailing list and --- bugzilla. --- -CREATE TABLE group_emails ( - id serial primary key, - email text not null, - group_id INTEGER NOT NULL references groups(id), - validtoken text, - description text, - verified boolean NOT NULL DEFAULT false, - creation TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - unique (email, verified) --You can't "claim" an email before you verify it first -); - -create index group_emails_group_id_idx on group_emails(group_id); -cluster group_emails_group_id_idx on group_emails; - -CREATE TABLE group_email_purposes ( - email_id INTEGER NOT NULL references group_emails(id), - group_id INTEGER NOT NULL references groups(id), - purpose text NOT NULL, - primary key (group_id, purpose), - check (purpose ~ ('(bugzilla|primary|mailing list|other[0-9]+)')) -); - -create index group_email_purposes_email_id_idx on group_email_purposes(email_id); -create index group_email_purposes_person_id_idx on group_email_purposes(group_id); -cluster group_email_purposes_person_id_idx on group_email_purposes; - CREATE TABLE person_roles ( person_id INTEGER NOT NULL REFERENCES people(id), group_id INTEGER NOT NULL REFERENCES groups(id), @@ -389,6 +335,16 @@ create or replace function bugzilla_sync_email() returns trigger AS $bz_sync_e$ emailAffectsBz = True return emailAffectsBz + def previous_emails(person_id): + '''Find the previous email used for bugzilla.''' + plan = plpy.prepare("select email, purpose from person_emails as pem," + " email_purposes as epu" + " where pem.id = epu.email_id and pem.person_id = $1" + " and epu.purpose in ('bugzilla', 'primary')", ('int4',)) + result = plpy.execute(plan, (TD['new']['person_id'],)) + email = None + return result + # # Main body of function starts here # @@ -445,42 +401,51 @@ create or replace function bugzilla_sync_email() returns trigger AS $bz_sync_e$ # use with bugzilla. if oldHasBugs and newHasBugs and newAffectsBz: # Retrieve the bugzilla email address - plan = plpy.prepare("select email, purpose from person_emails as pem," - " email_purposes as epu" - " where pem.id = epu.email_id and pem.person_id = $1" - " and epu.purpose in ('bugzilla', 'primary')", ('int4',)) - result = plpy.execute(plan, (TD['new']['person_id'],)) - email = None - bzEmail = False - for record in result: - email = record['email'] - if record['purpose'] == 'bugzilla': - bzEmail = True - break + previous = previous_emails(TD['new']['person_id']) + # Note: we depend on the unique constraint having already run and # stopped us from getting to this point with two email addresses # for the same purpose. # Since only one can be the bzEmail address and only one the # primary, we can do what we need only knowing the purpose for one # of the email addresses. - if bzEmail: - # Remove the new email address as the old one is the bz email - changes[TD['new']['email']] = (TD['new']['email'], fedorabugsId, TD['new']['person_id'], 'r') - else: - # Remove the current email address - changes[email] = (email, fedorabugsId, TD['new']['person_id'], 'r') + if previous: + + for email in previous: + if email['purpose'] == 'bugzilla': + # Remove the new email address as the old one is the bz email + changes[TD['new']['email']] = (TD['new']['email'], fedorabugsId, TD['new']['person_id'], 'r') + else: + # Remove the current email address + changes[email] = (email, fedorabugsId, TD['new']['person_id'], 'r') if TD['new']['verified'] != TD['old']['verified']: + plpy.execute("insert into debug values ('In verified')") if TD['new']['verified'] and newHasBugs and newAffectsBz: # Add the email address + plpy.execute("insert into debug values('Add email address')") if not TD['new']['email'] in changes: + plpy.execute("insert into debug values ('addind address for real')") changes[TD['new']['email']] = (TD['new']['email'], fedorabugsId, TD['new']['person_id'], 'a') - + # Check whether there's a previous email address this + # obsoletes + previous = previous_email(TD['new']['person_id']) + plan = plpy.prepare("insert into debug values ($1)", ('text',)) + plpy.execute(plan, (str(previous),)) + if previous and previous[0] == 'primary': + changes[previous[1]] = (previous[1], fedorabugsId, TD['new']['person_id'], 'r') elif not TD['new']['verified'] and oldHasBugs and oldAffectsBz: # Remove the email address changes[TD['old']['email']] = (TD['old']['email'], fedorabugsId, TD['old']['person_id'], 'r') + # Check if there's another email address that should take it's + # place + previous = previous_email(TD['new']['person_id']) + if previous and not pervious[1] in changes: + changes[previous[1]] = (previous[1], fedorabugsId, TD['new']['person_id'], 'a') # Now actually add the changes to the queue. + plan = plpy.prepare("insert into debug values ($1)", ('text',)) + plpy.execute(plan, (str(changes),)) for email in changes: plan = plpy.prepare("select email from bugzilla_queue where email = $1", ('text',)) result = plpy.execute(plan, (email,), 1)