magic-admin 0.0.0 → 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,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MagicAdmin
4
+
5
+ module Http
6
+
7
+ # Http Request and its methods are accessible
8
+ # on the Magic instance by the http_client.http_request attribute.
9
+ # It provides methods to interact with the http_request.
10
+ class Request
11
+
12
+ class << self
13
+ # Description:
14
+ # Method configure request object and provides request object
15
+ # based on method argument.
16
+ #
17
+ # Arguments:
18
+ # method: http method
19
+ # url: get request url
20
+ # options: a hash contains params and headers for request
21
+ #
22
+ # Returns:
23
+ # A request object.
24
+ def request(method, url, options)
25
+ case method
26
+ when :get, "get" then new.get(url, options)
27
+ when :post, "post" then new.post(url, options)
28
+ else
29
+ raise APIError.new("Request method not supported.", { http_method: method })
30
+ end
31
+ end
32
+ end
33
+
34
+ # Description:
35
+ # Method configure request object and provides you get request object.
36
+ #
37
+ # Arguments:
38
+ # url: get request url
39
+ # options: a hash contains params and headers for request
40
+ #
41
+ # Returns:
42
+ # A get request object.
43
+ def get(url, options)
44
+ headers = options[:headers] || {}
45
+ params = options[:params] || {}
46
+ url = url_with_params(url, params)
47
+ req = Net::HTTP::Get.new(url)
48
+ request_with_headers(req, headers)
49
+ end
50
+
51
+ # Description:
52
+ # Method configure request object and provides you post request object.
53
+ #
54
+ # Arguments:
55
+ # url: post request url
56
+ # options: a hash contains params and headers for request
57
+ #
58
+ # Returns:
59
+ # A post request object.
60
+ def post(url, options)
61
+ headers = options[:headers] || {}
62
+ params = options[:params] || {}
63
+ req = Net::HTTP::Post.new(url)
64
+ req = request_with_headers(req, headers)
65
+ request_with_params(req, params)
66
+ end
67
+
68
+ private
69
+
70
+ def request_with_headers(req, headers)
71
+ headers.each do |key, val|
72
+ req[key.to_s] = val
73
+ end
74
+ req
75
+ end
76
+
77
+ def url_with_params(url, params)
78
+ url.query = URI.encode_www_form(params)
79
+ url
80
+ end
81
+
82
+ def request_with_params(req, params)
83
+ req.body = params.to_json
84
+ req
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MagicAdmin
4
+
5
+ module Http
6
+
7
+ # Http Request and its methods are accessible
8
+ # on the Magic instance by the http_client.http_request attribute.
9
+ # It provides methods to interact with the http_request.
10
+ class Response
11
+
12
+ # attribute reader for response data
13
+ attr_reader :data
14
+
15
+ # attribute reader for response body
16
+ attr_reader :content
17
+
18
+ # attribute reader for response status_code
19
+ attr_reader :status_code
20
+
21
+ # Description:
22
+ # Method parse Magic API response
23
+ #
24
+ # Arguments:
25
+ # http_resp: Magic API response.
26
+ # request: request object.
27
+ #
28
+ # Returns:
29
+ # A HTTP Response object or raise an error
30
+ def self.from_net_http(http_resp, request)
31
+ resp = Response.new(http_resp)
32
+ error = case http_resp
33
+ when Net::HTTPUnauthorized then AuthenticationError
34
+ when Net::HTTPBadRequest then BadRequestError
35
+ when Net::HTTPForbidden then ForbiddenError
36
+ when Net::HTTPTooManyRequests then RateLimitingError
37
+ when Net::HTTPServerError then APIError
38
+ when Net::HTTPGatewayTimeout then APIError
39
+ when Net::HTTPServiceUnavailable then APIError
40
+ when Net::HTTPBadGateway then APIError
41
+ end
42
+ return resp unless error
43
+
44
+ raise error.new(resp.data[:message], resp.error_opt(request))
45
+ end
46
+
47
+ # The constructor allows you to create HTTP Response Object
48
+ # when your application interacting with the Magic API
49
+ #
50
+ # Arguments:
51
+ # http_resp: Magic API response.
52
+ #
53
+ # Returns:
54
+ # A HTTP Response object that provides access to
55
+ # all the supported resources.
56
+ #
57
+ # Examples:
58
+ # Response.new(<http_resp>)
59
+ def initialize(http_resp)
60
+ @content = http_resp.body
61
+ @data = JSON.parse(http_resp.body, symbolize_names: true)
62
+ @status_code = http_resp.code.to_i
63
+ end
64
+
65
+ # Description:
66
+ # Method provides you error info hash
67
+ #
68
+ # Arguments:
69
+ # request: request object.
70
+ #
71
+ # Returns:
72
+ # hash with following keys.
73
+ # http_status:
74
+ # status_code:
75
+ # http_response:
76
+ # http_message:
77
+ # http_error_code:
78
+ # http_request_params:
79
+ # http_request_header:
80
+ # http_method:
81
+ def error_opt(request)
82
+ {
83
+ http_status: data[:status],
84
+ http_code: status_code,
85
+ http_response: content,
86
+ http_message: data[:message],
87
+ http_error_code: data[:error_code],
88
+ http_request_params: request.body,
89
+ http_method: request.method
90
+ }
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MagicAdmin
4
+
5
+ module Resource
6
+
7
+ # The token resource and its methods are accessible
8
+ # on the Magic instance by the Token attribute.
9
+ # It provides methods to interact with the DID Token.
10
+ class Token
11
+
12
+ # Description:
13
+ # Method validate did_token
14
+ #
15
+ # Arguments:
16
+ # did_token: A DID Token generated by a Magic user on the client-side.
17
+ #
18
+ # Returns:
19
+ # true or raise an error
20
+ def validate(did_token)
21
+ time = Time.now.to_i
22
+ proof, claim = decode(did_token)
23
+ rec_address = rec_pub_address(claim, proof)
24
+
25
+ validate_public_address!(rec_address, did_token)
26
+ validate_claim_fields!(claim)
27
+ validate_claim_ext!(time, claim["ext"])
28
+ validate_claim_nbf!(time, claim["nbf"])
29
+ end
30
+
31
+ # Description:
32
+ # Method Decodes a DID Token from a Base64 string into
33
+ # a tuple of its individual components: proof and claim.
34
+ # This method allows you decode the DID Token
35
+ # and inspect the token
36
+ #
37
+ # Arguments:
38
+ # did_token: A DID Token generated by a Magic user on the client-side.
39
+ #
40
+ # Returns:
41
+ # An array containing proof and claim or raise an error
42
+ def decode(did_token)
43
+ proof = nil
44
+ claim = nil
45
+ begin
46
+ token_array = JSON.parse(base64_decode(did_token))
47
+ proof = token_array[0]
48
+ claim = JSON.parse(token_array[1])
49
+ validate_claim_fields!(claim)
50
+ rescue JSON::ParserError, ArgumentError
51
+ raise DIDTokenError, "DID Token is malformed"
52
+ end
53
+ [proof, claim]
54
+ end
55
+
56
+ # Description:
57
+ # Method parse public_address and extract issuer
58
+ #
59
+ # Arguments:
60
+ # public_address: Cryptographic public address of the Magic User.
61
+ #
62
+ # Returns:
63
+ # issuer info
64
+ def construct_issuer_with_public_address(public_address)
65
+ "did:ethr:#{public_address}"
66
+ end
67
+
68
+ # Description:
69
+ # Method parse did_token and extract issuer
70
+ #
71
+ # Arguments:
72
+ # did_token: A DID Token generated by a Magic user on the client-side.
73
+ #
74
+ # Returns:
75
+ # issuer info
76
+ def get_issuer(did_token)
77
+ decode(did_token).last["iss"]
78
+ end
79
+
80
+ # Description:
81
+ # Method parse did_token and extract cryptographic public_address
82
+ #
83
+ # Arguments:
84
+ # did_token: A DID Token generated by a Magic user on the client-side.
85
+ #
86
+ # Returns:
87
+ # cryptographic public address of the Magic User
88
+ # who generated the supplied DID Token.
89
+ def get_public_address(did_token)
90
+ get_issuer(did_token).split(":").last
91
+ end
92
+
93
+ private
94
+
95
+ def base64_decode(did_token)
96
+ Base64.urlsafe_decode64(did_token)
97
+ end
98
+
99
+ def personal_recover(claim, proof)
100
+ Eth::Key.personal_recover(JSON.dump(claim), proof)
101
+ end
102
+
103
+ def rec_pub_address(claim, proof)
104
+ Eth::Utils.public_key_to_address personal_recover(claim, proof)
105
+ end
106
+
107
+ def claim_fields
108
+ %w[iat ext iss sub aud nbf tid]
109
+ end
110
+
111
+ def validate_claim_fields!(claim)
112
+ missing_fields = claim_fields - claim.keys
113
+ return true unless missing_fields.any?
114
+
115
+ message = "DID Token missing required fields: %s"
116
+ raise DIDTokenError, message % missing_fields.join(", ")
117
+ end
118
+
119
+ def validate_public_address!(rec_address, did_token)
120
+ return true if rec_address.eql? get_public_address(did_token)
121
+
122
+ message = "Signature mismatch between 'proof' and 'claim'."
123
+ raise DIDTokenError, message
124
+ end
125
+
126
+ def validate_claim_ext!(time, claim_ext)
127
+ return true unless time > claim_ext
128
+
129
+ message = "Given DID token has expired. Please generate a new one."
130
+ raise DIDTokenError, message
131
+ end
132
+
133
+ def apply_nbf_grace_period(claim_nbf)
134
+ claim_nbf - MagicAdmin::Config.nbf_grace_period
135
+ end
136
+
137
+ def validate_claim_nbf!(time, claim_nbf)
138
+ return true unless time < apply_nbf_grace_period(claim_nbf)
139
+
140
+ message = "Given DID token cannot be used at this time."
141
+ raise DIDTokenError, message
142
+ end
143
+
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MagicAdmin
4
+
5
+ module Resource
6
+
7
+ # The user resource and its methods are accessible
8
+ # on the Magic instance by the User attribute.
9
+ # It provides methods to interact with the User.
10
+ class User
11
+
12
+ # attribute reader for magic client object
13
+ attr_reader :magic
14
+
15
+ # The constructor allows you to create user object
16
+ # when your application interacting with the Magic API
17
+ #
18
+ # Arguments:
19
+ # magic: A Magic object.
20
+ #
21
+ # Returns:
22
+ # A user object that provides access to all the supported resources.
23
+ #
24
+ # Examples:
25
+ # User.new(<magic>)
26
+ def initialize(magic)
27
+ @magic = magic
28
+ end
29
+
30
+ # Description:
31
+ # Method Retrieves information about the user by
32
+ # the supplied issuer
33
+ #
34
+ # Arguments:
35
+ # issuer: Extracted iss component of DID Token generated by a Magic user
36
+ # on the client-side.
37
+ #
38
+ # Returns:
39
+ # Metadata information about the user
40
+ def get_metadata_by_issuer(issuer)
41
+ headers = MagicAdmin::Util.headers(magic.secret_key)
42
+ options = { params: { issuer: issuer }, headers: headers }
43
+ magic.http_client
44
+ .call(:get, "/v1/admin/auth/user/get", options)
45
+ end
46
+
47
+ # Description:
48
+ # Method Retrieves information about the user by
49
+ # the supplied public_address
50
+ #
51
+ # Arguments:
52
+ # public_address: Extracted The user's Ethereum public address component
53
+ # of DID Token generated by a Magic user on the client-side.
54
+ #
55
+ # Returns:
56
+ # Metadata information about the user
57
+ def get_metadata_by_public_address(public_address)
58
+ issuer = token.construct_issuer_with_public_address(public_address)
59
+ get_metadata_by_issuer(issuer)
60
+ end
61
+
62
+ # Description:
63
+ # Method Retrieves information about the user by
64
+ # the supplied DID Token
65
+ #
66
+ # Arguments:
67
+ # did_token: A DID Token generated by a Magic user on the client-side.
68
+ #
69
+ # Returns:
70
+ # Metadata information about the user
71
+ def get_metadata_by_token(did_token)
72
+ issuer = token.get_issuer(did_token)
73
+ get_metadata_by_issuer(issuer)
74
+ end
75
+
76
+ # Description:
77
+ # Method logs a user out of all Magic SDK sessions by
78
+ # the supplied issuer
79
+ #
80
+ # Arguments:
81
+ # issuer: Extracted iss component of DID Token generated by a Magic user
82
+ # on the client-side.
83
+ #
84
+ # Returns:
85
+ # Magic Response
86
+ def logout_by_issuer(issuer)
87
+ headers = MagicAdmin::Util.headers(magic.secret_key)
88
+ options = { params: { issuer: issuer }, headers: headers }
89
+ magic.http_client
90
+ .call(:post, "/v2/admin/auth/user/logout", options)
91
+ end
92
+
93
+ # Description:
94
+ # Method logs a user out of all Magic SDK sessions by
95
+ # the supplied public_address
96
+ #
97
+ # Arguments:
98
+ # public_address: Extracted the user's Ethereum public address component
99
+ # of DID Token generated by a Magic user on the client-side.
100
+ #
101
+ # Returns:
102
+ # Magic Response
103
+ def logout_by_public_address(public_address)
104
+ issuer = token.construct_issuer_with_public_address(public_address)
105
+ logout_by_issuer(issuer)
106
+ end
107
+
108
+ # Description:
109
+ # Method logs a user out of all Magic SDK sessions by
110
+ # the supplied DID Token
111
+ #
112
+ # Arguments:
113
+ # did_token: A DID Token generated by a Magic user on the client-side.
114
+ #
115
+ # Returns:
116
+ # Magic Response
117
+ def logout_by_token(did_token)
118
+ issuer = token.get_issuer(did_token)
119
+ logout_by_issuer(issuer)
120
+ end
121
+
122
+ private
123
+
124
+ def token
125
+ magic.token
126
+ end
127
+
128
+ end
129
+ end
130
+ end