タグ「JIRA」が付けられているもの

JIRA 6.2で、同じプロジェクトで2種類のバグ票を運用したいと思った。
通常、JIRAのバグ票は、そのプロジェクトの関係者(のRole)をAdministrators, Developers, Usersと分ける時、Usersが起票し、Developersが処理する。それに加えて、今回は、Developers+α("group1"に所属するメンバーとする)だけが起票可能、閲覧可能な、開発者側の内部バグ票("IT Bug"とする)を、通常のバグ票とは別の課題タイプで運用したかった。
※結合テスト=Integration Testで発見された不具合、またはInternal Bugの略

特定の課題タイプを、特定のグループのメンバーだけが閲覧可能にするのも、直接的な設定方法が無く、容易ではないが、例えば、
  • Issue Security Schemeのセキュリティレベル定義を、default=Private(group1のみ閲覧可能)にする
  • Set Issue Security権限を課題作成可能者全員に与える
  • 画面にSecurity Levelフィールドを設ける
とすると、group1が開いた課題作成画面ではセキュリティレベルがPrivateになり、それ以外のメンバーが開いた課題作成画面ではセキュリティレベルがNoneになる(Noneは制限無し、設定権限があるのにNoneしか選べないからそうなる)ので、IT Bug以外は手操作でセキュリティレベルをNoneにすれば、何とか近いことが実現できる。
かなり強引で多少面倒だが、通常のバグ票を開発者が起票することがあまり無く、あっても非公開にしたいことがあり得るとすれば、現実的には妥当な解と言えなくはない。
(ちなみに、画面にSecurity Levelフィールドを設けなければ、全ての課題のセキュリティレベルがPrivateになる。これがNoneになるなら、IT Bugの作成画面だけにSecurity Levelフィールドを設ければ、IT Bug以外は自動的に全員閲覧可能にできるのだが、そうはならない。また、IT Bugのワークフローだけ、Create IssueトランジションのPost FunctionsにてSecurity Level=Noneにできれば良いが、JIRA 6.2にはその関数が無く、有料のプラグインをインストールしないとできない。)

しかし、特定の課題タイプは、特定のグループのメンバーだけが課題を作成可能にすることは、次の理由で、さらに難しい。

  • Create Issue権限を課題タイプ毎に別にできれば良いだけなのだが、Permission Schemeは、課題タイプ毎には設定できない。
  • 課題タイプ毎にworkflowは別にできるので、Create IssueトランジションのConditionsで制限すれば良いだけなのだが、なぜかCreate IssueトランジションにはConditionsが無い。
  • Create IssueトランジションにValidatorはあるが、グループではなく、何らかのPermissionしか指定できない(そのPermissionが無いというエラーになるので、Create Issue権限以外を指定すると意味不明なエラーになってしまう)し、作成ボタンを押した後にしかエラーにならない。
  • Workflow propertiesで制限できれば良いが、"(you can use in) Step"と書いてあり、実際に試してみたが、jira.permission.*はtransitionに対しては無効だった。Create Issueをする前には状態(Step)が無いので、jira.permission.*を設定する術が無い。
  • 同じプロジェクトで、課題タイプ毎に変えられる設定というのは、ワークフロー、画面、フィールド設定と、カスタムフィールドの初期値くらいしか見当たらない。

JIRA関連のドキュメントでもGoogleでも、"Permission per Issue Type"で検索して辿って行くと、大体、次の課題票に行き着く。
[JRA-5865] Allow permission schemes to be configured per issue type - Atlassian JIRA
約10年前に出され、多くの人に必要性を語られ、継続的に議論されてきた要望だが、JIRAを開発するAtlassian社によって、"Won't Fix"として昨年7月にcloseされている。
1つのプロジェクトで、課題タイプ毎に権限を変えるようなことはするな、という意味にしか取れない。
しかし、同じプロジェクトで、どの帳票も起票するメンバー、処理するメンバーが同じ、という制限では不便である。
例えば、発注者がバグ報告をする場合、開発者でない発注者が担当者になることは無いので、バグ票のAssigneeの権限は開発者に限定したいが、開発者が発注者に質問する場合、発注者が担当者になるので、Q&A票のAssigneeの権限は発注者に限定したい。
しかし、それだけのことすら、JIRAでは可能にす対応する予定が無いという。
プロジェクトを別にすれば良い、ということかも知れないが、課題タイプ毎にプロジェクトを別にするのでは課題タイプの意味が無いし、JIRAでプロジェクトを別に立ち上げるのは、バージョンやロールなど、共有できないので二重管理になるものもあるし、カスタムフィールドなど、プロジェクトに依存する設定もあり、結構面倒である。

帳票毎に権限を制約できないが為に、例えばAssignee権限を広くすると、その帳票の担当者になり得ないメンバーに間違ってassignしてしまう可能性があるし、一部の帳票は閲覧を制限するように課題にセキュリティレベルを設けていると、間違ってassignされた人がその課題を参照できず、課題が行方不明になってしまう。
今回やりたいことは、課題タイプが"IT Bug"の課題は、開発受注者のグループしか作成できないようにすることである。これも、帳票毎に権限を制約できないが為に、発注者も"IT Bug"を作成可能にすると、発注者にとっては、起票時に知らない課題タイプが選択肢に出て来るのがいまいちだし、間違って"IT Bug"で起票されてしまうと、いちいち開発者側で課題タイプを修正するのが面倒である。
それだけの為に、"IT Bug"を別プロジェクトにするのも、管理が面倒だし、見苦しい。

どうしても諦められなくて、上記の課題票JRA-5865のコメント欄を読むと、初期の頃から、JavaScriptでCreateボタンを消すことによる暫定対策が検討されており、貼られているJavaScriptを試してみると、JIRA 6.2でも部分的には動いたので、JavaScriptで何とかできないかを追究してみることにした。
なお、筆者にはJavaScriptの知識はほとんど無い。documentクラスでHTMLを操作する方法も、windowクラスでWebブラウザを制御する方法も知らない。AJS(Atlassian JavaScriptライブラリ) 頼みであり、AJSのドキュメントが無いのでGoogle頼みである。

■JIRAでのJavaScript使用の基本

フィールドのDescriptionにJavaScriptを書くと、そのDescriptionが表示される画面で実行される。
例えば、次のテキストをSummaryフィールド等のDescriptionに貼り付けると、課題作成画面を開く時に"JavaScript ran!"という警告ダイアログが出るようになる。

This description has some JavaScript.
<script language="JavaScript">
<!--
alert("JavaScript ran!");
//-->
</script>
Announcement Bannerに含めても実行されるが、scriptタグ以外に何も無くても、細いAnnoucement Bannerが表示されるようになってしまう。
参考:Fields Allowing Custom HTML or JavaScript - Atlassian Documentation
なお、 JIRA 6.2.x以降、これらのJavaScriptを埋め込む方法の一部は使えなくなる、と書いてあるが、JIRA 6.2.7では上の方法でJavaScriptが実行されることを確認した。

AJSを使用する場合は、実行されるタイミングが問題になるので、次の例の(function($) { ... })(AJS.$);の部分のように書くのが定跡のようである。

<script language="JavaScript">
(function($) {
  AJS.toInit(function(){
    //(A)
    alert("init on load: current user = " + AJS.params.loggedInUser);
  })

  JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function (e, context) {
    //(B)
    alert("init on refresh: current user = " + AJS.params.loggedInUser);
  });
})(AJS.$);

//(C)
alert("outside function: current user = " + AJS.params.loggedInUser);
</script>
これをSummaryフィールドのDescriptionに埋め込むと、次のことがわかる。
  • 通常の(ポップアップでない)課題作成画面が開く時に(A)が実行される。
  • 通常の課題作成画面が開く時は(C)の位置ではAJSが働かない。
  • ポッブアッブ形式の課題作成画面が開く時に(A)が実行される。
    その前に(B)が呼ばれることもある。(元のページによっては、例えばClosedでない課題画面は、開いた途端に(A)が呼ばれ、(B)のコールバックも登録されるので、Create Issueボタンを押してポップアップ課題画面が開いた途端に(B)が呼ばれる。)
  • ポッブアッブ形式の課題作成画面でプロジェクトや課題タイプを変えると(B)が呼ばれる。
なお、ポップアップ課題作成画面を開く度にコールバックが追加されるので、元の画面をそのままに、何度もポップアップ課題作成画面を開くと、開く度に(B)が何度も呼ばれるようになってしまうが、これは仕方が無さそうだ。

ポップアップ課題作成画面におけるJavaScript実行について

ポップアップ形式の課題作成画面は、開いた状態でプロジェクトや課題タイプが切り替えることが可能で、それらの切り替えに連動してフィールドが出現したりDescriptionが変化することがあるが、切り替えによって表示される新たなDescriptionのJavaScriptは実行されない。従って、ポップアップ課題作成画面で実行されるべきJavaScriptは、ポップアップ課題作成画面が開く時にどのプロジェクトのどの課題タイプの画面から始まっても、必ず実行されるようにする必要がある。
例えば、全てのフィールド設定において、いずれかのDescriptionにJavaScriptを含める方法や、Announcement Bannerに含める方法が考えられる。
今回は、あらゆるプロジェクトのあらゆる課題タイプでSummaryフィールドが表示されるものとして、全てのフィールド設定のSummaryフィールドのDescriptionにJavaScriptを埋め込んでテストした。

■作戦1

該当プロジェクトの、作成が制限された課題タイプなら、Createボタンをdisabledにする。

<script language="JavaScript">
<!--
function isUserInGroup(group){
  var groups;
    AJS.$.ajax({
    url: AJS.params.baseURL + "/rest/api/2/myself?expand=groups",
    type: 'GET',
    dataType: 'json',
    async: false,
    success: function(data) { groups = data.groups.items; }
  });
  for (i = 0; i < groups.length; i++){
    if (groups[i].name == group){ 
      return true;
    }
  }
  return false;
}

function disableCreate(value) {
  var inps = document.getElementsByTagName('INPUT');
  for (i = 0; i < inps.length; i++) {
    if (inps[i].type == 'submit') {
      inps[i].disabled = value;
    }
  }
}

function initCreateIssueScreen() {
  if (isUserInGroup('group1') == false) {
    var project = AJS.$("#issue-create-project-name").text();
    var issueType = AJS.$("#issue-create-issue-type").text();
    if (project == 'Test Project 1' && issueType == 'IT Bug') {
      disableCreate(true);
    }
  }
}

function initPopUpCreateIssueScreen() {
  if (isUserInGroup('group1') == false){
    var project = AJS.$("#project-field").val();
    var issueType = AJS.$("#issuetype-field").val();
    if (project == 'Test Project 1' && issueType == 'IT Bug') {
      disableCreate(true);
    } else {
      disableCreate(false);
    }
  }
}

(function($) {
  AJS.toInit(function(){
    if (location.pathname.indexOf("CreateIssue") != -1) {
      initCreateIssueScreen();
    }
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  })

  JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function (e, context) {
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  });
})(AJS.$);
//-->
</script>

スクリプトの説明

isUserInGroup()は、Webのどこかから拾ったものである。
disableCreate()は、JRA-5865のコメント欄にあるものを、display='none'(非表示)でなくdisabled=true(グレーアウトして無効化)にするよう変えたものである。Createボタン(type="submit"のINPUTタグ)を非表示にしても、いくつかのブラウザではSubmitのショートカットキー(ChromeやIEではAlt+S、FirefoxではAlt+Shift+S、SafariではCtrl+Shift+Sなど)は効いてしまうため、disabledにした。(ChromeとSafariは効く、FirefoxとIE8は効かない)
initCreateIssueScreen()は、通常の(ポップアップでない)課題作成画面のCreateボタンの処理である。ユーザーが'group1'に属さず、プロジェクトが'Test Project 1'で、課題タイプが'IT Bug'なら、Createボタンをdisabledにしている。
initPopUpCreateIssueScreen()は、ポップアップ形式の課題作成画面のCreateボタンの処理である。ユーザーが'group1'に属さず、プロジェクトが'Test Project 1'で、課題タイプが'IT Bug'なら、Createボタンをdisabledに、そうでなければ、enabledにしている。
AJS.toInit()の部分は、このスクリプトの中で最初に実行される処理である。課題作成画面が開く時にも実行される。通常の課題作成画面ならinitCreateIssueScreen()を、ポップアップ課題作成画面ならinitPopUpCreateIssueScreen()を呼び出している。
JIRA.bind()の部分は、同じページで画面が変化すると実行される処理である。現在のページがポップアップ課題作成画面なら、initPopUpCreateIssueScreen()を呼び出している。
なお、プロジェクトIDや課題タイプIDを取り出す方法がわからなかったので、プロジェクトや課題タイプは、IDではなく実際に表示される文字列を比較に使っている。これらの文字列は変更される可能性があるので、このやり方はあまり好ましくない。この方法だと、課題タイプは多言語訳の登録が可能なので、登録した全言語の文字列と比較するようなことも必要になるが、ここでは省略している。

結果

通常の課題作成画面では、作成が制限される条件であれば、うまくCreateボタンが無効化される。
ポップアップ形式の課題作成画面でも、開いた時はCreateボタンが無効化されるが、開いたままプロジェクトや課題タイプを切り替えると、disableCreate(true)が呼び出されても、Createボタンが有効になってしまう。(Safari, Firefox, Chrome, IE全て同様)
元々、ポップアップの課題作成画面では、プロジェクトや課題タイプを切り替えると、Createボタンが一時的に無効になって有効に戻るので、この有効にする処理が後から走ってしまうのだと推測される。
色々試したが(後述)、最終的にCreateボタンをdisabledにする適当な方法は見つからなかった。
但し、Createボタンを非表示にすると、その状態は維持されることがわかった。

■作戦2

通常の課題作成画面では、該当プロジェクトの、作成が制限された課題タイプなら、Createボタンをdisabledにする。
該当プロジェクトの、作成が制限された課題タイプでは、Createボタンをdisabledかつ非表示にする。

<script language="JavaScript">
<!--
function isUserInGroup(group){
  var groups;
    AJS.$.ajax({
    url: AJS.params.baseURL + "/rest/api/2/myself?expand=groups",
    type: 'GET',
    dataType: 'json',
    async: false,
    success: function(data) { groups = data.groups.items; }
  });
  for (i = 0; i < groups.length; i++){
    if (groups[i].name == group){ 
      return true;
    }
  }
  return false;
}

function disableCreate(value) {
  var inps = document.getElementsByTagName('INPUT');
  for (i = 0; i < inps.length; i++) {
    if (inps[i].type == 'submit') {
      inps[i].disabled = value;
    }
  }
}

function hideCreate(value) {
  var inps = document.getElementsByTagName('INPUT');
  for (i = 0; i < inps.length; i++) {
    if (inps[i].type == 'submit') {
      if (value == true) {
        inps[i].style.display = 'none';
      } else {
        inps[i].style.display = '';
      }
    }
  }
}

function initCreateIssueScreen() {
  if (isUserInGroup('group1') == false) {
    var project = AJS.$("#issue-create-project-name").text();
    var issueType = AJS.$("#issue-create-issue-type").text();
    if (project == 'Test Project 1' && issueType == 'IT Bug') {
      disableCreate(true);
    }
  }
}

function initPopUpCreateIssueScreen() {
  if (AJS.$("#create-issue-dialog").length) {
    if (isUserInGroup('group1') == false){
      var project = AJS.$("#project-field").val();
      var issueType = AJS.$("#issuetype-field").val();
      if (project == 'Test Project 1' && issueType == 'IT Bug') {
        disableCreate(true);
        hideCreate(true);
      } else {
        disableCreate(false);
        hideCreate(false);
      }
    }
  }
}

(function($) {
  AJS.toInit(function(){
    if (location.pathname.indexOf("CreateIssue") != -1) {
      initCreateIssueScreen();
    }
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  })

  JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function (e, context) {
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  });
})(AJS.$);
//-->
</script>

スクリプトの説明

作戦1のスクリプトのinitPopUpCreateIssueScreen()に、hideCreate()の呼び出しを加えた。。
hideCreate()は、Createボタンをdisplay='none'にする処理である。

結果

作戦1同様、通常の課題作成画面では、作成が制限される条件であれば、Createボタンが無効化される。
ポップアップ形式の課題作成画面では、意図通りに、作成が制限される条件ならCreateボタンが消え、そうでなければCreateボタンが出現する。画面を開いたままプロジェクトや課題タイプを切り替えると、条件に従ってCreateボタンが出たり消えたりする。
但し、開いた直後にCreateボタンが消えていればSubmitのショートカットキーも効かないが、開いた後にプロジェクトや課題タイプを切り替えると、Createボタンが消えても、作戦1と同様、ショートカットキーは効いてしまう。

■作戦3

通常の課題作成画面では、作成が制限された課題タイプなら、Createボタンをdisabledにする。
ポップアップ課題作成画面では、課題タイプの選択肢から、制限された課題タイプを削除する。
※プロジェクトを問わず、その課題タイプの使用を制限する。その理由は後述。

<script language="JavaScript">
<!--
function isUserInGroup(group){
  var groups;
  AJS.$.ajax({
    url: AJS.params.baseURL + "/rest/api/2/myself?expand=groups",
    type: 'GET',
    dataType: 'json',
    async: false,
    success: function(data) { groups = data.groups.items; }
  });
  for (i = 0; i < groups.length; i++){
    if (groups[i].name == group){ 
      return true;
    }
  }
  return false;
}

function disableCreate() {
  var inps = document.getElementsByTagName('INPUT');
  for (i = 0; i < inps.length; i++) {
    if (inps[i].type == 'submit') {
      inps[i].disabled = true;
    }
  }
}

function hideITBug(){
  var ops = AJS.$("#issuetype option");
  for (i = 0; i < ops.length; i++) {
    if(ops[i].text == 'IT Bug'){
      AJS.$("#" + ops[i].id).remove();
      return;
    }
  }
}

function initCreateIssueScreen() {
  if (isUserInGroup('group1') == false) {
    var issueType = AJS.$("#issue-create-issue-type").text();
    if (issueType == 'IT Bug') {
      disableCreate();
    }
  }
}

function initPopUpCreateIssueScreen() {
  if (AJS.$("#create-issue-dialog").length) {
    if (isUserInGroup('group1') == false){
      hideITBug();
    }
  }
}

(function($) {
  AJS.toInit(function(){
    if (location.pathname.indexOf("CreateIssue") != -1) {
      initCreateIssueScreen();
    }
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  })

  JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function (e, context) {
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  });
})(AJS.$);
//-->
</script>

スクリプトの説明

作戦1のスクリプトのinitPopUpCreateIssueScreen()は、hideITBug()を呼び出すように変えた。
hideITBug()は、課題タイプのプルダウンメニューから'IT Bug'のオプションを削除する処理である。
また、全体的にプロジェクト名の比較は無くした。
その結果、disableCreate()の引数は不要になったので、無くした。

補足説明

このようにして課題タイプの選択肢から'IT Bug'を削除しても、ポップアップ画面が開く時の初期値が'IT Bug'だったり、ポップアップ画面で別プロジェクトにして'IT Bug'を選択してからプロジェクトを切り替えると、課題タイプが'IT Bug'になってしまう。その為、課題タイプの選択肢から削除する方針なら、全プロジェクトにおいてその課題タイプを制限する必要がある。
通常の課題作成画面に入る前の、プロジェクトと課題タイプを選択する画面では、フィールドのDescriptionが表示されないので、JavaScriptによる課題タイプの制限が難しい。その為、通常の課題作成画面では、Createボタンをdisabledにしている。

結果

作戦1同様、通常の課題作成画面では、作成が制限される条件であれば、Createボタンが無効化される。
ポップアップ形式の課題作成画面では、意図通りに、課題タイプの選択肢から'IT Bug'が無くなる。
しかし、デフォルトの課題タイプを'IT Bug'にしていると、ポップアップ画面で'Test Project 1'に存在しない課題タイプを選択した状態から'Test Project 1'に切り替えると、課題タイプが'IT Bug'になってしまう。従って、例えば結合テストのフェーズではデフォルトの課題タイプを'IT Bug'にしたくても、それができないことになる。デフォルトの課題タイプを'IT Bug'にし得るなら、作戦2のように、その時にCreateボタンを非表示かつdisabledにする必要がありそうだ(それでも、作戦2と同じく、ショートカットキーによるSubmitまでは止められない)。
また、この方針だと、通常の課題作成画面の手前の課題タイプ選択画面では相変わらず'IT Bug'が選択できてしまうのが、統一感が無くて不満である。

結論

いずれの作戦もデメリットがあるし、全プロジェクトに影響してしまうので、特定の課題タイプの課題の作成をJavaScriptで制限するのは諦める。

面倒でもプロジェクトを分けるか、どうしてもプロジェクトを分けたくなければ、エラーメッセージがわかりにくくなるが、Create IssueトランジションのValidatorsで作成者を制限するのが最善だと思われる。
なお、Create IssueトランジションのValidatorsは、ワークフロー編集画面のDiagram版から開くことができる。Validatorとしてはいずれかの権限しか選択できないので、いずれの課題タイプについても内部バグ票の作成者に制限しても良さそうな権限、例えばSchedule Issues権限をを選んでそれを内部バグ票の作成可能者に制限し、内部バグ票のCreate IssueトランジションのValidatorにも使用する。

JIRAとOpenLDAPを連携させてみる

  • 投稿日:
  • by
  • カテゴリ:

JIRAという課題管理システムがシェアを伸ばしてるらしく、興味を持ったので、評価用ライセンスでインストールしてみた。
JIRAのユーザー管理にはLDAPが使えるとのことなので、ついでにOpenLDAPとの接続を試してみたので、その過程を記録する。

使用環境
OS: FreeBSD 9.2
packages: OpenJDK6, Tomcat6, openldap-server
JIRA version: 5.2.11

●slapd.confの設定
・inetOrgPerson, posixAccountクラス用のincludeを追加
include /usr/local/etc/openldap/schema/cosine.schema
include /usr/local/etc/openldap/schema/inetorgperson.schema
include /usr/local/etc/openldap/schema/nis.schema

・ベースDN設定

suffix "dc=raffine,dc=moriguchi,dc=jp"
ldapsearch等のコマンドの利便のため、ldap.confのBASEも同じにしておくと良い。

・ルートDN(特権DN)設定

rootdn "cn=Manager,dc=raffine,dc=moriguchi,dc=jp"
rootpw {SSHA}s/98X4C3ex/vtSrL3wzP0CyRub2ZdYoL
暗号化パスワード文字列は、slappasswdで作成できる。

●基本コンテナの作成
以下の内容を、init.ldifとして保存する。

dn: dc=raffine,dc=moriguchi,dc=jp
objectClass: dcObject
objectClass: organization
o: Raffine Moriguchi
dc: raffine

dn: cn=Manager,dc=raffine,dc=moriguchi,dc=jp
objectClass: organizationalRole
cn: Manager

dn: ou=People,dc=raffine,dc=moriguchi,dc=jp
objectClass: organizationalUnit
ou: People

dn: ou=Group,dc=raffine,dc=moriguchi,dc=jp
objectClass: organizationalUnit
ou: Group

次に、ldapaddコマンドで実行する。

ldapadd -x -D "cn=Manager,dc=raffine,dc=moriguchi,dc=jp" -W -f init.ldif
パスワードが覗かれたりシェルのヒストリーに残っても良ければ、"-W"を"-w (password)"としても良い。

●ユーザー情報の登録
以下をuser01.ldifとして保存し、ldapaddで実行

dn: cn=user01,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: inetOrgPerson
cn: user01
sn: User No.01
mail: user01@localhost
userPassword: user01

dn: cn=user02,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: inetOrgPerson
cn: user02
sn: User No.02
mail: user02@localhost
userPassword: user02

dn: cn=jira-users,ou=group,dc=raffine,dc=moriguchi,dc=jp
objectClass: groupOfUniqueNames
cn: jira-users
uniqueMember: cn=user01,ou=people,dc=raffine,dc=moriguchi,dc=jp
uniqueMember: cn=user02,ou=people,dc=raffine,dc=moriguchi,dc=jp

●JIRAへのディレクトリー追加
1. システム管理メニューの"Users"→"User Directories"の画面で"Add Directory"ボタンを押し、Directory TypeとしてLDAPを選択
2. Configure画面にて以下のように設定し、"Save and Test"ボタン押下
Name: LDAP server 1
Directory Type: OpenLDAPを選択
Hostname: localhost
Username: cn=Manager,dc=raffine,dc=moriguchi,dc=jp
Password: (ルートDNのパスワード)
Base DN: dc=raffine,dc=moriguchi,dc=jp
3. "Test basic connection: Succeeded"と表示されているのを確認し、再バインド用に、"For extended testing ..."の所で 
User name: user01
として"Test Settings"ボタン押下
4. システム管理メニューの"Users"→"User Directories"の画面でuser01とuser02が追加されていること、Groupがjira-usersであることを確認

●ユーザー情報の追加
以下をuser03.ldifとして保存し、ldapaddで実行

dn: cn=user03,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: inetOrgPerson
cn: user03
sn: User No.03
mail: user03@localhost
userPassword: user03

dn: cn=user04,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: inetOrgPerson
cn: user04
sn: User No.04
mail: user04@localhost
userPassword: user04
以下をuser03m.ldifとして保存し、ldapmodifyで実行
dn: cn=jira-users,ou=group,dc=raffine,dc=moriguchi,dc=jp
add: uniqueMember
uniqueMember: cn=user03,ou=people,dc=raffine,dc=moriguchi,dc=jp
uniqueMember: cn=user04,ou=people,dc=raffine,dc=moriguchi,dc=jp
ldapmodify -x -D "cn=Manager,dc=raffine,dc=moriguchi,dc=jp" -W -f user03m.ldif

●JIRAでの確認
"User Directory"画面で"LDAP server 1"の"Synchronize"をクリックし、
"User"画面でuser03やuser04が追加されていることを確認


●ユーザー情報の登録(Posix Schemaのテスト用)
以下をuser11.ldifとして保存し、ldapaddで実行

dn: uid=user11,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: posixAccount
objectClass: inetOrgPerson
cn: user11
uid: user11
uidNumber: 10011
gidNumber: 10918
homeDirectory: /home/user11
userPassword: user11
mail: user11@localhost
sn: User No.11

dn: uid=user12,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: posixAccount
objectClass: inetOrgPerson
cn: user12
uid: user12
uidNumber: 10012
gidNumber: 10918
homeDirectory: /home/user12
userPassword: user12
mail: user12@localhost
sn: User No.12

#dn: cn=jira-users,ou=group,dc=raffine,dc=moriguchi,dc=jp
#is already used by the above groupOfUniqueName object.
dn: gidNumber=10918,ou=group,dc=raffine,dc=moriguchi,dc=jp
objectClass: posixGroup
description: the default group in JIRA
gidNumber: 10918
cn: jira-users
memberUid: user11
memberUid: user12

posixAccountは補助型なので、他に何か構造型が必要であるが、その構造型としてinetOrgPersonを選んだのは、mail属性を使用するため。
posixGroupオブジェクトのRDNは"cn=jira-users"とするのが普通だと思うが、user01.ldifの例で既に使ってしまっており、それと共存させたので、gidNumberをRDNに用いた。
ou=PosixGroupというサブグループを作って、その下に置くのがbetterだと思うが、手を抜いた。
なお、10918としたのは、A=1, B=2とするとJ=10, I=9, R=18だから。

●JIRAへのディレクトリー追加(Posix Schema)
1. システム管理メニューの"Users"→"User Directories"の画面で、
"LDAP server 1"の"Disable"をクリック(※理由は後述)し、
"Add Directory"ボタンを押し、Directory TypeとしてLDAPを選択
2. Configure画面にて以下のように設定し、"Save and Test"ボタン押下
Name: LDAP server 2
Directory Type: OpenLDAP (Read-Only Posix Schema)
Hostname: localhost
Username: cn=Manager,dc=raffine,dc=moriguchi,dc=jp
Password: (ルートDNのパスワード)
Base DN: dc=raffine,dc=moriguchi,dc=jp
3. "Test basic connection: Succeeded"と表示されているのを確認し、再バインド用に、"For extended testing ..."の所で 
User name: user11
として"Test Settings"ボタン押下
4. システム管理メニューの"Users"→"User Directories"の画面でuser11とuser12が追加されていること、Groupがjira-usersであることを確認

※Configure画面のGroup Schema Settingsセクションを開くと
Group Object Class: groupOfUniqueNames
となっているが、これを特に"posixGroup"に修正しなくても、posixGroupクラスのオブジェクトからグループ情報が取得されるようである。

●"LDAP server 1"と"LDAP server 2"の共存のための設定
ここまでの状態で"LDAP server 1"を有効にすると、use11やuser12がinetOrgPersonクラスである為、"LDAP server 1"から取得されてしまい、"LDAP server 2"のuser11やuser12が無効になり、posixGroupオブジェクトによるグループ設定も無効になり、user11やuser12がグループ無しになってしまう。
これをJIRA側で解決する方法は、2つほど考えられる。
(a) "LDAP server 2"を"LDAP server 1"より優先する
 "User Directories"の画面でディレクトリーの参照順序を入れ替えるだけである。
(b) 該当するユーザーが"LDAP server 1"から取得されないようにする
 "LDAP server 1"の"Edit"からConfigure画面に入ると、User Schema SettingsのセクションのUser Object Filterが"(objectClass=inetOrgPerson)"になっているのがユーザーの検索条件で、これを、例えば"(&(objectClass=inetOrgPerson)(!(objectClass=posixAccount)))"とすれば良い。

●ユーザー情報の追加、JIRAでの確認(Posix Schemaのテスト用)
以下をuser13.ldifとして保存し、ldapaddで実行

dn: uid=user13,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: posixAccount
objectClass: inetOrgPerson
cn: user13
uid: user13
uidNumber: 10013
gidNumber: 10918
homeDirectory: /home/user13
userPassword: user13
mail: user13@localhost
sn: User No.13

dn: uid=user14,ou=people,dc=raffine,dc=moriguchi,dc=jp
objectClass: posixAccount
objectClass: inetOrgPerson
cn: user14
uid: user14
uidNumber: 10014
gidNumber: 10918
homeDirectory: /home/user14
userPassword: user14
mail: user14@localhost
sn: User No.14
以下をuser13m.ldifとして保存し、ldapmodifyで実行
dn: gidNumber=10918,ou=group,dc=raffine,dc=moriguchi,dc=jp
add: memberUid
memberUid: user13
memberUid: user14

→"User Directory"画面で"LDAP server 2"の"Synchronize"をクリックし、
 "User"画面でuser13やuser14が追加されていることを確認