magic-admin 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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