ユーザーごとに閲覧を制限する。
全てのユーザーが平等に同じファイル内容を見られる現在の状態では、CSVサーバープロジェクトの意味は無い。CSVファイルをメールに添付して送信するのと同じことだ。目指すところは、ユーザーごとに設定した権限によって、閲覧内容を制限すること。この機能をどのように実現すべきか、ちょっと考えてみた。
まず、公開するCSVファイルに一行ごとに、誰が閲覧することが出来るか、その情報を付加する必要がある。ここでは、部署別に閲覧を制限することを考えた。CSVファイルに「path」というタイトルの1列を追加して、そこに閲覧可能な部署情報を付加することにした。
例えば、以下のような組織があったとして...
株式会社RAILS |---営業1部 | |---営業1課 | |---営業2課 | |---営業3課 | |---営業2部 |---営業1課 |---営業2課
path列の閲覧情報には、以下のどれかを記述する。
1. /株式会社RAILS/ 2. /株式会社RAILS/営業1部/ 3. /株式会社RAILS/営業1部/営業1課/ 4. /株式会社RAILS/営業1部/営業2課/ 5. /株式会社RAILS/営業1部/営業3課/ 6. /株式会社RAILS/営業2部/ 7. /株式会社RAILS/営業2部/営業1課/ 8. /株式会社RAILS/営業2部/営業2課/
もし、ユーザーが /株式会社RAILS/営業2部/ の閲覧権限を持っていたとしたら、そのユーザーはpath列に6、7、8の組織のパスが書いてある行を閲覧できる。つまり営業2部以下の組織は全て閲覧できるのだ。ユーザーが社長であれば、その権限は /株式会社RAILS/ であるべきだ。そうすれば1〜8まで、全ての組織を閲覧することが出来る。
特殊な条件として、path列がブランクの行は、だれでも閲覧できることにした。また、閲覧権限を一つも持たないユーザーは、path列がブランクの行だけ閲覧できることになる。以上のような仕様で閲覧制限してみた。
LoginEngineの利用
ユーザー別に管理するので、LoginEngineを利用することにした。以前のsoftwarebookプロジェクトで利用した日本語化したものがあるので、そのままコピーして使うことにした。
- enginesフォルダ、login_engineフォルダをvender/pluginsフォルダにコピーして、db:migrate:enginesを実行した。
- Ruby-GetTextで日本語化したLoginEngineの設定は以下のようにしてある。
# ----------config/environment.rb---------- $KCODE = 'u' require 'jcode' # Be sure to restart your web server when you modify this file. ...(途中省略)... # Include your application configuration below require 'gettext/rails' module LoginEngine config :salt, "zarigani" # 今回、メールによる認証はしない設定にした。 config :use_email_notification, false # 以下で編集可能なフィールドを設定している。 config :changeable_fields, [ 'firstname', 'lastname' ] end Engines.start :login
# ----------app/controllers/application.rb---------- require 'login_engine' class ApplicationController < ActionController::Base init_gettext "csv_server" include LoginEngine helper :user model :user before_filter :login_required end
# ----------app/helpers/application_helpers.rb---------- module ApplicationHelper include LoginEngine end
-
-
- LoginEngineの使い方については、以前の記事「ログイン管理してみたい!その1 Login Engineのインストールと設定。」辺りが、多少参考になるかも。
-
path、paths_usersテーブルの追加
閲覧制限をかける組織情報は、/株式会社RAILS/営業1部/営業1課/ のような書式でpathsテーブルに保存しておく。また、usersテーブルとは多:多の関連になるので、paths_usersテーブルも作成した。
- まずはモデルの作成。
script/generate model path
- テーブルを追加するマイグレーションファイルは以下の通り。
- :paths_usersテーブルは、多:多関連を設定するための実体(モデル)のないテーブル。
- idフィールドを無しにするときは、:id => false とオプションをセットする。
# ---------- db/migrate/003_create_paths.rb ---------- class CreatePaths < ActiveRecord::Migration def self.up create_table :paths do |t| t.column :name, :string end create_table :paths_users, :id => false do |t| t.column :path_id, :integer t.column :user_id, :integer end end def self.down drop_table :paths drop_table :paths_users end end
多:多の関連の設定
usersテーブル、pathsテーブルの関連を設定した。モデルの設定は以下の通り。(オレンジ色の部分が追記した箇所)
# ---------- app/models/user.rb ---------- class User < ActiveRecord::Base include LoginEngine::AuthenticatedUser untranslate :salt, :salted_password, :verified, :role, :security_token, :token_expiry, :created_at, :updated_at, :logged_in_at, :deleted, :delete_after N_("User|Password") has_and_belongs_to_many :paths end
# ---------- app/models/path.rb ---------- class Path < ActiveRecord::Base has_and_belongs_to_many :users end
with_scopeで閲覧制限をする。
閲覧制限は、with_scopeでやってみた。主要なコードは以下のようになった。
...(途中省略)... # 閲覧制限をかけるため、with_scopeのオプション設定を返す。 # 例:ログインユーザーが、営業1部1課を担当している場合、以下のようなハッシュを作成して返す。 # {:find => {:conditions => path LIKE '/株式会社RAILS/営業1部/営業1課/%' OR path=''} def login_user sql = current_paths.inject("path=''") do |result, item| "path LIKE '#{item}%' OR " + result end {:find => {:conditions => sql}} end # ログインユーザーが所有しているpathsテーブルのnameフィールドの配列を返す。 def current_paths paths = session[:user].paths.map(&:name) end ...(途中省略)... def component Display.with_scope(login_user) do @show_wrapper = true if @show_wrapper.nil? @sort_sql = Display.scaffold_columns_hash[current_sort(params)].sort_sql rescue nil @sort_by = @sort_sql.nil? ? "#{Display.table_name}.#{Display.primary_key} asc" : @sort_sql + " " + current_sort_direction(params) @paginator, @displays = paginate(:displays, :order => @sort_by, :per_page => default_per_page) render :action => "component", :layout => false end end ...(途中省略)...
ユーザーを閲覧制限情報のpathと関連付けて登録する。
userとpathを関連付けるビューは、以下のようにした。path.nameの一覧にチェックボックスを付けて表示するだけ。今の所なんの工夫もない...。本当はツリー表示で表現したいところだ。
<%# ----------app/views/user/_edit.rhtml---------- %> <div class="user_edit"> <table> <%= _("%{firstname} %{lastname}") % { :firstname => (form_input changeable(user, "firstname"), _("First Name"), "firstname"), :lastname => (form_input changeable(user, "lastname"), _("Last Name"),"lastname")} %> <%= form_input changeable(user, "login"), _("Login ID"), "login", :size => 30 %><br/> <%= form_input changeable(user, "email"), _("Email"), "email" %> <!--path情報をチェックボックスでリスト表示--------------------------------------------> <tr> <td><label for="path_name">Path:</label></td> </tr> <% for path in Path.find(:all) %> <% checked = @user.paths.find(path.id) rescue nil %> <tr> <td></td> <td><%= check_box_tag "checked_items[#{path.id}]", path.id, checked %><%=h path.name %></td> </tr> <% end %> <!--------------------------------------------path情報をチェックボックスでリスト表示--> <% if submit %> <%= form_input :submit_button, (user.new_record? ? _('Signup') : _('Change Settings')), :class => 'two_columns' %> <% end %> </table> </div>
チェックボックスの情報を受け取るコントローラーは、オレンジ色の部分を以下のように追記した。
# ---------- app/controllers/user_controller.rb ---------- ...(途中省略)... def signup return if generate_blank params[:user].delete('form') params[:user].delete('verified') # you CANNOT pass this as part of the request @user = User.new(params[:user]) begin User.transaction(@user) do @user.new_password = true unless LoginEngine.config(:use_email_notification) and LoginEngine.config(:confirm_account) @user.verified = 1 end if @user.save @user.paths = Path.find(params[:checked_items].keys) if params[:checked_items] ...(途中省略)... protected def do_edit_user(user) begin User.transaction(user) do user.attributes = params[:user].delete_if { |k,v| not LoginEngine.config(:changeable_fields).include?(k) } if user.save # path情報を一つも持たない状態を登録するため、対応するユーザーを、全削除してから登録する。 user.paths.delete(user.paths) user.paths = Path.find(params[:checked_items].keys) if params[:checked_items] ...(途中省略)...
以上で、閲覧制限の準備は整った。新規ユーザー登録の画面は以下のようになる。
以下のような表を作成して、CSVファイルに書き出す。
-
- 利益管理の最小単位は、分類コードと呼ばれる3桁の数字で表現されるコード。
- 組織は、この分類コードを所有して構成される仕組みにする。
/株式会社RAILS/にチェックを入れた状態での表示はこうなる。
下記のように、二つの組織にチェックを入れた状態に変更すると...
チェックを入れた組織だけが表示される!