devise で ActiveDirectory 認証


# 前提条件

以下の環境にて動作確認が行えました。

## 開発環境

* Mac OS X Yosemite (10.10.3)
* Bundler version 1.10.5
* rbenv 0.4.0
* ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]
* mysql  Ver 14.14 Distrib 5.6.24, for osx10.10 (x86_64)

## Rails

* rails (4.2.3)
* net-ldap (0.11)
* devise (3.5.1)
* devise_ldap_authenticatable (0.8.5)

## Active Directory

* Windows Server 2012

# devise のインストールと設定

## Gemfile 追記

```ruby:Gemfile
gem 'devise'
gem 'devise_ldap_authenticatable'
```

## インストール

```
$ bundle install -j4 --path=vendor/bundle
$ rails g devise:install
$ rails g devise User
$ rails g devise_ldap_authenticatable:install
```

# User モデル修正

```ruby:app/models/user.rb
class User < ActiveRecord::Base
 devise :ldap_authenticatable, :rememberable, :trackable
end
```

# マイグレーションファイル修正

デフォルトの email ログインを username ログインに修正。

```ruby:db/migrate/YYYYMMDDhhmmss_devise_create_user.rb
class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|
      ## LDAP authenticatable
      t.string :username, null: false, default: "", unique: true
      ## Database authenticatable
      # t.string :email,              null: false, default: ""
      # t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      # t.string   :reset_password_token
      # t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :users, :username,                unique: true
    # add_index :users, :email,                unique: true
    # add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end
```

# マイグレーション

```
$ rake db:migrate
```

# devise 初期設定

修正箇所のみ。

```ruby:config/initializers/devise.rb
config.ldap_logger = true
config.ldap_create_user = true
config.ldap_update_password = false
config.ldap_use_admin_to_bind = true
config.authentication_keys = [ :username ]
```

# LDAP 設定

```yaml:config/ldap.yml
development:
host: <Domain Controller を指定>
port: 389
attribute: sAMAccountName
base: dc=xxx,dc=xx,dc=xx
admin_user: "<bind user>"
admin_password: "bind user password"
ssl: false
```

`admin_user` と `admin_password` は `""` で括る必要があります。
デフォルトだと `""` がありませんが、その状態だと以下のエラーが発生してかなりハマりました。。

```
Net::LDAP::LdapError (Invalid binding information)
```

# ログイン用 view 作成

```
$ rails g devise:views
```

# ログイン画面修正

* `:email` => `:username`
* `email_field` => `text_field`

```erb:app/views/devise/session/new.html.erb
<h2>Log in</h2>

<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
  <div class="field">
    <%= f.label :username %><br />
    <%= f.text_field :username, autofocus: true %>
  </div>

  <div class="field">
    <%= f.label :password %><br />
    <%= f.password_field :password, autocomplete: "off" %>
  </div>

  <% if devise_mapping.rememberable? -%>
    <div class="field">
      <%= f.check_box :remember_me %>
      <%= f.label :remember_me %>
    </div>
  <% end -%>

  <div class="actions">
    <%= f.submit "Log in" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>
```

# ルーティング

## 設定

```ruby:config/routes.rb
devise_for :users, only: [:sessions]
```

## 確認

```sh
rake routes
```

# 全ページ認証設定

```ruby:app/controllers/application_controller.rb
before_action :authenticate_user!
```

# ログアウトリンク設定

```erb
<%= link_to "Logout", destroy_user_session_path, method: :delete, :class => 'navbar-link' %>