Миграция пользователей Atlassian Jira на LDAP
Date December 26th, 2013 Author Vitaly Agapov
Миграция пользователей, хранящихся в локальной директории в Jira на LDAP-сервер (AD либо OpenLDAP) не вызовет никаких трудностей, если логины этих пользователей совпадают в Jira и на LDAP-сервере. В этом случае надо просто настроить новую User Directory в Jira и поменять ей приоритет на самый высокий, чтобы аутентификация сперва проходила через неё. Если же есть пользователи, у которых в Jira один логин, а в том же Active Directory – другой, то тут вылезет ряд проблем. В основном эти проблемы будут связаны с тем, что LDAP-аккаунт Джирой будет восприниматься как совершенно отдельный пользователь, а все задачи, комментарии и история останутся привязанными к старому пользователю. Посмотрим, как решить эту проблему.
Подготовка маппинга логинов пользователей
Для начала нужно получить соответсвие всех старых логинов в Jira всем новых логинам в LDAP. Если пользователей два, пять или даже десять, то это можно сделать и вручную. Если же их гораздо больше, то поможет набросанный мною Perl-скрипт create-user-map.pl, который выбирает из БД Джиры всех пользователей и по их электронной почте производит поиск по LDAP (в моём случае – в AD) соответствующего пользователя. Потом всё это он выплёвывает в виде хэша, который пригодится непосредственно для миграции, хэша, который пригодится для оповещений по почте, списка пользователей, у которых логин не меняется и списка пользователей Jira, для которых в AD не найдено соответствие.
#!/usr/bin/perl use Net::LDAP; use DBI; use Data::Dumper; glob $dbName = "jiraDB"; glob $dbHost = "127.0.0.1"; glob $dbUser = "jiraUser"; glob $dbPass = "jiraPass"; glob $ADUrl = "x.x.x.x"; glob $ADBaseDN = "OU=Users,DC=example,DC=com"; glob $ADDNattr = "sAMAccountName"; glob $ADBindDN = "CN=address book,OU=Users,DC=example,DC=com"; glob $ADBindPWD = "password"; my %map; my %BCmails; glob $dbh = DBI->connect("DBI:mysql:database=$dbName;host=$dbHost",$dbUser,$dbPass,{'RaiseError' => 1}); $dbh->do("SET NAMES utf8"); $dbh->do("SET CHARACTER SET utf8"); $dbh->do("SET character_set_connection=utf8"); my $sql = "SELECT user_name, CONCAT(first_name, ' ', last_name) AS name, email_address FROM cwd_user WHERE directory_id=1 AND active=1"; my $sth = $dbh->prepare( $sql ); $sth->execute; while (my $ref = $sth->fetchrow_hashref()) { my $mail = $ref->{email_address}; $map{$mail}->{jiraLogin} = $ref->{user_name}; $map{$mail}->{jiraName} = $ref->{name}; my $entry=searchADbyMail($mail); if ($entry) { $map{$mail}->{ADDN}=$entry->dn; $map{$mail}->{ADLogin}=$entry->get_value($ADDNattr); $map{$mail}->{ADName}=$entry->get_value('name'); #$entry->dump; } } $sth->finish; $dbh->disconnect(); print "Mapped accounts with different logins\n"; print "glob %map = (\n"; foreach my $mail ( sort keys %map ) { if ( $map{$mail}->{ADLogin} && $map{$mail}->{jiraLogin} ne $map{$mail}->{ADLogin} ) { print " '".$map{$mail}->{jiraLogin}."' => '".$map{$mail}->{ADLogin}."',\n"; $BCmails{$mail} = $map{$mail}->{ADLogin}; } } print ");\n"; print "\nMapped accounts with same logins\n"; foreach my $mail ( sort keys %map ) { if ( $map{$mail}->{jiraLogin} eq $map{$mail}->{ADLogin} ) { print $map{$mail}->{jiraLogin}.' '.$mail."\n"; $BCmails{$mail} = $map{$mail}->{ADLogin}; } } print "\nNot mapped accounts\n"; foreach my $mail ( sort keys %map ) { unless ( $map{$mail}->{ADLogin} ) { print $map{$mail}->{jiraName}.' '.$mail."\n"; } } print "\nMails for broadcast\n"; print "glob %mails = (\n"; foreach my $mail ( sort keys %BCmails ) { print " '".$mail."' => '".$BCmails{$mail}."',\n"; } print ");\n"; exit(0); sub searchADbyMail { my $mail = shift; my $ldap = Net::LDAP->new( $ADUrl ); unless ($ldap) { print "$@. Could not connect to AD"; return; } my $mesg = $ldap->bind($ADBindDN, password => $ADBindPWD, version => 3 ); $mesg->{resultCode} && return undef; $mesg = $ldap->search( base => $ADBaseDN, attrs => [$ADDNattr, "name"], filter => "mail=".$mail, ); $mesg->code && return undef; my $entry = $mesg->shift_entry; $ldap->unbind; return $entry; }
Само собой в скрипте надо вставить свои параметры БД и LDAP.
Рассылка оповещений
Пользователям Jira, которых коснётся миграция, было бы неплохо послать письма с информацией о смене способа аутентификации. И ещё лучше при этом сообщить, какой же новый логин будет использоваться дальше.
Для этого надо хэш %mails из результата работы предыдущего скрипта вида:
glob %mails = ( 'user1@example.com' => 'user1', # ....... 'user2@example.com' => 'user2', );
Поместить в файл config.pl и запустить такой вот Perl-скрипт send-broadcast.pl:
#!/usr/bin/perl use MIME::Lite; require "config.pl"; $text = qq ~ Добрый день, <br/><br/> С утра xxxx.xx.xx изменится принцип авторизации в Jira. Теперь для входа необходимо будет использовать логин и пароль из доменной учётной записи. <br/><br/> Ваш доменный логин - %USERLOGIN% <br/><br/> Все задачи, статьи, комментарии, дашборды и прочие элементы останутся доступны и будут привязаны к новым учётным записям. Старые аккаунты будут удалены. <br/> ---<br/> С уважением,<br/> Подпись ~; foreach my $mail ( sort keys %mails) { print $mail."\n"; my $data = $text; $data =~ s/%USERLOGIN%/$mails{$mail}/; my $msg = MIME::Lite->new( From => 'noreply@example.com', To => $mail, Subject => 'Новая схема авторизации в Jira', Data => $data, ); #$msg->attr('content-type.charset' => 'UTF-8'); $msg->attr("Content-type" => "text/html; charset=UTF-8"); $msg->send('smtp', 'x.x.x.x'); }
Миграция
Допустим, новая user directory в Jira уже настроена и работает. Тогда останавливаем Джиру, делаем бэкап её базы данных и запускаем следующий Perl-скрипт. Только перед началом в config.pl добавляем ещё и хэш %map, полученный от запуска первого скрипта или просто заполненный вручную. В общем выглядеть config.pl должен примерно так:
glob $dbName = "jiraDB"; glob $dbHost = "127.0.0.1"; glob $dbUser = "jiraUser"; glob $dbPass = "jiraPass"; glob %map = ( 'jirauser1' => 'ldapuser1', # ....... 'jirauser2' => 'ldapuser2', );
А вот самый главный скрипт migrate-jira-to-ad.pl, непосредственно производящий миграцию. Он проглядывает все таблицы (кроме тех, которые начинаются на cwd_, то есть содержат учётки и группы) и во всех полях varchar или text меняет старые логины на новые – в Jira привязка элементов к пользователю сделана не по id, а по юзернейму.
#!/usr/bin/perl use DBI; use Data::Dumper; require "config.pl"; glob $dbh = DBI->connect("DBI:mysql:database=$dbName;host=$dbHost",$dbUser,$dbPass,{'RaiseError' => 1}); $dbh->do("SET NAMES utf8"); $dbh->do("SET CHARACTER SET utf8"); $dbh->do("SET character_set_connection=utf8"); my $tables = $dbh->selectall_arrayref("SHOW TABLES"); foreach ( @{$tables} ) { my $table = $_->[0]; next if ($table =~ /^cwd_/); my $sth = $dbh->prepare( "DESCRIBE $table" ); $sth->execute; while (my $ref = $sth->fetchrow_hashref()) { my $col = $ref->{Field}; my $type = $ref->{Type}; next unless ($type =~ /^varchar/ || $type =~ /text/); foreach my $username ( keys %map ) { my $sql = "SELECT COUNT(*) FROM $table WHERE `$col` = '$username'"; my $sth2 = $dbh->prepare($sql); $sth2->execute; my $count = $sth2->fetchrow(); if ($count) { print "Table ".$table."\n"; print " $col - $type :$count\n"; $sql = "UPDATE $table SET `$col` = '$map{$username}' WHERE `$col` = '$username'"; print $sql."\n\n"; eval { $dbh->do($sql); }; warn $@ if $@; } $sth2->finish; } } $sth->finish; } $dbh->disconnect();
Запускаем Jira и производим реиндексацию из админки. Останется только назначить новым пользователям старые группы, а старых пользователей деактивировать или удалить. Если есть пользователи, у которых логины в разных User Directories совпадают, то, как уже говорилось, надо поставить повысить LDAP’у приоритет. Только сначала нужно запомнить, в каких группах состояли старые аккаунты, так как при назначении групп новым аккаунтам будет не с чем сверяться.
Другие продукты Atlassian
Для других продуктов, независимо от того, используют они для аутентификации Джиру или будут ходить напрямую в LDAP-сервер, тоже придётся провернуть кое какие действия.
Для миграции статей и комментариев в Atlassian Confluence можно воспользоваться тем же самым скриптом migrate-jira-to-ad.pl. Нужно будет только в config.pl поменять параметры подключения к базе на базу Confluence. Работает, проверено.
В Atlassian Stash всё чуть сложнее. Там привязка прав на репозитории завязана на id аккаунтов, а не юзернеймы. Поэтому мой скрипт не подойдёт. Можно написать другой скрипт, а можно поменять все права вручную. Также надо у старых аккаунтов удалить SSH-ключи, иначе эти же ключи нельзя будет привязать к новым аккаунтам. Ещё можно в базе передать личные репозитории новым аккаунтам. Но это простая задача, на ней можно не останавливаться.
В Bamboo всё наоборот проще. Там надо обеспечить только нужные права новым аккаунтам. Если они рулятся группами в Jira, то можно сказать, что и делать ничего не надо – всё уже сделано.
Tags: Atlassian, LDAP, Perl
Category:
Perl |
2 Comments »
3 April 2015 - 15:02
А ты пробовал сам запускать его?
3 April 2015 - 15:13
А как я по-твоему у нас миграцию проводил?