123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- # -*- Mode: perl; indent-tabs-mode: nil -*-
- #
- # The contents of this file are subject to the Mozilla Public
- # License Version 1.1 (the "License"); you may not use this file
- # except in compliance with the License. You may obtain a copy of
- # the License at http://www.mozilla.org/MPL/
- #
- # Software distributed under the License is distributed on an "AS
- # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- # implied. See the License for the specific language governing
- # rights and limitations under the License.
- #
- # The Original Code is the Bugzilla Bug Tracking System.
- #
- # Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
- package Bugzilla::Install;
- # Functions in this this package can assume that the database
- # has been set up, params are available, localconfig is
- # available, and any module can be used.
- #
- # If you want to write an installation function that can't
- # make those assumptions, then it should go into one of the
- # packages under the Bugzilla::Install namespace.
- use strict;
- use Bugzilla::Constants;
- use Bugzilla::Error;
- use Bugzilla::Group;
- use Bugzilla::Product;
- use Bugzilla::User;
- use Bugzilla::User::Setting;
- use Bugzilla::Util qw(get_text);
- use Bugzilla::Version;
- sub SETTINGS {
- return {
- # 2005-03-03 travis@sedsystems.ca -- Bug 41972
- display_quips => { options => ["on", "off"], default => "on" },
- # 2005-03-10 travis@sedsystems.ca -- Bug 199048
- comment_sort_order => { options => ["oldest_to_newest", "newest_to_oldest",
- "newest_to_oldest_desc_first"],
- default => "oldest_to_newest" },
- # 2005-05-12 bugzilla@glob.com.au -- Bug 63536
- post_bug_submit_action => { options => ["next_bug", "same_bug", "nothing"],
- default => "next_bug" },
- # 2005-06-29 wurblzap@gmail.com -- Bug 257767
- csv_colsepchar => { options => [',',';'], default => ',' },
- # 2005-10-26 wurblzap@gmail.com -- Bug 291459
- zoom_textareas => { options => ["on", "off"], default => "on" },
- # 2005-10-21 LpSolit@gmail.com -- Bug 313020
- per_bug_queries => { options => ['on', 'off'], default => 'off' },
- # 2006-05-01 olav@bkor.dhs.org -- Bug 7710
- state_addselfcc => { options => ['always', 'never', 'cc_unless_role'],
- default => 'cc_unless_role' },
- # 2006-08-04 wurblzap@gmail.com -- Bug 322693
- skin => { subclass => 'Skin', default => 'Dusk' },
- # 2006-12-10 LpSolit@gmail.com -- Bug 297186
- lang => { subclass => 'Lang',
- default => ${Bugzilla->languages}[0] },
- # 2007-07-02 altlist@gmail.com -- Bug 225731
- quote_replies => { options => ['quoted_reply', 'simple_reply', 'off'],
- default => "quoted_reply" }
- }
- };
- use constant SYSTEM_GROUPS => (
- {
- name => 'admin',
- description => 'Administrators'
- },
- {
- name => 'tweakparams',
- description => 'Can change Parameters'
- },
- {
- name => 'editusers',
- description => 'Can edit or disable users'
- },
- {
- name => 'creategroups',
- description => 'Can create and destroy groups'
- },
- {
- name => 'editclassifications',
- description => 'Can create, destroy, and edit classifications'
- },
- {
- name => 'editcomponents',
- description => 'Can create, destroy, and edit components'
- },
- {
- name => 'editkeywords',
- description => 'Can create, destroy, and edit keywords'
- },
- {
- name => 'editbugs',
- description => 'Can edit all bug fields',
- userregexp => '.*'
- },
- {
- name => 'canconfirm',
- description => 'Can confirm a bug or mark it a duplicate'
- },
- {
- name => 'bz_canusewhines',
- description => 'User can configure whine reports for self'
- },
- {
- name => 'bz_sudoers',
- description => 'Can perform actions as other users'
- },
- # There are also other groups created in update_system_groups.
- );
- use constant DEFAULT_CLASSIFICATION => {
- name => 'Unclassified',
- description => 'Not assigned to any classification'
- };
- use constant DEFAULT_PRODUCT => {
- name => 'TestProduct',
- description => 'This is a test product.'
- . ' This ought to be blown away and replaced with real stuff in a'
- . ' finished installation of bugzilla.'
- };
- use constant DEFAULT_COMPONENT => {
- name => 'TestComponent',
- description => 'This is a test component in the test product database.'
- . ' This ought to be blown away and replaced with real stuff in'
- . ' a finished installation of Bugzilla.'
- };
- sub update_settings {
- my %settings = %{SETTINGS()};
- foreach my $setting (keys %settings) {
- add_setting($setting,
- $settings{$setting}->{options},
- $settings{$setting}->{default},
- $settings{$setting}->{subclass});
- }
- }
- sub update_system_groups {
- my $dbh = Bugzilla->dbh;
- # Create most of the system groups
- foreach my $definition (SYSTEM_GROUPS) {
- my $exists = new Bugzilla::Group({ name => $definition->{name} });
- $definition->{isbuggroup} = 0;
- Bugzilla::Group->create($definition) unless $exists;
- }
- # Certain groups need something done after they are created. We do
- # that here.
- # Make sure people who can whine at others can also whine.
- if (!new Bugzilla::Group({name => 'bz_canusewhineatothers'})) {
- my $whineatothers = Bugzilla::Group->create({
- name => 'bz_canusewhineatothers',
- description => 'Can configure whine reports for other users',
- isbuggroup => 0 });
- my $whine = new Bugzilla::Group({ name => 'bz_canusewhines' });
- $dbh->do('INSERT INTO group_group_map (grantor_id, member_id)
- VALUES (?,?)', undef, $whine->id, $whineatothers->id);
- }
- # Make sure sudoers are automatically protected from being sudoed.
- if (!new Bugzilla::Group({name => 'bz_sudo_protect'})) {
- my $sudo_protect = Bugzilla::Group->create({
- name => 'bz_sudo_protect',
- description => 'Can not be impersonated by other users',
- isbuggroup => 0 });
- my $sudo = new Bugzilla::Group({ name => 'bz_sudoers' });
- $dbh->do('INSERT INTO group_group_map (grantor_id, member_id)
- VALUES (?,?)', undef, $sudo_protect->id, $sudo->id);
- }
- # Re-evaluate all regexps, to keep them up-to-date.
- my $sth = $dbh->prepare(
- "SELECT profiles.userid, profiles.login_name, groups.id,
- groups.userregexp, user_group_map.group_id
- FROM (profiles CROSS JOIN groups)
- LEFT JOIN user_group_map
- ON user_group_map.user_id = profiles.userid
- AND user_group_map.group_id = groups.id
- AND user_group_map.grant_type = ?
- WHERE userregexp != '' OR user_group_map.group_id IS NOT NULL");
- my $sth_add = $dbh->prepare(
- "INSERT INTO user_group_map (user_id, group_id, isbless, grant_type)
- VALUES (?, ?, 0, " . GRANT_REGEXP . ")");
- my $sth_del = $dbh->prepare(
- "DELETE FROM user_group_map
- WHERE user_id = ? AND group_id = ? AND isbless = 0
- AND grant_type = " . GRANT_REGEXP);
- $sth->execute(GRANT_REGEXP);
- while (my ($uid, $login, $gid, $rexp, $present) = $sth->fetchrow_array()) {
- if ($login =~ m/$rexp/i) {
- $sth_add->execute($uid, $gid) unless $present;
- } else {
- $sth_del->execute($uid, $gid) if $present;
- }
- }
- }
- # This function should be called only after creating the admin user.
- sub create_default_product {
- my $dbh = Bugzilla->dbh;
- # Make the default Classification if it doesn't already exist.
- if (!$dbh->selectrow_array('SELECT 1 FROM classifications')) {
- my $class = DEFAULT_CLASSIFICATION;
- print get_text('install_default_classification',
- { name => $class->{name} }) . "\n";
- $dbh->do('INSERT INTO classifications (name, description)
- VALUES (?, ?)',
- undef, $class->{name}, $class->{description});
- }
- # And same for the default product/component.
- if (!$dbh->selectrow_array('SELECT 1 FROM products')) {
- my $default_prod = DEFAULT_PRODUCT;
- print get_text('install_default_product',
- { name => $default_prod->{name} }) . "\n";
- $dbh->do(q{INSERT INTO products (name, description)
- VALUES (?,?)},
- undef, $default_prod->{name}, $default_prod->{description});
- my $product = new Bugzilla::Product({name => $default_prod->{name}});
- # The default version.
- Bugzilla::Version::create(Bugzilla::Version::DEFAULT_VERSION, $product);
- # And we automatically insert the default milestone.
- $dbh->do(q{INSERT INTO milestones (product_id, value, sortkey)
- SELECT id, defaultmilestone, 0
- FROM products});
- # Get the user who will be the owner of the Product.
- # We pick the admin with the lowest id, or we insert
- # an invalid "0" into the database, just so that we can
- # create the component.
- my $admin_group = new Bugzilla::Group({name => 'admin'});
- my ($admin_id) = $dbh->selectrow_array(
- 'SELECT user_id FROM user_group_map WHERE group_id = ?
- ORDER BY user_id ' . $dbh->sql_limit(1),
- undef, $admin_group->id) || 0;
-
- my $default_comp = DEFAULT_COMPONENT;
- $dbh->do("INSERT INTO components (name, product_id, description,
- initialowner)
- VALUES (?, ?, ?, ?)", undef, $default_comp->{name},
- $product->id, $default_comp->{description}, $admin_id);
- }
- }
- sub create_admin {
- my ($params) = @_;
- my $dbh = Bugzilla->dbh;
- my $template = Bugzilla->template;
- my $admin_group = new Bugzilla::Group({ name => 'admin' });
- my $admin_inheritors =
- Bugzilla::User->flatten_group_membership($admin_group->id);
- my $admin_group_ids = join(',', @$admin_inheritors);
- my ($admin_count) = $dbh->selectrow_array(
- "SELECT COUNT(*) FROM user_group_map
- WHERE group_id IN ($admin_group_ids)");
- return if $admin_count;
- my %answer = %{Bugzilla->installation_answers};
- my $login = $answer{'ADMIN_EMAIL'};
- my $password = $answer{'ADMIN_PASSWORD'};
- my $full_name = $answer{'ADMIN_REALNAME'};
- if (!$login || !$password || !$full_name) {
- print "\n" . get_text('install_admin_setup') . "\n\n";
- }
- while (!$login) {
- print get_text('install_admin_get_email') . ' ';
- $login = <STDIN>;
- chomp $login;
- eval { Bugzilla::User->check_login_name_for_creation($login); };
- if ($@) {
- print $@ . "\n";
- undef $login;
- }
- }
- while (!defined $full_name) {
- print get_text('install_admin_get_name') . ' ';
- $full_name = <STDIN>;
- chomp($full_name);
- }
- if (!$password) {
- $password = _prompt_for_password(
- get_text('install_admin_get_password'));
- }
- my $admin = Bugzilla::User->create({ login_name => $login,
- realname => $full_name,
- cryptpassword => $password });
- make_admin($admin);
- }
- sub make_admin {
- my ($user) = @_;
- my $dbh = Bugzilla->dbh;
- $user = ref($user) ? $user
- : new Bugzilla::User(login_to_id($user, THROW_ERROR));
- my $admin_group = new Bugzilla::Group({ name => 'admin' });
- # Admins get explicit membership and bless capability for the admin group
- $dbh->selectrow_array("SELECT id FROM groups WHERE name = 'admin'");
- my $group_insert = $dbh->prepare(
- 'INSERT INTO user_group_map (user_id, group_id, isbless, grant_type)
- VALUES (?, ?, ?, ?)');
- # These are run in an eval so that we can ignore the error of somebody
- # already being granted these things.
- eval {
- $group_insert->execute($user->id, $admin_group->id, 0, GRANT_DIRECT);
- };
- eval {
- $group_insert->execute($user->id, $admin_group->id, 1, GRANT_DIRECT);
- };
- # Admins should also have editusers directly, even though they'll usually
- # inherit it. People could have changed their inheritance structure.
- my $editusers = new Bugzilla::Group({ name => 'editusers' });
- eval {
- $group_insert->execute($user->id, $editusers->id, 0, GRANT_DIRECT);
- };
- print "\n", get_text('install_admin_created', { user => $user }), "\n";
- }
- sub _prompt_for_password {
- my $prompt = shift;
- my $password;
- while (!$password) {
- # trap a few interrupts so we can fix the echo if we get aborted.
- local $SIG{HUP} = \&_password_prompt_exit;
- local $SIG{INT} = \&_password_prompt_exit;
- local $SIG{QUIT} = \&_password_prompt_exit;
- local $SIG{TERM} = \&_password_prompt_exit;
- system("stty","-echo") unless ON_WINDOWS; # disable input echoing
- print $prompt, ' ';
- $password = <STDIN>;
- chomp $password;
- print "\n", get_text('install_confirm_password'), ' ';
- my $pass2 = <STDIN>;
- chomp $pass2;
- eval { validate_password($password, $pass2); };
- if ($@) {
- print "\n$@\n";
- undef $password;
- }
- system("stty","echo") unless ON_WINDOWS;
- }
- return $password;
- }
- # This is just in case we get interrupted while getting a password.
- sub _password_prompt_exit {
- # re-enable input echoing
- system("stty","echo") unless ON_WINDOWS;
- exit 1;
- }
- sub reset_password {
- my $login = shift;
- my $user = Bugzilla::User->check($login);
- my $prompt = "\n" . get_text('install_reset_password', { user => $user });
- my $password = _prompt_for_password($prompt);
- $user->set_password($password);
- $user->update();
- print "\n", get_text('install_reset_password_done'), "\n";
- }
- 1;
- __END__
- =head1 NAME
- Bugzilla::Install - Functions and variables having to do with
- installation.
- =head1 SYNOPSIS
- use Bugzilla::Install;
- Bugzilla::Install::update_settings();
- =head1 DESCRIPTION
- This module is used primarily by L<checksetup.pl> during installation.
- This module contains functions that deal with general installation
- issues after the database is completely set up and configured.
- =head1 CONSTANTS
- =over
- =item C<SETTINGS>
- Contains information about Settings, used by L</update_settings()>.
- =back
- =head1 SUBROUTINES
- =over
- =item C<update_settings()>
- Description: Adds and updates Settings for users.
- Params: none
- Returns: nothing.
- =item C<create_default_product()>
- Description: Creates the default product and classification if
- they don't exist.
- Params: none
- Returns: nothing
- =back
|