Posts for: #Ldap

レイオフ

4時から寝てあまり眠れなくて8時半に起きた。2時過ぎまでオフィスでブログを書いてた。

レビュー対応

昨日の go-ldap のレビュー対応 の続き。標準ライブラリの sql パッケージのソースを読みながらレビューアが提案した api を サンプル実装 してみた。実際に実装して動かしてみるとコードからのフィードバックが働き、より実践的な感覚で設計の議論できる。他のレビューアもその方がわかりやすいだろうと思ってやってみたが、はたして伝わるかどうか。

クックパッド社のレイオフ

社内 slack で知人から教えてもらって気付いた。

ちょうど晩ご飯の買い出しを終えた頃に一報があってそれからあちこちのタイムラインやニュースや掲示板などを追いかけてだいたいの雰囲気を掴んだ。私は昔からクックパッドさんを応援していて、いまも応援してはいるのだけど、さすがにもうこれは昔のような栄華を取り戻すことはないのだろうと、応援している私自身にも思えるぐらいのインパクトがあった。

この先に起こることは社長が交代する10月までに経営にとってネガティブなことをやり尽くしてその後 mbo して上場廃止にするのではないかと思う。まだまだキャッシュはあるし、右肩下がりとはいえ、現状の有償会員の売上もそれなりにあるので最低限の人員で保守していく分には十分に収益力のあるプラットフォームだと思う。2017年からの10年投資でなにか隠し玉のサービスがあるわけではなく、いろいろやって失敗したのでサービス終了して人員も整理して赤字の出ない程度で継続していくのではないかと思う。

早起きは三文の徳

昨日は飲んだくれで帰ってきて1時に寝て何度か起きて7時に起きたものの、二日酔いではないけど、10時頃までだらだらしていた。それ以降はオフィスで作業していたのだけど、朝起きるのが遅れた分の進捗がいまいちになってしまった。翌2時過ぎまでやってもやりたかったことを完了できなかった。

レビュー対応

先日送った go-ldap の pr のレビューが付いていたので指摘されたところの修正をしたり、バグに気付いてリファクタリングしたりしていた。昨日の夜にコメントが付いていることに気付いたが、飲んだくれだったので対応が遅れた。レビューコメントの1つを除いて他はすべて対応した。残りの1つは api の設計をもっと堅牢、且つ拡張性に富んだものにしてはどうか?という提案。提案内容自体は悪くないと思う。他のレビューアのコメントも確認するためにまたしばし待つ。いまのところはよい感じ。

stripe アカウント

あるアイディアの poc を作ろうと思って stripe のアカウントを作った。当初は決済手段として paypay でやろうと思ったものの、サポートに電話して聞いてみると paypay は実店舗向けにしかサービスを提供していないらしい。テスト環境もなければ、開発会社が検証用途で使うといったことも想定していないらしい。それで stripe のアカウントを作成した。即時でアカウントを発行できて使えるようになった。さすがって感じ。

わかりにくさと能動的

22時に寝て何度か起きて7時に起きた。たまには早く寝てみた。

チャンネルを用いた ldap 検索の api

うちらの要件に足りない機能が go-ldap にある。私が機能拡張についての issue を作ったときにある開発者が先にこの機能が必要だとコメントしてくれた。もともと draft pr で実装されたコードがあったのでそれをベースに検証したら普通に動いた。あとは go のエンジニアリングとして開発者が使いやすいように、私の経験からのアレンジを加えて pr とした。テストも実装した。なにか問題があればレビューで指摘さえしてくれれば私がすぐ修正してマージできるはずと考えている。はてさて、どうなることやら。

能―650年続いた仕掛けとは―

能―650年続いた仕掛けとは― を読んでいる続き。世阿弥の紹介をしている第五章に感化された。

第四章 能にはこんな仕掛けが隠されていた

能はシテ (主役) の役柄や内容で5種類にわけられる。

  1. 初番目物 神: 神様が登場して颯爽 (さっそう) と舞う
  2. 二番目物 男: 修羅物とも呼ばれ、武将が修羅道に落ちた苦しみを描く
  3. 三番目物 女: 鬘物 (かずらもの) とも呼ばれ、優雅な美しいものが多い
  4. 四番目物 狂: 雑能とも呼ばれ、他の4つに分類されないもの
  5. 五番目物 鬼: 切能 (きりのう) とも呼ばれ、鬼や妖怪、精霊、霊獣などがシテになる

さらにこの5つの分類に入らない翁という演目もある。翁を最初に置き、この順番に上演しながら、能と能の間に狂言を演じ、最後に祝言の短い能を演じるのがかつての正式な上演だったらしい。これだけ演じると朝から晩までかかってしまうので忙しい現代ではなかなかみれなくなってしまっているという。

ひと昔前は結婚式で仲人さんや親戚が謡を謡っていたという。たしかに古風な結婚式ではそうだったような、、、と私もうっすらとそういう記憶があるような気もする。

能の身体的な特徴の1つに摺り足がある。摺り足には重い二本の刀を腰に差して腰痛にならないという効能があるらしい。ほんとかな?

世阿弥が能の構造は序破急にせよと書いている。序はワキの登場、破はシテが登場して話をして去る、急は再びシテが姿を変えて登場するといった構造になる。水戸黄門や暴れん坊将軍のような時代劇の最後の展開が急に相当する。水戸黄門で例えると次になる。

  • 序: 現状把握と善人の窮状
  • 破: 善人が騙される/襲われる
  • 急: 印籠を出す

そして、この後に書いてあることが個人的におもしろかった。水戸黄門は番組開始時点では印籠を出すようなシーンはなくて、当初は助さん角さんが敵をたたき斬っていただけだったという。そもそも印籠を出したぐらいで本物の水戸黄門かどうか分かるわけもなく悪人がひれ伏すはずがないw あるときから印籠を出すという急を作って、序破急が安定したことで人気が出て長寿番組となったと書いてある。ほんとかな?

第五章 世阿弥はこんなにすごかった

能の大成に大きな影響を及ぼした世阿弥についていろいろ書いてある。

夢幻能 という能のジャンルを完成させた。念が残る、思いが残っているといった残念を昇華させる物語の構造になっている。世阿弥は特に敗者の無念をみせる舞台構造を作ることに成功したという。もともと日本人は死者を尊ぶ習慣があったことも要因としてあげている。

世阿弥は世襲で継いでいくという家元制度を作った。これは後世に必ず継ぐシステムを作ったと言える。現代まで能が継続されている背景の1つに家元制度はたしかにあげられると私も思う。しかし、現代では基本的人権 (職業選択の自由) に反することから家元制度の批判もあるようだ。著者はこの仕組みを称賛しているが、私は現代の感覚からすると個人の自由を制限して成り立っている古い制度のように感じてあまり著者の意見に同意できなかった。

陰陽の和するところの境を成就とは知るべし

昼や晴れた日には観客の気分が盛り上がり過ぎているので控え目に演じなさい。曇りや雨の日には逆に観客の気持ちが萎えているので派手目に演じなさいといったことを言っている。要は客の状態を見て演じ方を変えなさいと言っている。これは言うは易し、行うは難しだという。能ではこれを楽器の構造から音の力で解決していると説明がある。

時に用ゆるをもて花と知るべし

ともすれば絶対的な善し悪しがあるように思い込み、そのようなものを追求しがちであるが、実際はそのようなものはない。あるのは時との関係性だけだという。易経の時中も引用している。いまがどのような「時」であるかを知り、それがもっとも適合した時期であるか、行動できるか、それこそが「花」であるという。

花と面白きと珍しきと、これ三つは同じ心なり

現代の言葉とはちょっと意味が異なる。

  • 面白き: 目の前がパッと明るくなること
  • 珍しき (愛ず): 愛らしいこと、まったく普通のことに感嘆を抱かせる工夫など
  • 花: 秘すれば花、秘密にすることで偉大な働きをすること

能では、演者はあまり観客に働きかけない。よくわからないことで、逆に観る人が能動的になり、見えないものが見え、聞こえない音が聞こえるようになる。これも秘することによって咲く花だという。師匠が弟子に教えないというのも、簡単なことでも秘することで、弟子が散々苦しみ抜いた上でその助言の価値に気付くこともあるという。

「老後の初心」という考え方。どの歳になっても初心はあるが、歳をとって体力が劣っていくからこそやることも変えていく。第一章にも出てきた能における「初心」という言葉の概念は本当におもしろい。能では体が動かなくなっていくのだから「しないというやり方も方法としてありえる」と考える。演じないことで演じる、歳を取ったときの表現方法がある。高齢な能楽師でしか演じられない境地があるから能楽師は歳を取ることを楽しみにする。この考え方はいまの時代にとてもあうように私は思えた。

c 言語の開発は chatgpt と共に

1時に寝てやや吐き気で起きて休憩して3時ぐらいに寝たか寝てないかわからないうちに8時に起きた。ジンをたくさん飲んで帰ってきたのでうまく眠れなかった。

カスタム overlay モジュールの改修

OpenLDAP Overlays という機能がある。以前 ppolicy_hash_cleartext と カスタム overlay モジュール を組み合わせると ppolicy が平文パスワードをハッシュ化してしまって、自分たちのカスタム overlay モジュールから平文パスワードを取得できないことをデバッグで検証した。ppolicy_hash_cleartext の機能が必要なのかどうかが私はよくわかっていないけど、協調して自分たちのカスタム overlay モジュールが動くならそれに越したことはない。

gdb でデバッグして振る舞いと対応方法が分かったところで issue を作成していた。メンバーが暇だったらやってくれるかな?と思っていたら誰も手をつけないみたいなので私がやろうと決めた。最早、私は c 言語をなんも分からんのやけど、chatgpt に質問しながら教えてもらえる。例えば、c 言語についてもこんな感じにヒアリングしながら実装する。

Q. c 言語で関数の最初に変数を宣言するのと、実際に変数を割り当てるところで宣言するのでなにか振る舞いが違ったりしますか?どちらの方がよいのでしょうか?

C言語は伝統的に変数のスコープとライフタイムを管理するために、ブロック(通常は関数または制御構造内)の先頭で変数を宣言するスタイルを採用してきました。しかし、C99標準以降では、変数はその使用が初めて必要となる場所で宣言できるようになりました。 これら2つのスタイルの違いは、主にスコープ、可読性、そしてある程度はパフォーマンスに関連しています。

スコープとライフタイム

関数の先頭で変数を宣言すると、その変数は関数全体で使用できます。これは必要に応じて変数を再利用できるという利点がありますが、一方でスコープが広くなりすぎて変数の使用を追跡しにくくなるという欠点もあります。 一方、変数を使用が必要となる場所で宣言すると、その変数のスコープとライフタイムはその場所からブロックの終わりまでに制限されます。これは、変数の使用をより明確にし、未使用の変数を避けるという利点があります。

可読性

変数を使用が必要となる場所で宣言すると、その変数の目的と使用方法がより明確になるため、コードの可読性が向上することが多いです。一方で、関数の先頭で変数を一括して宣言すると、変数の目的が明確でない場合や、それらがどのように使用されるのかを追跡するのが難しくなることがあります。

パフォーマンス

パフォーマンスについては、現代のコンパイラは非常に高度に最適化されているため、変数の宣言場所がパフォーマンスに直接的な影響を及ぼすことはほとんどありません。

結論

どちらのスタイルを選ぶかは、主にプログラミングスタイルやチームのコーディング規約、または特定のタスクに対する特定の要件に依存します。しかし、変数を使用が初めて必要となる場所で宣言するスタイルは、コードの可読性を向上させ、変数のスコープとライフタイムを制限するという利点から、一般的には推奨されています。これにより、コードの可読性が向上し、変数の使用範囲が明確になるため、コードの理解と保守が容易になります。

そっか。C99 で変数宣言の制約が撤廃されたんだということを学んだ。そんな感じで openldap についても chatgpt にヒアリングしながら開発したら意外とすんなり実装できてローカルでは動いた。レビューまでできた。

初めてのコントリビュート経験

4時過ぎに寝て7時に起きた。3時前まで相続手続きの書類作業して帰ってきてから本を読んでたら寝るのが遅くなった。

メンバーの oss へのコントリビュート

チームのメンバーがプロダクトで使っているライブラリにコントリビュートした。土日にライブラリのメンテナーからコメントがあって、その指摘にもすぐに対応して日曜日にマージされていた。ライブラリ側のレビューもすぐに終わってすごくうまくいったと言える。

もともとうちらが欲しい機能を作りかけた pr があったものの、作業途中でマージされずにクローズされていた。その残骸を参考にうちらの要件を満たせるかどうかの調査から始めた。調査を進めているうちにうちらの要件には足りない機能があってそれを拡張するようにも指示していた。すぐに出来たというのでライブラリにコントリビュートするように私が指示していた。

その後メンバーに聞いてみると、oss のライブラリにコントリビュートしたのは今回が初めてだという。開発者なら初めて oss にコントリビュートしたときのことを覚えているだろうか?私は、、、何だったか覚えてないが、おそらく linux distributor で働いているときに簡単なパッチを送った気がする。昔はメールや課題管理システムにパッチを投稿していたのが、いまは pr で気軽にパッチを送れる。若いメンバーがうまくコントリビュート経験を積めたことの背景に github がもたらしたパッチを送る簡単さがあるようにも思えた。oss にコントリビュートする機会は普通に oss を使って開発していればいくらでもある。マネージャーがその機会を見逃さず、ちゃんとメンバーにアサインしてあげることの大事さもあると思う。メンバーにはこの成功体験を活かして今後とも活躍してほしい。

サイトデザイン打ち合わせ

先日の ワイヤーフレームのレビュー の続き。顧問のはらさんとデザイナーさんと私の3人でデザイン案をみながらレビュー会をした。素人の私からみたら十分によいものが出来つつあるように思えるのでデザイナーさんのモチベーションを削がないようにサポートできればと思う。はらさんはデザインの専門家なので細かいところの確認や気付きをあげていてさすがという印象。きっとデザイナーさんのモチベーションも上がるのではないかと推測する。私はこれまでデザインの打ち合わせに出たことがなかったので参考になることが多々ある。サイトのトップは普通は会社の紹介があるものらしいけど、私がそれはいらないとデザイナーさんに伝えたのでトップで会社紹介を一切しない稀な構成のサイトのデザインが出来上がった。その後、社名ぐらいあった方がよいのではないかという話しもあってこれから変えるかもしれない。私の会社情報を書かない理由は次になる。

  • 一見さんの訪問客がみるようなサイト (会社) ではない
  • 知人やうちの会社の関係者がみて近況を手早く分かるようにしたい
  • デザインはシンプルな構成にしたい

カスタム overlay モジュールを完全にマスターした

0時に寝て何度か起きて7時に起きた。変な夢をみた気がするが、どんな夢だったかは思い出せない。

openldap のカスタム overlay モジュールのデバッグ

先週末から openldap のカスタム overlay モジュール の開発やデバッグをしている。openldap のソースコードや gdb のデバッグのやり方にも慣れてきて私の中でも理解度が増してきた。

openldap サーバーのデバッグログは次のコードが設定されている。

#define LDAP_DEBUG_TRACE  0x0001

このコードを slapd.conf の loglevel に設定するとデバッグログを出力できるようになる。

loglevel stats 0x0001 ...

このときに ppolicy の overlay モジュールがどのタイミングで呼ばれるかをデバッグログを確認しながら検証した。

overlay ppolicy
ppolicy_hash_cleartext on

結論から言うと次の2点になる。

  • overlay は slapd.conf の後ろに書いた方のカスタムモジュールが先に実行される
  • ppolicy_hash_cleartext は ppolicy_add のタイミングで平文パスワードをハッシュ化している
    • gdb でデバッグすると op->o_request->oq_add->rs_modlist も op->o_request->oq_add->rs_e->e_attrs も同じアドレスを指す
    • overlay の処理は db に書き込む前と後の2つのタイミングがある
slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
...
pa->a_vals[0].bv_val = hpw.bv_val;
pa->a_vals[0].bv_len = hpw.bv_len;

カスタム overlay モジュールを使って openldap からパスワードを取得するには次の順番で処理が行われることを理解しておく必要がある。

  1. mycustom_add <= このタイミングで平文パスワードを取得しないといけない
  2. ppolicy_add <= このタイミングで平文のパスワードをハッシュ化する
  3. mycustom_add_response <= このタイミングではすでにパスワードがハッシュ化されている

openldap のことを何も知らない素人が chatgpt と一緒にデバッグしてこのことを2日で理解できた。開発のやり方が変わっていく予兆を感じた。

oss な開発は chatgpt が猛威を振るう予感

2時に寝て6時半に起きた。開発の追い込みが佳境に入ってきて集中力が増してきた。

chatgpt と一緒にデバッグ

openldap サーバーの拡張の仕組みに Overlays がある。c 言語でカスタム overlay を実装することで openldap サーバーに任意のフック処理を実装できる。いまやっていることはパスワードの追加や更新をフックしてそのパスワードを id 連携するためのモジュールを開発している。というか、開発済みだと聞いていたモジュールが意図したように動かないのでデバッグしている。例えば ppolicy という overlay を使って次のように設定すると、平文で送ったパスワードをディレクトリサービスの db へ格納する前に平文からパスワードをハッシュ化してくれる。この変換はパスワード変更を overlay でフックして実装されている。

overlay ppolicy
ppolicy_hash_cleartext on

overlay は slapd.conf に設定した順番に実行されるようで、それぞれの overlay に依存関係がある場合は実際の処理にも影響がある。そんな openldap サーバーの拡張モジュールの開発を引き継ぐことになったが、私がまったく openldap サーバーのことをわかっていないので chatgpt を使って理解しながらデバッグしている。これがそれなりにうまくいっていて調査が捗った。但し、chatgpt が教えてくれたことなので完全に正しいかどうかの保証がない。振る舞いで検証できるものはともかく、そうじゃないものは最後に有識者に正しいかどうかを確認する必要がある。

例えば、次のような ldif エントリーをサンプルとして、パスワードは userPassword という属性で扱う。ここで userPassword だけコロンが2重 (::) になっていることがわかる。これは属性の値が base64 でエンコーディングされていることを意味している。こういった2重コロンのような短いキーワードを検索で調べるのは難しい。chatgpt ならピンポイントに答えてくれる。

dn: uid=jdoe,ou=users,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: jdoe
cn: John Doe
givenName: John
sn: Doe
mail: jdoe@example.com
userPassword:: e1NTSEF9bm9ZMU5kdzN3WUdSbFhpdDJUaTY5UW9SeXpXaklEeXc=

openldap は oss だし、ドキュメントもインターネット上にあるので構造体の定義や c 言語のサンプルコードも書いてくれる。それらが完全に正しいか、私には判断できないが、openldap のソースコードで調査するところの当たりをつけるには十分な情報を返してくれる。カスタム overlay を開発するときの主要なエントリーポイントと ldap 操作のタグ名は次になる。

  • bi_op_bind: バインド(認証)操作に対応するエントリーポイント、LDAP_REQ_BIND
  • bi_op_search: 検索操作に対応するエントリーポイント、LDAP_REQ_SEARCH
  • bi_op_compare: 比較操作に対応するエントリーポイント、LDAP_REQ_COMPARE
  • bi_op_modify: 修正(属性の追加、削除、変更)操作に対応するエントリーポイント、LDAP_REQ_MODIFY
  • bi_op_modrdn: エントリ名の変更 (MODIFY RDN) 操作に対応するエントリーポイント、LDAP_REQ_MODRDN
  • bi_op_add: エントリの追加操作に対応するエントリーポイント、LDAP_REQ_ADD
  • bi_op_delete: エントリの削除操作に対応するエントリーポイント、LDAP_REQ_DELETE
  • bi_op_abandon: 中止操作に対応するエントリーポイント、LDAP_REQ_ABANDON
  • bi_op_extended: 拡張操作に対応するエントリーポイント、LDAP_REQ_EXTENDED

例えば、LDAP_REQ_ADD は ldap.h で次のように定義されている。

#define LDAP_REQ_ADD        ((ber_tag_t) 0x68U) /* application + constructed */

これを gdb でデバッグしてタグを確認するときは次のように Operation 構造体内の o_tag をチェックすればよい。gdb で16進数表示するときは /x を指定する。

(gdb) print /x op->o_tag
$8 = 0x68

ppolicy よりも前にカスタム overlay を設定すれば平文のパスワードにアクセスできそうにみえるのだけど、gdb でデバッグしているとハッシュ化済みのパスワードになっていた。

あと稼働している openldap サーバーに gdb で attach してデバッグする方法も chatgpt に聞きながら行った。やりたい操作に対して gdb のコマンドを教えてもらってすぐに検証してフィードバックからさらに質問できるのでインタラクティブな repl のような環境と chatgpt は相性がよいように思えた。gdb のコマンドを覚えておく必要も、ググる必要もないことに気付いた。

近況報告

元同僚と 約1年ぶりの近況報告 の雑談会をしてきた。これで3回目かな。毎年の恒例行事のようになってきた。兵庫県の住みたい街ランキングでいつも上位にある 西宮市 でカレーを食べて、バーで飲んできた。三ノ宮から西宮は快速で15分程度の距離。すぐ行ける場所なんだが、とくに行く機会がなかったので神戸に引っ越してきて5年以上経つのに電車で行ったのは今回が初めてになる。いつも通り近況を聞きながら、みんな私と同じぐらいの世代なので今後のキャリアの方向性などを話していた。

私は起業して税金やその仕組みに関心をもつようになり、起業する前より少し詳しくなった。知人から節税相談を受けることもある。税金の基本的な考え方として、1つの大きな収入に対して節税することはできない。自由に使えるお金がほしかったら基本的に節税できない。税金をたくさん払って貯金するしかない。一方で個人と会社に資産を分割したり、共済や基金を活用することで手取りの収入は減るが、支払う税金は少なくなって中長期でみると資産が増える。例えば、共済や基金に積み立てたお金は原則としては退職所得で戻ってくるので、ずっと優遇された 退職所得の所得税 により、最終的に支払う税金が少なくなるからである。これが税金を支払う基本的な考え方。自分の手元にお金を残した上で税金を払いたくないが、どうすればよいか?とよく聞かれるが、そんなことはできないというのが模範回答になる。元同僚も私もそうなのだが、もはや自分の生活にお金をあまり必要としていない。私が節税の仕組みを調べたり実践したりするのは、税金の仕組みを学ぶために過ぎない。ただ知識として学ぶよりも、実際に実践して運用してみる方が学びになる。

以前の 出張もくもく会 の後で懇親会のときにそのうち資本主義は新しい制度にとって変わられるのではないかという話題があった。それは行き過ぎた資本主義の弊害と、資本主義である限り40時間/週の労働時間から抜け出すには資本家になるしかなくて、人類はすでにこれだけ技術があるのだからもっと多くの人が今よりも働かずに食べていけるのではないかと多くの人が考えている。私の場合も、実質は自分のやりたいことしかやってなくて、自分のために働きながらも、老後のために一応はお金をもらっておくみたいな働き方になっている。この考え方は資本主義の次の制度へ移行するときに活きてくればいいなと思う。

リファクタリングにはまった

2時に寝て変な夢をみて何度か起きて7時に起きた。いつも通りな感じ。

go のシリアライズ/デシリアライズとポインタ

ldap の distinguished name (以下dn) をパース するときはエスケープを扱う必要があるのでわりとややこしいのでライブラリを使った方がよいことを前回学んだ。

複数の web api で dn を扱っていると、それぞれで dn をパースして正規化した形で扱わないと大文字小文字の表記揺れなどに対応できなくて困るということに気付いた。いや、前回もうっすら気付いていたのだけど、既存の api はすべて対応しているからいいかなと楽観的に考えていた。すると、たまたま新規に dn を扱う web api を作ったときに正規化を忘れていることに気付いた。今後の保守や拡張を考慮すると、dn を string 型として扱うのは潜在的に正規化漏れの懸念があることからよくないと理解できた。そのため、既存のリクエストで受け取る dn を特別な型として必ず正規化して扱えるようにリファクタリングすることにした。あちこち直す必要はあったが、幸いにも単体テストも結合テストもそこそこあるのでバグっていればテストが落ちることで不具合には気付けるようになっていた。

encoding/json に Marshaler/Unmarshaler のインターフェースが定義されているのでそれぞれのメソッドを実装する必要がある。DNParameter の値を json にシリアライズするときは値レシーバで MarshalJSON メソッドを実装し、デシリアライズするときはポインタレシーバで UnmarshalJSON メソッドを実装しないと json ライブラリで意図した振る舞いにならないようにみえる。ここで UnmarshalJSON するときに byte 列から一旦 json の文字列に変換 (引用符を外す) してから ldap.ParseDN() しないといけない処理を直接 string 型に変換する誤ったコードを書いてしまって、この誤りに気付くのに1-2時間はまってしまった。

  • 誤ったコード
func (p *DNParameter) UnmarshalJSON(data []byte) error {
	dn, err := ldap.ParseDN(string(data))
	if err == nil {
		(*p).Value = dn
	}
	return err
  • 正しいコード
func (p *DNParameter) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return fmt.Errorf("dn should be a string")
	}
	dn, err := ldap.ParseDN(s)
	if err == nil {
		(*p).Value = dn
	}
	return err

このときに引用符を ldap のパーサーがエスケープした形で扱えてしまい、テストは失敗するけれど、見た目がほとんど同じ文字列で動いてしまうのにはまった。引用符がエスケープされてテストが落ちることには気付いたものの、どこの処理が問題なのかが分からなくてはまっていた。おそらくスクラッチからこの仕様でコードを書いていたらすぐに気付いたと思えるが、リファクタリングであちこち書き換えていたからどこの処理が誤っているのかの切り分けに時間がかかった。

syncrepl ことはじめ

1時に寝て5時に起きて7時に起きた。

openldap の syncrepl

openldap サーバー同士のレプリケーションの仕組みとして syncrepl (LDAP Sync Replication) と呼ばれる仕組みがある。experimental ではあるものの、仕様は rfc で提案されていてオープンになっている。実際には openldap サーバー (slapd) で実装されている参照実装が仕様みたいなものかもしれない。

既存の java のアプリケーションで syncrepl の機能を使うツールがあって、ソースコードを読むとかなりレガシーで、もっと言うと設計はひどく、コードも推定バグのような実装をみかけるのであまり使いたくない。go-ldap で syncrepl できれば作り直してしまえばよいのではないかと考えている。ドキュメントには provider (master) と consumer (slave) という用語で説明されているので pubsub に近いような仕組みで実装されているのではないかと推測する。slapd は provider にも consumer にもなる可能性があるのでどちらの実装ももっている。私がやりたいことは更新エントリを取得したいだけなので go-ldap で consumer として振る舞うモジュールのみを作ればよい。

基本的には openldap サーバーに接続して bind して、syncrepl に準拠したやり取りをすればよいのでそんな難しそうにはみえない。いくつかの定型的な通信の実装をすれば consumer ができるのではないかと思う。go-ldap のソースを grep してみる分には特別な機能はなさそうにみえる。ないならないで私が作ってもいいし、このライブラリでやらない方がよい背景があるのであれば、それに従う。ひとまず背景がわかっていないので issue を立てて聞いてみた。

いろいろな変換

前日は0時ぐらいまでオフィスでお仕事していて、家に帰ってお風呂入って荷造りして、そのまま寝ないで5時過ぎに出かけて新神戸駅から6時10分発の始発に乗ってから2時間ほど寝た。昼間働いて18時にホテルに戻ってまた2-3時間ほど寝て晩ご飯食べてから2時間ほどお仕事していた。寝付けなくて3時ぐらいに起きて吐いた。吐いたら気分よくなってその後はよく眠れた。

windows の内部の文字の扱い

前日の日曜日に実装したツールをメンバーに windows server検証してもらう。ある変換処理で windows の内部では文字を utf16le で扱っていて、文字コード変換の処理が漏れていることに気付いた。go の準標準ライブラリを使って、次のようなコードで utf16le から utf8 への変換ができる。

import "golang.org/x/text/encoding/unicode"

decoded, err := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder().Bytes(b)

LDAP の DN の仕様

LDAP の distinguished name (DN) を調べた。例えば、windows で dn を powershell の Get-ADUser を使って取得すると次のような値を取得できる。

dn="CN=Test,CN=Users,DC=example,DC=com"

CNDC は大文字/小文字どちらでもよいが、それが違うと dn の値として別の値になってしまう。できれば小文字に統一したい。その変換処理を実装しようと思って仕様を調べていたら RFC-4514 Lightweight Directory Access Protocol (LDAP): String Representation of Distinguished Names で定義されていることがわかった。ここで定義されている特殊な文字、例えばカンマや等号などはエスケープして値としても使えるように書いてある。キーの値を変換するにはこれらの仕様を理解してパースして変換しないといけない。わりと大変なことを理解して go-ldap のライブラリのコードを読んでいたら正に ParseDN() がそんな実装になっていた。

func ParseDN(str string) (*DN, error)

このツールを使って dn インスタンスにパースして文字列表現を取得すると自動的に正規化されて小文字に変換してくれる。

dn, err := ldap.ParseDN(s)
if err != nil {
    return fmt.Errorf("failed to parse dn: %w", err)
}
dn.String()