개발노트: 현천 #0 SiSO

Bookmark and Share

thumbnail.jpg

SiSO는 현천 프로젝트의 기반 모듈로, 스스로가 OAuth2 등의 인증 Client이자 동시에 OAuth2 Provider이다. 현재까지는 대표적으로 '혼천의'와 '관문'에서 사용하고 있으며, OpenID/LDAP/Facebook 등을 통한 계정 등록을 지원하고 프로젝트 내의 각 Application에 대한 일관된 사용자 인증 정보와 Single Sign On 기능을 제공한다.

사이트: https://github.com/hyeoncheon/siso

SiSO Server 구성

SiSO Client 구성

SiSO는 중앙에서 인증을 담당하는 Server 부분이 본체이나, 이를 이용하기 위해서는 인증 대상 Application의 구성이 필요하다.

패키지 설치

현재는 단일화된 Gem 등을 구성하지는 못하였고, 일반적인 개발 방식으로 관련 내용의 설정을 하여야 한다.

단계 #1: Gem 및 패키지 설치

SiSO는 OmniAuth를 기반으로 하고 있다. 다음과 같이 OmniAuth의 설치를 진행한다.

$ cat >> Gemfile <<EOF
> 
> # local gems
> gem 'omniauth-oauth2'
> EOF
$ 
$ bundle
<...>
Installing multipart-post 2.0.0
Installing faraday 0.9.1
Installing hashie 3.4.0
Installing jwt 1.3.0
Installing multi_xml 0.5.5
Installing oauth2 1.0.0
Installing omniauth 1.2.2
Installing omniauth-oauth2 1.2.0
<...>
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
$

배포되는 Gem 외의 SiSO 관련 파일을 설치한다. 구성요소는 /lib 아래에 위치할 Strategy 관련 요소와 초기화 모듈, 초기화 설정 파일 등이다.

--- /dev/null
+++ b/app/controllers/sessions_controller.rb
@@ -0,0 +1,27 @@
+class SessionsController < ApplicationController
+  def create
+    omniauth = request.env['omniauth.auth']
+    ai = omniauth[:info].clone
+    ai[:uid] = omniauth[:uid]
+    ai[:omniauth] = omniauth
+    logger.debug("DEBUG info: --#{ai.to_xml}--")
+
+    # for test purpose. edit below for real action.
+    render :xml => ai.to_xml
+  end
+
+  def failure
+    flash[:error] = request.parameters['message']
+    redirect_to root_path
+  end
+
+  def signout
+    if current_user
+      session[:user] = nil
+      session.delete :user
+      flash[:notice] = 'successfully signed out!'
+    end
+    redirect_to root_path
+  end
+end
+# vim: set ts=2 sw=2 expandtab:
--- /dev/null
+++ b/config/initializers/omniauth.rb
@@ -0,0 +1,13 @@
+require 'omniauth-siso'
+require File.expand_path('../../omniauth', __FILE__)
+
+Rails.application.config.middleware.use OmniAuth::Builder do
+  configure do |config|
+    config.path_prefix = SISO_PREFIX
+  end
+  provider :siso, SISO_APP_ID, SISO_SECRET, :client_options => {
+    site: SISO_SITE,
+    authorize_url: SISO_AUTHORIZE_URL,
+    token_url: SISO_TOKEN_URL
+  }
+end
--- /dev/null
+++ b/config/omniauth.rb.dist
@@ -0,0 +1,8 @@
+# Load the Rails application.
+
+SISO_APP_ID = "app_id from siso 0af2159c1b735c9511df..."
+SISO_SECRET = "secret from siso ab1c293db193a1e114ac..."
+SISO_SITE = "http://sandbox.example.com"
+SISO_AUTHORIZE_URL = "/siso/oauth/authorize"
+SISO_TOKEN_URL = "/siso/oauth/token"
+SISO_PREFIX = "/appname/auth"
--- /dev/null
+++ b/lib/omniauth-siso.rb
@@ -0,0 +1 @@
+require 'omniauth/siso'
--- /dev/null
+++ b/lib/omniauth/siso.rb
@@ -0,0 +1,2 @@
+require 'omniauth/siso/version'
+require 'omniauth/strategies/siso'
--- /dev/null
+++ b/lib/omniauth/siso/version.rb
@@ -0,0 +1,5 @@
+module Omniauth
+  module Siso
+    VERSION = "0.0.1"
+  end
+end
--- /dev/null
+++ b/lib/omniauth/strategies/siso.rb
@@ -0,0 +1,43 @@
+require 'omniauth'
+require 'omniauth-oauth2'
+
+module OmniAuth
+  module Strategies
+    class Siso < OmniAuth::Strategies::OAuth2
+      option :name, "siso"
+
+      # default values
+      option :client_options, {
+        :site => "http://sandbox.example.com",
+        :authorize_url => "/siso/oauth/authorize",
+        :token_url => "/siso/oauth/token"
+      }
+
+      uid { raw_info['id'] }
+
+      info do
+        {
+          :name => raw_info['name'],
+          :email => raw_info['mail'],
+          :nickname => raw_info['mail'],
+          :image => raw_info['image'],
+          :phone => raw_info['phone'],
+          :mobile => raw_info['mobile'],
+
+          :gid => raw_info['group_id'],
+          :active => raw_info['active']
+        }
+      end
+
+      extra do
+        {
+          'raw_info' => raw_info
+        }
+      end
+
+      def raw_info
+        @raw_info ||= access_token.get('/siso/api/v1/me.json').parsed
+      end
+    end
+  end
+end

위의 패치는 아래 URL에서 바로 받을 수 있으며,

http://hyeoncheon.github.io/siso.patch

다음과 같이 간단히 Patch 적용을 할 수 있다.

$ wget http://hyeoncheon.github.io/siso.patch -O - |patch -p1
<...>

patching file app/controllers/sessions_controller.rb
patching file config/initializers/omniauth.rb
patching file config/omniauth.rb.dist
patching file lib/omniauth-siso.rb
patching file lib/omniauth/siso.rb
patching file lib/omniauth/siso/version.rb
patching file lib/omniauth/strategies/siso.rb
$

그리고 실 설정파일은 저장소 예외처리

$ cat >> .gitignore <<EOF
> /config/omniauth.rb
> EOF
$ 
$ git commit -m "sisolize part-1: add module"
$

단계 #2: 사용자 Scaffold 생성

통합 인증은 SiSO에서 수행하지만 각 Application은 각자의 사용자 정보를 보관하여 Application에 특화된 정보 수용을 할 수 있다.

$ rails g scaffold user name:string mail:string roles:string image:string siso_uid:integer siso_gid:integer siso_active:boolean last_seen:datetime --no-javascripts --no-stylesheets
      invoke  active_record
      create    db/migrate/20150227101449_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      invoke  resource_route
       route    resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    erb
      create      app/views/users
      create      app/views/users/index.html.erb
      create      app/views/users/edit.html.erb
      create      app/views/users/show.html.erb
      create      app/views/users/new.html.erb
      create      app/views/users/_form.html.erb
      invoke    test_unit
      create      test/controllers/users_controller_test.rb
      invoke    helper
      create      app/helpers/users_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/users/index.json.jbuilder
      create      app/views/users/show.json.jbuilder
      invoke  assets
      invoke    coffee
      invoke    scss
$ vi db/migrate/20150227101449_create_users.rb
$ 
$ rake db:migrate
== 20150227101449 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0068s
-- add_index(:users, :mail, {:unique=>true})
   -> 0.0032s
== 20150227101449 CreateUsers: migrated (0.0101s) =============================

$ 
$ git add <...>
$ git commit -m "sisolize part-2: generate user model"
$

단계 #3: 세션 및 사용자 설정

세션 정보가 없을 경우, 인증을 거치게 하고, SiSO로부터 전송 받은 인증 정보를 이용하여 사용자를 자동으로 추가하거나 세션을 생성하는 등의 작업을 수행하기 위하여 주요 Controller의 설정을 하여야 한다.

$ vi app/controllers/application_controller.rb
$ vi app/controllers/sessions_controller.rb
$ vi app/views/layouts/application.html.erb
$ vi config/routes.rb
$

수정 내용은 아래와 같다.

--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -2,4 +2,10 @@ class ApplicationController < ActionController::Base
   # Prevent CSRF attacks by raising an exception.
   # For APIs, you may want to use :null_session instead.
   protect_from_forgery with: :exception
+
+  private
+  def current_user
+    @current_user ||= User.find_by_id(session[:user]) if session[:user]
+  end
+  helper_method :current_user
 end
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -6,8 +6,30 @@ class SessionsController < ApplicationController
     ai[:omniauth] = omniauth
     logger.debug("DEBUG info: --#{ai.to_xml}--")
 
-    # for test purpose. edit below for real action.
-    render :xml => ai.to_xml
+    #render :xml => ai.to_xml
+    unless @user = User.find_by_siso_uid(ai[:uid])
+      @user = User.create(siso_uid: ai[:uid], siso_gid: ai[:gid],
+                          siso_active: ai[:active],
+                          name: ai[:name], mail: ai[:email], image: ai[:image])
+      flash[:notice] = "New user for #{@user.mail} registered!"
+    else
+      # update user informations from siso.
+      @user.siso_gid = ai[:gid]
+      @user.siso_active = ai[:active]
+      @user.name = ai[:name]
+      @user.mail = ai[:email]
+      @user.image = ai[:image]
+      @user.save
+      flash[:notice] = "welcome #{@user.name}!"
+    end
+    session[:user] = @user.id
+    session[:name] = @user.name
+    session[:mail] = @user.mail
+
+    logger.debug("DEBUG cookie_origin: #{cookies[:siso_oauth_origin]}")
+    next_path = cookies[:siso_oauth_origin] || root_path
+    cookies.delete :siso_oauth_origin
+    redirect_to next_path
   end
 
   def failure
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -8,7 +8,28 @@
 </head>
 <body>
 
+<% if current_user %>
+
+<div class="body-wrapper">
+    <div class="mainpage">
 <%= yield %>
+    </div>
+    <div class="tail-spaceholder">
+    </div>
+</div>
+<div class="sticky-footer">
+</div>
+
+<% else %>
+    <div id="siso-login" class="hc dialog box">
+        <div class="hc dialog title">Login</div>
+        <div class="hc dialog body">
+            <span>Signin with</span>
+            <%= link_to "SiOS", '/yeoksa/auth/siso' %>
+        </div>
+    </div>
+<% end %>
 
 </body>
 </html>
+<!-- vim: set ts=2 sw=2: -->
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -2,11 +2,19 @@ Rails.application.routes.draw do
  scope '/yeoksa' do
   resources :histories
   resources :users
+
+  ### hyeoncheon.siso
+  get '/auth/siso', as: :signin
+  get '/auth/:provider/callback', to: 'sessions#create'
+  get '/auth/failure', to: 'sessions#failure'
+  get '/signout', to: 'sessions#signout'
+
   # The priority is based upon order of creation: first created -> highest priority.
   # See how all your routes lay out with "rake routes".
 
   # You can have the root of your site routed with "root"
   # root 'welcome#index'
+  root 'histories#index'
 
   # Example of regular route:
   #   get 'products/:id' => 'catalog#view'

이상의 내용으로 commit 하면 마무리.

$ git commit -m "sisolize part-3: session handling"
$

Bookmark and Share


따로 명시하지 않는 한에서 이 사이트의 모든 콘텐츠는 다음의 라이선스를 따릅니다: Creative Commons Attribution-NonCommercial 3.0 License