diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index ce6255e16..7c3343ea1 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -451,6 +451,8 @@ class AccountController < ApplicationController eval("code = " + "/^" + home_url.gsub(/\//,"\\\/") + "\\\/*(welcome)?\\\/*(\\\/index\\\/*.*)?\$/") if (code=~params[:back_url] || params[:back_url].to_s.include?('lost_password')) && last_login_on != '' redirect_to user_activities_path(user,host: Setting.host_user) + elsif params[:back_url] + redirect_to params[:back_url] else if last_login_on == '' redirect_to my_account_url diff --git a/app/controllers/oauth_controller.rb b/app/controllers/oauth_controller.rb new file mode 100644 index 000000000..efc28bfa8 --- /dev/null +++ b/app/controllers/oauth_controller.rb @@ -0,0 +1,116 @@ +class OauthController < ApplicationController + before_filter :user_setup + before_filter :require_login, only: [:authorize, :token] + + # 客户端申请认证的URI,包含以下参数: + # + # response_type:表示授权类型,必选项,此处的值固定为”code” + # client_id:表示客户端的ID,必选项 + # redirect_uri:表示重定向URI,可选项 + # scope:表示申请的权限范围,可选项 + # state:表示客户端的当前状态,可以指定任意值(最好是随机字符串),认证服务器会原封不动地返回这个值,可防止CSRF攻击 + # + # 这个页显示授权页,如果授权成功,返回redirect_uri+code + # + # + # 服务器回应客户端的URI,包含以下参数: + # + # code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次, 否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。 + # state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。 + def authorize + + #参数检查 + raise "response_type只能为code" unless params["response_type"] != "code" + raise "client_id为必传项" unless params["client_id"].present? + raise "redirect_uri为必传项" unless params["redirect_uri"].present? + + + config = OauthConfig.where(client_id: params["client_id"], redirect_uri: params["redirect_uri"]).first + raise "client_id或redirect_uri不正确" unless config + + + @data = params + + if params[:gen_code] + ## 检查通过,生成code + oauth = Oauth.create!(client_id: config.client_id, + client_secret: config.client_secret, + redirect_uri: config.redirect_uri + ) + code = oauth.gen_code + + redirect_to params["redirect_uri"] + "?code=#{code}&state=#{params[:state]}" + end + + end + + def test_callback + # 申请 token + # + client_id = "88d893c5a345313e7b8c6fcf23d3d024ee08d5e41ce120c3448b6eea77d8de30" + client_secret = "e9240cc5fc913741db5aea93f2986a8ea0631bb67f7c00e41e491b95d9619e64" + redirect_uri = "http://localhost:3000/oauth/cb" + url = "http://127.0.0.1:3000/oauth/token?grant_type=authorization_code&code=#{params['code']}" + +"&redirect_uri=#{redirect_uri}&client_id=#{client_id}&client_secret=#{client_secret}" + + render text: url + end + + + # 客户端向认证服务器申请令牌的HTTP请求,包含以下参数: + # + # grant_type:表示使用的授权模式,必选项,此处的值固定为”authorization_code”。 + # code:表示上一步获得的授权码,必选项。 + # redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。 + # client_id:表示客户端ID,必选项。 + # client_secret: 表示客户端密钥,必选项。 + # + # + # 认证服务器核对了授权码和”重定向URI”,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。 + # + # 认证服务器发送的HTTP回复,包含以下内容: + # + # access_token:表示访问令牌,必选项。 + # token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。 + # expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。 + # refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。 + # scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。 + def token + if params[:grant_type] == 'authorization_code' + + raise "code必传" unless params["code"] + raise "client_id必传" unless params["client_id"] + raise "client_secret必传" unless params["client_secret"] + + raise "code错误或已超时" unless Oauth.code_valid?(params["code"]) + + oauth = Oauth.auth(params["code"], params["client_id"], params["client_secret"]) + raise "认证不通过" unless oauth + + ## 生成 token + # + oauth.gen_token(User.current.id) + + + { + access_token: oauth.access_token, + token_type: 'bearer', + expires_in: oauth.token_expires_in, + refresh_token: oauth.refresh_token + } + + end + + end + + + private + def require_login + if !User.current.logged? + redirect_to '/login?back_url='+request.original_url + end + end + + include Trustie::Http + +end \ No newline at end of file diff --git a/app/controllers/oschina_controller.rb b/app/controllers/oschina_controller.rb index fc9a4a6a8..7cffee4aa 100644 --- a/app/controllers/oschina_controller.rb +++ b/app/controllers/oschina_controller.rb @@ -1,3 +1,5 @@ +#coding=utf-8 +# class OschinaController < ApplicationController CLIENT_ID = 'e5da9855f89bc724a335d100cb63cf02a03a592bd3151bbc84acf7b2e222ddb8' @@ -48,38 +50,6 @@ class OschinaController < ApplicationController private - def get(url) - uri = URI(url) - res = Net::HTTP.start(uri.host, uri.port, use_ssl: url.start_with?('https')) do |http| - req = Net::HTTP::Get.new(uri) - #req['Content-Type'] = 'application/json' - # The body needs to be a JSON string, use whatever you know to parse Hash to JSON - #req.body = {a: 1}.to_json - http.request(req) - end - - res.body - end - - def post(url) - uri = URI(url) - res = Net::HTTP.start(uri.host, uri.port, use_ssl: url.start_with?('https')) do |http| - req = Net::HTTP::Post.new(uri) - #req['Content-Type'] = 'application/json' - # The body needs to be a JSON string, use whatever you know to parse Hash to JSON - #req.body = {a: 1}.to_json - http.request(req) - end - - res.body - end - - def decode(s) - begin - obj = ActiveSupport::JSON.decode(s) - rescue ActiveSupport::JSON.parse_error - logger.error("Attempted to decode invalid JSON: #{s}") - end - end + include Trustie::Http end \ No newline at end of file diff --git a/app/models/oauth.rb b/app/models/oauth.rb new file mode 100644 index 000000000..5b369be68 --- /dev/null +++ b/app/models/oauth.rb @@ -0,0 +1,14 @@ +require 'base64' + +class Oauth < ActiveRecord::Base + attr_accessible :client_id, :client_secret, :redirect_uri + + + def gen_code + code = Base64.urlsafe_encode64 Digest::MD5.hexdigest "#{Time.now}-#{Random.new_seed}" + update_column(:code, code) + code + end + + +end diff --git a/app/models/oauth_config.rb b/app/models/oauth_config.rb new file mode 100644 index 000000000..15bfcd823 --- /dev/null +++ b/app/models/oauth_config.rb @@ -0,0 +1,3 @@ +class OauthConfig < ActiveRecord::Base + attr_accessible :client_id, :client_secret, :redirect_uri, :scope +end diff --git a/app/views/oauth/authorize.html.erb b/app/views/oauth/authorize.html.erb new file mode 100644 index 000000000..049f09342 --- /dev/null +++ b/app/views/oauth/authorize.html.erb @@ -0,0 +1,19 @@ +授权页 + + +

当前用户: <%= current_user.login %>

+ +

+ + +<%= form_for oauth_authorize_path do%> + + "> + "> + "> + "> + "> + +<% end %> + +

diff --git a/config/routes.rb b/config/routes.rb index b34d7422b..b4a9efacc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -558,6 +558,11 @@ RedmineApp::Application.routes.draw do match 'oschina/login', to: 'oschina#login', :via => :get match 'oschina/login_cb', to: 'oschina#login_callback', :via => [:get, :post] + ## oauth相关 + match 'oauth/authorize', to: 'oauth#authorize', :via => [:get, :post] + match 'oauth/token', to: 'oauth#token', :via => :post + match 'oauth/cb', to: 'oauth#test_callback', :via => :get + # boards match 'boards/:board_id/topics/new', :to => 'messages#new', :via => [:get, :post], :as => 'new_board_message' match 'boards/:id/join_to_org_subfields', :to => 'boards#join_to_org_subfields' diff --git a/db/migrate/20181121064652_create_oauth.rb b/db/migrate/20181121064652_create_oauth.rb new file mode 100644 index 000000000..b54d00d6d --- /dev/null +++ b/db/migrate/20181121064652_create_oauth.rb @@ -0,0 +1,18 @@ +class CreateOauth < ActiveRecord::Migration + def change + create_table :oauth do |t| + t.string :client_id + t.string :client_secret + t.string :code + t.string :redirect_uri + t.string :scope + + t.string :access_token + t.string :refresh_token + t.integer :token_created_at + t.integer :token_expires_in #过期时间 + + t.timestamps + end + end +end diff --git a/db/migrate/20181121071704_create_oauth_configs.rb b/db/migrate/20181121071704_create_oauth_configs.rb new file mode 100644 index 000000000..5e0389b89 --- /dev/null +++ b/db/migrate/20181121071704_create_oauth_configs.rb @@ -0,0 +1,22 @@ +class CreateOauthConfigs < ActiveRecord::Migration + def change + create_table :oauth_configs do |t| + t.string :client_id + t.string :client_secret + t.string :redirect_uri + t.string :scope + + t.timestamps + end + + + OauthConfig.create( + client_id: '88d893c5a345313e7b8c6fcf23d3d024ee08d5e41ce120c3448b6eea77d8de30', + client_secret: 'e9240cc5fc913741db5aea93f2986a8ea0631bb67f7c00e41e491b95d9619e64', + redirect_uri: 'http://localhost:3000/oschina/login_cb', + scope: '' + ) + end + + +end diff --git a/db/schema.rb b/db/schema.rb index 78d5ed6a3..ba8c9e915 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20181121032918) do +ActiveRecord::Schema.define(:version => 20181121071704) do create_table "activities", :force => true do |t| t.integer "act_id", :null => false @@ -1501,6 +1501,29 @@ ActiveRecord::Schema.define(:version => 20181121032918) do t.datetime "updated_at", :null => false end + create_table "oauth_configs", :force => true do |t| + t.string "client_id" + t.string "client_secret" + t.string "redirect_uri" + t.string "scope" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "oauths", :force => true do |t| + t.string "client_id" + t.string "client_secret" + t.string "code" + t.string "redirect_uri" + t.string "scope" + t.string "access_token" + t.string "refresh_token" + t.integer "token_created_at" + t.integer "token_expires_in" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "onclick_times", :force => true do |t| t.integer "user_id" t.datetime "onclick_time" diff --git a/lib/trustie.rb b/lib/trustie.rb index 46554f4c9..31537282c 100644 --- a/lib/trustie.rb +++ b/lib/trustie.rb @@ -4,3 +4,4 @@ require 'trustie/gitlab/api' require 'trustie/grack/grack' require 'trustie/at/at' require 'trustie/sms/sms' +require 'trustie/http/http' \ No newline at end of file diff --git a/lib/trustie/http/http.rb b/lib/trustie/http/http.rb new file mode 100644 index 000000000..be6099fe2 --- /dev/null +++ b/lib/trustie/http/http.rb @@ -0,0 +1,46 @@ +#coding=utf-8 + +require 'net/http' +require 'uri' + + +module Trustie + module Http + + def get(url) + uri = URI(url) + res = Net::HTTP.start(uri.host, uri.port, use_ssl: url.start_with?('https')) do |http| + req = Net::HTTP::Get.new(uri) + #req['Content-Type'] = 'application/json' + # The body needs to be a JSON string, use whatever you know to parse Hash to JSON + #req.body = {a: 1}.to_json + http.request(req) + end + + res.body + end + + def post(url, data=nil) + uri = URI(url) + res = Net::HTTP.start(uri.host, uri.port, use_ssl: url.start_with?('https')) do |http| + req = Net::HTTP::Post.new(uri) + #req['Content-Type'] = 'application/json' + # The body needs to be a JSON string, use whatever you know to parse Hash to JSON + req.body = data if data + http.request(req) + end + + res.body + end + + def decode(s) + begin + obj = ActiveSupport::JSON.decode(s) + rescue ActiveSupport::JSON.parse_error + logger.error("Attempted to decode invalid JSON: #{s}") + end + end + + + end +end \ No newline at end of file