Commit dee88453 by Francis Zhou

增加 个人免登录场景 签名方法

parent d1bf8bdb
require 'uri'
require 'base64'
require 'openssl'
require "dingtalk/version" require "dingtalk/version"
require "dingtalk/config"
module Dingtalk module Dingtalk
class Error < StandardError class Error < StandardError
end end
class << self
# 个人免登录场景的签名计算
# @url https://ding-doc.dingtalk.com/doc#/faquestions/hxs5v9
# timestamp 用于计算的时间戳 如果不提供,表示当前时间
# url_encode 是否对最终结果进行 rfc2396 转义
def login_free_signature(app_secret, options = {})
default_options = {
timestamp: nil,
url_encode: true,
}
options = default_options.merge(options)
timestamp = unless options[:timestamp].nil?
options[:timestamp]
else
(Time.now.localtime("+08:00").to_f * 1000).to_i
end
signature_str = OpenSSL::HMAC.digest("SHA256", app_secret, timestamp.to_s)
signature_str_base64 = Base64.encode64(signature_str).strip
if options[:url_encode]
# 不知道为什么 URI.encode 方法不行
return URI.encode_www_form_component(signature_str_base64)
end
signature_str_base64
end
end
end end
...@@ -9,7 +9,8 @@ module Dingtalk ...@@ -9,7 +9,8 @@ module Dingtalk
# 获取 access_token # 获取 access_token
# @url https://ding-doc.dingtalk.com/doc#/serverapi2/eev437 # @url https://ding-doc.dingtalk.com/doc#/serverapi2/eev437
add_request :get_access_token, :get, Dingtalk::RequestUrl::ACCESS_TOKEN do |request| add_request :get_access_token, :get, Dingtalk::RequestUrl::ACCESS_TOKEN do |request|
request.with_key_and_secret! request.add_arg :appkey, required: true, in: :query
request.add_arg :appsecret, required: true, in: :query
end end
end end
end end
......
...@@ -8,10 +8,11 @@ module Dingtalk ...@@ -8,10 +8,11 @@ module Dingtalk
# 企业内部应用免登 # 企业内部应用免登
# @url https://ding-doc.dingtalk.com/doc#/serverapi2/clotub # @url https://ding-doc.dingtalk.com/doc#/serverapi2/clotub
add_request :get_int_user_id, :get, Dingtalk::RequestUrl::GET_USER_INFO do |request| add_request :get_int_login_free_user_id, :get, Dingtalk::RequestUrl::GET_USER_INFO do |request|
request.add_arg :code, required: true, in: :query request.add_arg :code, required: true, in: :query
request.add_arg :access_token, required: true, in: :query request.add_arg :access_token, required: true, in: :query
end end
end end
end end
end end
\ No newline at end of file
module Dingtalk
module Config
class << self
attr_accessor :app_key, :app_secret
end
end
end
\ No newline at end of file
...@@ -4,7 +4,6 @@ require "active_support" ...@@ -4,7 +4,6 @@ require "active_support"
require "active_support/core_ext/hash/keys" require "active_support/core_ext/hash/keys"
require "active_support/core_ext/hash/deep_merge" require "active_support/core_ext/hash/deep_merge"
require "dingtalk" require "dingtalk"
require "dingtalk/config"
module Dingtalk module Dingtalk
module Core module Core
...@@ -18,14 +17,6 @@ module Dingtalk ...@@ -18,14 +17,6 @@ module Dingtalk
@with_key_and_secret = false @with_key_and_secret = false
end end
def with_key_and_secret!
@with_key_and_secret = true
end
def with_key_and_secret?
@with_key_and_secret
end
def is_arg_required?(arg_name) def is_arg_required?(arg_name)
@required_args.include? arg_name @required_args.include? arg_name
end end
...@@ -44,8 +35,6 @@ module Dingtalk ...@@ -44,8 +35,6 @@ module Dingtalk
end end
end end
# options
# with_key_secret 是否携带 appkey, appsecret 在请求 query 中
def add_request(request_name, method, url) def add_request(request_name, method, url)
builder = RequestBuilder.new builder = RequestBuilder.new
yield builder if block_given? yield builder if block_given?
...@@ -56,13 +45,6 @@ module Dingtalk ...@@ -56,13 +45,6 @@ module Dingtalk
} }
request_options = default_options.tap do |h| request_options = default_options.tap do |h|
if builder.with_key_and_secret?
h[:query].merge!({
appkey: Dingtalk::Config.app_key,
appsecret: Dingtalk::Config.app_secret,
})
end
builder.required_args.each do |arg| builder.required_args.each do |arg|
raise Dingtalk::Error.new("missing required argument '#{arg}' when invoke request #{request_name}") \ raise Dingtalk::Error.new("missing required argument '#{arg}' when invoke request #{request_name}") \
if method_args[arg].nil? if method_args[arg].nil?
...@@ -73,6 +55,7 @@ module Dingtalk ...@@ -73,6 +55,7 @@ module Dingtalk
end end
builder.payload_args.each do |arg| builder.payload_args.each do |arg|
h[:body][arg] = method_args[arg]
end end
end end
......
...@@ -4,6 +4,11 @@ module Dingtalk ...@@ -4,6 +4,11 @@ module Dingtalk
ACCESS_TOKEN = "https://oapi.dingtalk.com/gettoken".freeze ACCESS_TOKEN = "https://oapi.dingtalk.com/gettoken".freeze
# 企业内应用免登录获取用户ID # 企业内应用免登录获取用户ID
# https://ding-doc.dingtalk.com/doc#/serverapi2/clotub
GET_USER_INFO = "https://oapi.dingtalk.com/user/getuserinfo".freeze GET_USER_INFO = "https://oapi.dingtalk.com/user/getuserinfo".freeze
# 服务端通过临时授权码获取授权用户的个人信息
# @url https://ding-doc.dingtalk.com/doc#/serverapi2/etaarr
GET_USER_INFO_SNS = "https://oapi.dingtalk.com/sns/getuserinfo_bycode".freeze
end end
end end
\ No newline at end of file
...@@ -7,9 +7,6 @@ RSpec.describe Dingtalk::AccessToken do ...@@ -7,9 +7,6 @@ RSpec.describe Dingtalk::AccessToken do
@app_secret = "mocked_app_secret" @app_secret = "mocked_app_secret"
@mocked_access_token = "mocked_access_token" @mocked_access_token = "mocked_access_token"
Dingtalk::Config.app_key = @app_key
Dingtalk::Config.app_secret = @app_secret
response_body = {}.tap do |h| response_body = {}.tap do |h|
h[:errcode] = 0 h[:errcode] = 0
h[:errmsg] = 0 h[:errmsg] = 0
...@@ -22,7 +19,7 @@ RSpec.describe Dingtalk::AccessToken do ...@@ -22,7 +19,7 @@ RSpec.describe Dingtalk::AccessToken do
end end
it "should return correct access_token from request" do it "should return correct access_token from request" do
response = Dingtalk::AccessToken.get_access_token response = Dingtalk::AccessToken.get_access_token appkey: @app_key, appsecret: @app_secret
expect(response[:access_token]).to eq(@mocked_access_token) expect(response[:access_token]).to eq(@mocked_access_token)
end end
......
...@@ -18,8 +18,8 @@ RSpec.describe Dingtalk::Auth do ...@@ -18,8 +18,8 @@ RSpec.describe Dingtalk::Auth do
.to_return(status: 200, body: response_body.to_json) .to_return(status: 200, body: response_body.to_json)
end end
it 'should get internal app user id' do it 'should get internal app user id in login-free scenario' do
response = Dingtalk::Auth.get_int_user_id code: @int_login_code, access_token: @access_token response = Dingtalk::Auth.get_int_login_free_user_id code: @int_login_code, access_token: @access_token
expect(response[:userid]).to eq(@int_user_id) expect(response[:userid]).to eq(@int_user_id)
expect(response[:is_sys]).to eq(false) expect(response[:is_sys]).to eq(false)
......
require "dingtalk/config"
RSpec.describe Dingtalk::Config do
before :each do
@app_key = "ci_dingtalk_app_key"
@app_secret = "ci_dingtalk_app_secret"
end
it "can set and read config singleton" do
Dingtalk::Config.app_key = @app_key
Dingtalk::Config.app_secret = @app_secret
expect(Dingtalk::Config.app_key).to eq(@app_key)
expect(Dingtalk::Config.app_secret).to eq(@app_secret)
end
end
\ No newline at end of file
...@@ -2,4 +2,14 @@ RSpec.describe Dingtalk do ...@@ -2,4 +2,14 @@ RSpec.describe Dingtalk do
it "has a version number" do it "has a version number" do
expect(Dingtalk::VERSION).not_to be nil expect(Dingtalk::VERSION).not_to be nil
end end
# 测试样本见 https://ding-doc.dingtalk.com/doc#/faquestions/hxs5v9 底部
it "signature correct timestamp with app_secret in personal login-free scenario" do
timestamp = 1546084445901
app_secret = "testappSecret"
signature = Dingtalk.login_free_signature(app_secret, timestamp: timestamp)
expect(signature).to eq("HCbG3xNE3vzhO%2Bu7qCUL1jS5hsu2n5r2cFhnTrtyDAE%3D")
end
end end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment