Просмотр исходного кода

Improved session validation and usage of cors headers.

Martin Edenhofer 8 лет назад
Родитель
Сommit
cd28e904ac

+ 2 - 0
app/assets/javascripts/app/controllers/_ui_element/textarea.coffee

@@ -34,6 +34,8 @@ class App.UiElement.textarea
                         '<div class="btn btn-default qq-upload-icon2 qq-upload-button pull-right" style="">{uploadButtonText}</div>' +
                         '<ul class="qq-upload-list span5" style="margin-top: 10px;"></ul>' +
                       '</div>',
+            customHeaders:
+              'X-CSRF-Token': App.Ajax.token()
             classes:
               success: ''
               fail:    ''

+ 19 - 3
app/assets/javascripts/app/lib/app_post/ajax.coffee

@@ -40,16 +40,22 @@ class App.Ajax
       _instance ?= new _ajaxSingleton
     _instance.current()
 
+  @token: ->
+    if _instance == undefined
+      _instance ?= new _ajaxSingleton
+    _instance.token()
+
 # The actual Singleton class
 class _ajaxSingleton
   defaults:
     contentType: 'application/json'
     dataType: 'json'
     processData: false
-    headers: {'X-Requested-With': 'XMLHttpRequest'}
+    headers:
+      'X-Requested-With': 'XMLHttpRequest'
     cache: false
     async: true
-
+  currentToken: null
   currentRequest: {}
   queueList: []
   queueRunning: false
@@ -63,8 +69,15 @@ class _ajaxSingleton
     # bindings
     $(document).bind('ajaxSend', =>
       @_show_spinner()
-    ).bind('ajaxComplete', =>
+    ).bind('ajaxComplete', (request, xhr, settings) =>
       @_hide_spinner()
+
+      # remeber XSRF-TOKEN for later
+      CSRFToken = xhr.getResponseHeader('CSRF-TOKEN')
+      return if !CSRFToken
+      @currentToken = CSRFToken
+      @defaults.headers['X-CSRF-Token'] = CSRFToken
+      Spine.Ajax.defaults.headers['X-CSRF-Token'] = CSRFToken
     )
 
     # show error messages
@@ -170,6 +183,9 @@ class _ajaxSingleton
   current: =>
     @currentRequest
 
+  token: =>
+    @currentToken
+
   _show_spinner: =>
     @count++
     $('.spinner').show()

+ 5 - 2
app/assets/javascripts/app/lib/base/html5Upload.js

@@ -155,9 +155,7 @@
         uploadCancel: function () {
           var manager = this;
           //manager.uploadsQueue.shift()
-          console.log(99999, manager._xhrs)
           _.each( manager._xhrs, function(xhr){
-            console.log(888, xhr)
             xhr.abort()
           })
           manager._xhrs = []
@@ -198,6 +196,11 @@
 
             xhr.open('POST', manager.uploadUrl);
 
+            // add csrf token
+            if (App.Ajax && App.Ajax.token) {
+              xhr.setRequestHeader('X-CSRF-Token', App.Ajax.token());
+            }
+
             // Triggered when upload starts:
             xhr.upload.onloadstart = function () {
                 // File size is not reported during start!

+ 4 - 3
app/assets/javascripts/app/views/package.jst.eco

@@ -1,16 +1,17 @@
 <div class="page-header-title">
-  <h1><%- @T( 'Package' ) %> <small><%- @T( 'Management' ) %></small></h1>
+  <h1><%- @T( 'Package' ) %> <small><%- @T('Management') %></small></h1>
 </div>
 
 <div class="page-content">
   <!--
   <ul class="nav nav-tabs nav-stacked">
-    <li class=""><a data-type="" ><%- @T( 'Installed' ) %></a></li>
-    <li class=""><a data-type="" ><%- @T( 'Store' ) %></a></li>
+    <li class=""><a data-type="" ><%- @T('Installed') %></a></li>
+    <li class=""><a data-type="" ><%- @T('Store') %></a></li>
   </ul>
   -->
   <p>
     <form action="<%= App.Config.get('api_path') %>/packages" method="post" enctype="multipart/form-data" class="horizontal center">
+      <input type="hidden" name="authenticity_token" value="<%= App.Ajax.token() %>"/>
       <input type="file" name="file_upload"/>
       <button class="align-right btn btn--primary" type="submit"><%- @T('Install Package') %></button>
     </form>

+ 1 - 1
app/controllers/activity_stream_controller.rb

@@ -1,7 +1,7 @@
 # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
 
 class ActivityStreamController < ApplicationController
-  before_action :authentication_check
+  prepend_before_action :authentication_check
 
   # GET /api/v1/activity_stream
   def show

+ 31 - 15
app/controllers/application_controller.rb

@@ -4,8 +4,6 @@ require 'exceptions'
 class ApplicationController < ActionController::Base
   include ErrorHandling
 
-  #  http_basic_authenticate_with :name => "test", :password => "ttt"
-
   helper_method :current_user,
                 :authentication_check,
                 :config_frontend,
@@ -17,36 +15,53 @@ class ApplicationController < ActionController::Base
                 :model_index_render
 
   skip_before_action :verify_authenticity_token
-  before_action :transaction_begin, :set_user, :session_update, :user_device_check, :cors_preflight_check
-  after_action  :transaction_end, :http_log, :set_access_control_headers
+  before_action :verify_csrf_token, :transaction_begin, :set_user, :session_update, :user_device_check, :cors_preflight_check
+  after_action  :transaction_end, :http_log, :set_access_control_headers, :set_csrf_token_headers
 
   # For all responses in this controller, return the CORS access control headers.
   def set_access_control_headers
+    return if @_auth_type != 'token_auth' && @_auth_type != 'basic_auth'
+    set_access_control_headers_execute
+  end
+
+  def set_access_control_headers_execute
     headers['Access-Control-Allow-Origin']      = '*'
-    headers['Access-Control-Allow-Methods']     = 'POST, GET, PUT, DELETE, OPTIONS'
+    headers['Access-Control-Allow-Methods']     = 'POST, GET, PUT, DELETE, PATCH, OPTIONS'
     headers['Access-Control-Max-Age']           = '1728000'
     headers['Access-Control-Allow-Headers']     = 'Content-Type, Depth, User-Agent, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control, Accept-Language'
-    headers['Access-Control-Allow-Credentials'] = 'true'
   end
 
   # If this is a preflight OPTIONS request, then short-circuit the
   # request, return only the necessary headers and return an empty
   # text/plain.
-
   def cors_preflight_check
+    return true if @_auth_type != 'token_auth' && @_auth_type != 'basic_auth'
+    cors_preflight_check_execute
+  end
 
-    return if request.method != 'OPTIONS'
-
+  def cors_preflight_check_execute
+    return true if request.method != 'OPTIONS'
     headers['Access-Control-Allow-Origin']      = '*'
-    headers['Access-Control-Allow-Methods']     = 'POST, GET, PUT, DELETE, OPTIONS'
+    headers['Access-Control-Allow-Methods']     = 'POST, GET, PUT, DELETE, PATCH, OPTIONS'
     headers['Access-Control-Allow-Headers']     = 'Content-Type, Depth, User-Agent, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control, Accept-Language'
     headers['Access-Control-Max-Age']           = '1728000'
-    headers['Access-Control-Allow-Credentials'] = 'true'
     render text: '', content_type: 'text/plain'
-
     false
   end
 
+  def set_csrf_token_headers
+    return true if @_auth_type.present? && @_auth_type != 'session'
+    headers['CSRF-TOKEN'] = form_authenticity_token
+  end
+
+  def verify_csrf_token
+    return true if request.method != 'POST' && request.method != 'PUT' && request.method != 'DELETE' && request.method != 'PATCH'
+    return true if @_auth_type == 'token_auth' || @_auth_type == 'basic_auth'
+    return true if valid_authenticity_token?(session, params[:authenticity_token] || request.headers['X-CSRF-Token'])
+    logger.info 'CSRF token verification failed'
+    raise Exceptions::NotAuthorized, 'CSRF token verification failed!'
+  end
+
   def http_log_config(config)
     @http_log_support = config
   end
@@ -74,8 +89,9 @@ class ApplicationController < ActionController::Base
     @_current_user = User.lookup(id: session[:user_id])
   end
 
-  def current_user_set(user)
+  def current_user_set(user, auth_type = 'session')
     session[:user_id] = user.id
+    @_auth_type = auth_type
     @_current_user = user
     set_user
   end
@@ -224,7 +240,7 @@ class ApplicationController < ActionController::Base
     )
   end
 
-  def authentication_check_only(auth_param)
+  def authentication_check_only(auth_param = {})
     #logger.debug 'authentication_check'
     #logger.debug params.inspect
     #logger.debug session.inspect
@@ -336,7 +352,7 @@ class ApplicationController < ActionController::Base
       raise Exceptions::NotAuthorized, 'Not authorized (user)!'
     end
 
-    current_user_set(user)
+    current_user_set(user, auth_type)
     user_device_log(user, auth_type)
     logger.debug "#{auth_type} for '#{user.login}'"
     true

+ 1 - 1
app/controllers/applications_controller.rb

@@ -1,7 +1,7 @@
 # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
 
 class ApplicationsController < ApplicationController
-  before_action { authentication_check(permission: 'admin.api') }
+  prepend_before_action { authentication_check(permission: 'admin.api') }
 
   def index
     all = Doorkeeper::Application.all

+ 1 - 1
app/controllers/calendar_subscriptions_controller.rb

@@ -3,7 +3,7 @@
 require 'icalendar'
 
 class CalendarSubscriptionsController < ApplicationController
-  before_action { authentication_check( { basic_auth_promt: true, permission: 'user_preferences.calendar' } ) }
+  prepend_before_action { authentication_check( { basic_auth_promt: true, permission: 'user_preferences.calendar' } ) }
 
   # @path       [GET] /calendar_subscriptions
   #

+ 1 - 1
app/controllers/calendars_controller.rb

@@ -1,7 +1,7 @@
 # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
 
 class CalendarsController < ApplicationController
-  before_action { authentication_check(permission: 'admin.calendar') }
+  prepend_before_action { authentication_check(permission: 'admin.calendar') }
 
   def index
 

+ 1 - 20
app/controllers/channels_email_controller.rb

@@ -1,10 +1,9 @@
 # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
 
 class ChannelsEmailController < ApplicationController
-  before_action :authentication_check
+  prepend_before_action { authentication_check(permission: 'admin.channel_email') }
 
   def index
-    permission_check('admin.channel_email')
     system_online_service = Setting.get('system_online_service')
     account_channel_ids = []
     notification_channel_ids = []
@@ -54,9 +53,6 @@ class ChannelsEmailController < ApplicationController
 
   def probe
 
-    # check admin permissions
-    permission_check('admin.channel_email')
-
     # probe settings based on email and password
     result = EmailHelper::Probe.full(
       email: params[:email],
@@ -74,9 +70,6 @@ class ChannelsEmailController < ApplicationController
 
   def outbound
 
-    # check admin permissions
-    permission_check('admin.channel_email')
-
     # verify access
     return if params[:channel_id] && !check_access(params[:channel_id])
 
@@ -86,9 +79,6 @@ class ChannelsEmailController < ApplicationController
 
   def inbound
 
-    # check admin permissions
-    permission_check('admin.channel_email')
-
     # verify access
     return if params[:channel_id] && !check_access(params[:channel_id])
 
@@ -103,9 +93,6 @@ class ChannelsEmailController < ApplicationController
 
   def verify
 
-    # check admin permissions
-    permission_check('admin.channel_email')
-
     email = params[:email] || params[:meta][:email]
     email = email.downcase
     channel_id = params[:channel_id]
@@ -195,7 +182,6 @@ class ChannelsEmailController < ApplicationController
   end
 
   def enable
-    permission_check('admin.channel_email')
     channel = Channel.find_by(id: params[:id], area: 'Email::Account')
     channel.active = true
     channel.save!
@@ -203,7 +189,6 @@ class ChannelsEmailController < ApplicationController
   end
 
   def disable
-    permission_check('admin.channel_email')
     channel = Channel.find_by(id: params[:id], area: 'Email::Account')
     channel.active = false
     channel.save!
@@ -211,7 +196,6 @@ class ChannelsEmailController < ApplicationController
   end
 
   def destroy
-    permission_check('admin.channel_email')
     channel = Channel.find_by(id: params[:id], area: 'Email::Account')
     channel.destroy
     render json: {}
@@ -229,9 +213,6 @@ class ChannelsEmailController < ApplicationController
 
     check_online_service
 
-    # check admin permissions
-    permission_check('admin.channel_email')
-
     adapter = params[:adapter].downcase
 
     email = Setting.get('notification_sender')

Некоторые файлы не были показаны из-за большого количества измененных файлов