agora_dynamic_key 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/dynamic_key.rb +13 -0
- data/lib/dynamic_key/access_token.rb +55 -0
- data/lib/dynamic_key/rtc_token_builder.rb +112 -0
- data/lib/dynamic_key/rtm_token_builder.rb +46 -0
- data/lib/dynamic_key/sign.rb +73 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fc5aad7f3d500a314906d62f0aaf3cfb2266890e
|
4
|
+
data.tar.gz: 1d8f2023302be38b0deccd0db0894e60216fe369
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e742e625976a71cf5c0ca53b92f3be6ff28435c4170132c4dc12763637f2933aec892053a0263eba5fbb73b070af8b69dee245689941b7ba9871af9b1c28679a
|
7
|
+
data.tar.gz: 9a669a7c61cfc91c093fc11e185354dedce2b59f0697f2967862fd2abbc10851ed1d944786849f6b031064b5f44526bb2547c8e853d42cc3120916dad9e34fd9
|
data/lib/dynamic_key.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'zlib'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
require_relative 'dynamic_key/sign'
|
7
|
+
require_relative 'dynamic_key/access_token'
|
8
|
+
require_relative 'dynamic_key/rtc_token_builder'
|
9
|
+
require_relative 'dynamic_key/rtm_token_builder'
|
10
|
+
|
11
|
+
module AgoraDynamicKey
|
12
|
+
VERSION = "0.1.0"
|
13
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module AgoraDynamicKey
|
2
|
+
module Privilege
|
3
|
+
JOIN_CHANNEL = 1
|
4
|
+
PUBLISH_AUDIO_STREAM = 2
|
5
|
+
PUBLISH_VIDEO_STREAM = 3
|
6
|
+
PUBLISH_DATA_STREAM = 4
|
7
|
+
RTM_LOGIN = 1000 # for RTM Only
|
8
|
+
end
|
9
|
+
|
10
|
+
class AccessToken
|
11
|
+
VERSION = "006"
|
12
|
+
|
13
|
+
ONE_DAY = 864_00
|
14
|
+
SEED = 2 ** 32 - 1
|
15
|
+
|
16
|
+
attr_accessor :app_id, :channel_name, :app_certificate,
|
17
|
+
:uid, :privileges, :privilege_expired_ts,
|
18
|
+
:salt, :expired_ts
|
19
|
+
|
20
|
+
def initialize args={}
|
21
|
+
@app_id = args[:app_id]
|
22
|
+
@channel_name = args.fetch(:channel_name, "")
|
23
|
+
@app_certificate = args[:app_certificate]
|
24
|
+
@uid = "#{args.fetch(:uid, "")}"
|
25
|
+
@privileges = {}
|
26
|
+
@privilege_expired_ts = args[:privilege_expired_ts]
|
27
|
+
@salt = SecureRandom.rand(SEED)
|
28
|
+
@expired_ts = Time.now.to_i + ONE_DAY
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_privilege privilege, ts
|
32
|
+
privileges[privilege] = ts
|
33
|
+
end
|
34
|
+
|
35
|
+
alias grant add_privilege
|
36
|
+
|
37
|
+
def build
|
38
|
+
Sign.encode self
|
39
|
+
end
|
40
|
+
|
41
|
+
def build!
|
42
|
+
Sign.encode! self
|
43
|
+
end
|
44
|
+
|
45
|
+
def from_string token
|
46
|
+
Sign.decode token
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.generate! payload={}, &block
|
50
|
+
token = AccessToken.new payload
|
51
|
+
block.call token
|
52
|
+
token.build!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module AgoraDynamicKey
|
2
|
+
|
3
|
+
class RTCTokenBuilder
|
4
|
+
class InvalidParamsError < StandardError
|
5
|
+
attr_reader :params, :missing_keys
|
6
|
+
def initialize args={}
|
7
|
+
super
|
8
|
+
@params = args[:params]
|
9
|
+
@missing_keys = args[:missing_keys]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Role
|
14
|
+
# DEPRECATED. Role::ATTENDEE has the same privileges as Role::PUBLISHER.
|
15
|
+
ATTENDEE = 0
|
16
|
+
|
17
|
+
# RECOMMENDED. Use this role for a voice/video call or a live broadcast, if your scenario does not require authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in).
|
18
|
+
PUBLISHER = 1
|
19
|
+
|
20
|
+
# Only use this role if your scenario require authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in).
|
21
|
+
# @note In order for this role to take effect, please contact our support team to enable authentication for Hosting-in for you. Otherwise, Role::SUBSCRIBER still has the same privileges as Role::PUBLISHER.
|
22
|
+
SUBSCRIBER = 2
|
23
|
+
|
24
|
+
# DEPRECATED. Role::ADMIN has the same privileges as Role::PUBLISHER.
|
25
|
+
ADMIN = 101
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
|
30
|
+
#
|
31
|
+
# Builds an RTC token using an Integer uid.
|
32
|
+
# @param payload
|
33
|
+
# :app_id The App ID issued to you by Agora.
|
34
|
+
# :app_certificate Certificate of the application that you registered in the Agora Dashboard.
|
35
|
+
# :channel_name The unique channel name for the AgoraRTC session in the string format. The string length must be less than 64 bytes. Supported character scopes are:
|
36
|
+
# - The 26 lowercase English letters: a to z.
|
37
|
+
# - The 26 uppercase English letters: A to Z.
|
38
|
+
# - The 10 digits: 0 to 9.
|
39
|
+
# - The space.
|
40
|
+
# - "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
|
41
|
+
# :uid User ID. A 32-bit unsigned integer with a value ranging from 1 to (2^32-1).
|
42
|
+
# :role See #userRole.
|
43
|
+
# - Role::PUBLISHER; RECOMMENDED. Use this role for a voice/video call or a live broadcast.
|
44
|
+
# - Role::SUBSCRIBER: ONLY use this role if your live-broadcast scenario requires authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in). In order for this role to take effect, please contact our support team to enable authentication for Hosting-in for you. Otherwise, Role_Subscriber still has the same privileges as Role_Publisher.
|
45
|
+
# :privilege_expired_ts represented by the number of seconds elapsed since 1/1/1970. If, for example, you want to access the Agora Service within 10 minutes after the token is generated, set expireTimestamp as the current timestamp + 600 (seconds).
|
46
|
+
#
|
47
|
+
# @return The new Token.
|
48
|
+
#
|
49
|
+
def build_token_with_uid payload
|
50
|
+
check! payload, %i[app_id app_certificate channel_name role uid privilege_expired_ts]
|
51
|
+
build_token_with_account @params.merge(:account => @params[:uid])
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Builds an RTC token using a string user_account.
|
56
|
+
# @param payload
|
57
|
+
# :app_id The App ID issued to you by Agora.
|
58
|
+
# :app_certificate Certificate of the application that you registered in the Agora Dashboard.
|
59
|
+
# :channel_name The unique channel name for the AgoraRTC session in the string format. The string length must be less than 64 bytes. Supported character scopes are:
|
60
|
+
# - The 26 lowercase English letters: a to z.
|
61
|
+
# - The 26 uppercase English letters: A to Z.
|
62
|
+
# - The 10 digits: 0 to 9.
|
63
|
+
# - The space.
|
64
|
+
# - "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
|
65
|
+
# :account The user account.
|
66
|
+
# :role See #userRole.
|
67
|
+
# - Role::PUBLISHER; RECOMMENDED. Use this role for a voice/video call or a live broadcast.
|
68
|
+
# - Role::SUBSCRIBER: ONLY use this role if your live-broadcast scenario requires authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in). In order for this role to take effect, please contact our support team to enable authentication for Hosting-in for you. Otherwise, Role_Subscriber still has the same privileges as Role_Publisher.
|
69
|
+
# :privilege_expired_ts represented by the number of seconds elapsed since 1/1/1970. If, for example, you want to access the Agora Service within 10 minutes after the token is generated, set expireTimestamp as the current timestamp + 600 (seconds).
|
70
|
+
#
|
71
|
+
# @return The new Token.
|
72
|
+
#
|
73
|
+
def build_token_with_account payload
|
74
|
+
check! payload, %i[app_id app_certificate channel_name role account privilege_expired_ts]
|
75
|
+
@params.merge!(:uid => @params[:account])
|
76
|
+
generate_access_token!
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# generate access token
|
82
|
+
def generate_access_token!
|
83
|
+
# Assign appropriate access privileges to each role.
|
84
|
+
AgoraDynamicKey::AccessToken.generate!(@params) do |t|
|
85
|
+
t.grant AgoraDynamicKey::Privilege::JOIN_CHANNEL, t.privilege_expired_ts
|
86
|
+
if @params[:role] == Role::PUBLISHER ||
|
87
|
+
@params[:role] == Role::SUBSCRIBER ||
|
88
|
+
@params[:role] == Role::ADMIN
|
89
|
+
t.grant AgoraDynamicKey::Privilege::PUBLISH_AUDIO_STREAM, t.privilege_expired_ts
|
90
|
+
t.grant AgoraDynamicKey::Privilege::PUBLISH_VIDEO_STREAM, t.privilege_expired_ts
|
91
|
+
t.grant AgoraDynamicKey::Privilege::PUBLISH_DATA_STREAM, t.privilege_expired_ts
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# params check
|
97
|
+
def check!(payload, args)
|
98
|
+
raise InvalidParamsError.new(params: payload), "invalid params" if payload.nil? or payload.empty?
|
99
|
+
symbolize_keys payload.select { |key| args.include? key }
|
100
|
+
missing_keys = args - @params.keys
|
101
|
+
raise InvalidParamsError.new(params: payload, missing_keys: missing_keys), "missing params" if missing_keys.size != 0
|
102
|
+
_invalid_params = @params.select { |hash, (k, v)| v.nil? or v.empty?}
|
103
|
+
raise InvalidParamsError.new(params: payload), "invalid params" if _invalid_params.empty?
|
104
|
+
end
|
105
|
+
|
106
|
+
# symbolize keys
|
107
|
+
def symbolize_keys payload
|
108
|
+
@params = payload.inject({}) { |hash, (k, v)| hash[k.to_sym] = v; hash }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module AgoraDynamicKey
|
2
|
+
class RTMTokenBuilder
|
3
|
+
module Role
|
4
|
+
RTM_USER = 1
|
5
|
+
end
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :token
|
9
|
+
|
10
|
+
#
|
11
|
+
# @param payload
|
12
|
+
# :app_id app_id The App ID issued to you by Agora. Apply for a new App ID from
|
13
|
+
# Agora Dashboard if it is missing from your kit. See Get an App ID.
|
14
|
+
# :app_certificate app_certificate Certificate of the application that you registered in
|
15
|
+
# the Agora Dashboard. See Get an App Certificate.
|
16
|
+
# :role role AgoraDynamicKey::RTCTokenBuilder::Role::RTM_USER = 1: RTM USER
|
17
|
+
# :account User Account.
|
18
|
+
# :privilege_expired_ts represented by the number of seconds elapsed since 1/1/1970.
|
19
|
+
# If, for example, you want to access the Agora Service within 10 minutes
|
20
|
+
# after the token is generated, set expireTimestamp as the current time stamp
|
21
|
+
# + 600 (seconds).
|
22
|
+
def build_token payload
|
23
|
+
check! payload, %i[app_id app_certificate role account privilege_expired_ts]
|
24
|
+
token = AccessToken.new @params.merge(:channel_name => @params[:account])
|
25
|
+
token.grant Privilege::RTM_LOGIN, @params[:privilege_expired_ts]
|
26
|
+
token.build!
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
# params check
|
31
|
+
def check!(payload, args)
|
32
|
+
raise InvalidParamsError.new(params: payload), "invalid params" if payload.nil? or payload.empty?
|
33
|
+
symbolize_keys payload.select { |key| args.include? key }
|
34
|
+
missing_keys = args - @params.keys
|
35
|
+
raise InvalidParamsError.new(params: payload, missing_keys: missing_keys), "missing params" if missing_keys.size != 0
|
36
|
+
_invalid_params = @params.select { |hash, (k, v)| v.nil? or v.empty?}
|
37
|
+
raise InvalidParamsError.new(params: payload), "invalid params" if _invalid_params.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
# symbolize keys
|
41
|
+
def symbolize_keys payload
|
42
|
+
@params = payload.inject({}) { |hash, (k, v)| hash[k.to_sym] = v; hash }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AgoraDynamicKey
|
4
|
+
class Sign
|
5
|
+
MAX_SIZE = 1024
|
6
|
+
SHA256 = OpenSSL::Digest.new("sha256")
|
7
|
+
class InvalidToken < StandardError; end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
private def parse_map_uint32 map
|
11
|
+
binary = [map.size].pack "v"
|
12
|
+
binary << map.flatten.pack("vV"*map.size)
|
13
|
+
end
|
14
|
+
|
15
|
+
def encode option
|
16
|
+
encode! option
|
17
|
+
rescue
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def encode! option
|
22
|
+
# pack message
|
23
|
+
message = [option.salt, option.expired_ts].pack "VV" # pack into uint16 with little endian
|
24
|
+
message << parse_map_uint32(option.privileges)
|
25
|
+
|
26
|
+
# generate signature
|
27
|
+
to_sign = "#{option.app_id}#{option.channel_name}#{option.uid}#{message}"
|
28
|
+
|
29
|
+
sign = OpenSSL::HMAC.new(option.app_certificate, SHA256).update(to_sign).digest
|
30
|
+
|
31
|
+
crc32_channel_name = Zlib::crc32(option.channel_name) & 0xffffffff
|
32
|
+
crc32_uid = Zlib::crc32("#{option.uid}") & 0xffffffff
|
33
|
+
|
34
|
+
uint32_channel_name = [crc32_channel_name].pack "V"
|
35
|
+
uint32_uid = [crc32_uid].pack "V"
|
36
|
+
|
37
|
+
uint16_sign_size = [sign.size].pack "v"
|
38
|
+
|
39
|
+
uint16_message_size = [message.size].pack "v"
|
40
|
+
# generate content
|
41
|
+
content = "#{uint16_sign_size}#{sign}#{uint32_channel_name}#{uint32_uid}#{uint16_message_size}#{message}"
|
42
|
+
# final content
|
43
|
+
"#{AccessToken::VERSION}#{option.app_id}#{Base64.strict_encode64(content)}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def decode string
|
47
|
+
docode!
|
48
|
+
rescue
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def decode! string
|
53
|
+
version = string[0..2]
|
54
|
+
raise InvalidToken, "can't match version" unless version == VERSION
|
55
|
+
appId = string[3..3+31]
|
56
|
+
content = string[3+32..-1]
|
57
|
+
content_binary = Base64.strict_decode64(content)
|
58
|
+
uint16_sign_size = content_binary.unpack("v")[0]
|
59
|
+
sign = content_binary[2, uint16_sign_size].unpack "H*"
|
60
|
+
offset = uint16_sign_size + 2
|
61
|
+
uint32_channel_name, uint32_uid = content_binary[offset..offset+1].unpack("VV")
|
62
|
+
offset = offset + 8
|
63
|
+
uint16_message_size = content_binary[offset..offset+1].unpack("v")[0]
|
64
|
+
offset = offset + 2
|
65
|
+
message = content_binary[offset..offset+uint16_message_size]
|
66
|
+
salt, expired_ts = message[0..7].unpack("V*")
|
67
|
+
privileges_size = message[8..9].unpack("v")[0]
|
68
|
+
message[10..-1].unpack("vV"*privileges_size)
|
69
|
+
true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: agora_dynamic_key
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- matrixbirds
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-06-08 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A Simple Agora Dynamic Key Implementation
|
14
|
+
email: sales@agora.io
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/dynamic_key.rb
|
20
|
+
- lib/dynamic_key/access_token.rb
|
21
|
+
- lib/dynamic_key/rtc_token_builder.rb
|
22
|
+
- lib/dynamic_key/rtm_token_builder.rb
|
23
|
+
- lib/dynamic_key/sign.rb
|
24
|
+
homepage: https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/ruby/sample
|
25
|
+
licenses:
|
26
|
+
- MIT
|
27
|
+
metadata:
|
28
|
+
source_code_uri: https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/ruby
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 2.5.2.3
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: Agora Dynamic Key Client
|
49
|
+
test_files: []
|