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.
@@ -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
@@ -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: []