mobius-client 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.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mobius/client"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "sinatra"
4
+ gem "slim"
5
+ gem "mobius-client", path: "../.."
@@ -0,0 +1,106 @@
1
+ PATH
2
+ remote: ../..
3
+ specs:
4
+ mobius-client (0.1.0)
5
+ dry-initializer (~> 2.4)
6
+ faraday (~> 0.14)
7
+ faraday_middleware (~> 0.12, >= 0.12.2)
8
+ jwt (~> 1.5, >= 1.5.6)
9
+ stellar-sdk (~> 0.3)
10
+ thor (~> 0.20)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ activemodel (5.1.6)
16
+ activesupport (= 5.1.6)
17
+ activesupport (5.1.6)
18
+ concurrent-ruby (~> 1.0, >= 1.0.2)
19
+ i18n (>= 0.7, < 2)
20
+ minitest (~> 5.1)
21
+ tzinfo (~> 1.1)
22
+ base32 (0.3.2)
23
+ citrus (3.0.2)
24
+ concurrent-ruby (1.0.5)
25
+ contracts (0.16.0)
26
+ digest-crc (0.4.1)
27
+ dry-initializer (2.4.0)
28
+ excon (0.44.4)
29
+ faraday (0.14.0)
30
+ multipart-post (>= 1.2, < 3)
31
+ faraday-digestauth (0.3.0)
32
+ faraday (~> 0.7)
33
+ net-http-digest_auth (~> 1.4)
34
+ faraday_hal_middleware (0.1.0)
35
+ faraday_middleware (~> 0.9)
36
+ faraday_middleware (0.12.2)
37
+ faraday (>= 0.7.4, < 1.0)
38
+ ffi (1.9.23)
39
+ futuroscope (0.1.11)
40
+ hyperclient (0.7.2)
41
+ faraday
42
+ faraday-digestauth
43
+ faraday_hal_middleware
44
+ faraday_middleware
45
+ futuroscope
46
+ net-http-digest_auth
47
+ uri_template
48
+ i18n (1.0.0)
49
+ concurrent-ruby (~> 1.0)
50
+ jwt (1.5.6)
51
+ minitest (5.11.3)
52
+ multipart-post (2.0.0)
53
+ mustermann (1.0.2)
54
+ net-http-digest_auth (1.4.1)
55
+ rack (2.0.4)
56
+ rack-protection (2.0.1)
57
+ rack
58
+ rbnacl (5.0.0)
59
+ ffi
60
+ rbnacl-libsodium (1.0.16)
61
+ rbnacl (>= 3.0.1)
62
+ sinatra (2.0.1)
63
+ mustermann (~> 1.0)
64
+ rack (~> 2.0)
65
+ rack-protection (= 2.0.1)
66
+ tilt (~> 2.0)
67
+ slim (3.0.9)
68
+ temple (>= 0.7.6, < 0.9)
69
+ tilt (>= 1.3.3, < 2.1)
70
+ stellar-base (0.13.0)
71
+ activesupport (>= 4.2.7)
72
+ base32
73
+ digest-crc
74
+ rbnacl
75
+ rbnacl-libsodium (~> 1.0.3)
76
+ xdr (~> 2.0.0)
77
+ stellar-sdk (0.3.0)
78
+ activesupport (>= 4.2.7)
79
+ contracts (~> 0.7)
80
+ excon (~> 0.44.4)
81
+ hyperclient (~> 0.7.0)
82
+ stellar-base (~> 0.13.0)
83
+ toml-rb (~> 1.1.1)
84
+ temple (0.8.0)
85
+ thor (0.20.0)
86
+ thread_safe (0.3.6)
87
+ tilt (2.0.8)
88
+ toml-rb (1.1.1)
89
+ citrus (~> 3.0, > 3.0)
90
+ tzinfo (1.2.5)
91
+ thread_safe (~> 0.1)
92
+ uri_template (0.7.0)
93
+ xdr (2.0.0)
94
+ activemodel (>= 4.2.7)
95
+ activesupport (>= 4.2.7)
96
+
97
+ PLATFORMS
98
+ ruby
99
+
100
+ DEPENDENCIES
101
+ mobius-client!
102
+ sinatra
103
+ slim
104
+
105
+ BUNDLED WITH
106
+ 1.16.0
@@ -0,0 +1,33 @@
1
+ require "bundler/setup"
2
+ require "sinatra"
3
+ require "mobius/client"
4
+
5
+ keypair = Stellar::KeyPair.random
6
+
7
+ puts "App public: #{keypair.address}"
8
+
9
+ set :public_folder, "public"
10
+
11
+ get "/" do
12
+ slim :index, locals: { app_public_key: keypair.address }
13
+ end
14
+
15
+ # Generates challenge transaction signed with application private key.
16
+ get "/auth" do
17
+ Mobius::Client::Auth::Challenge.call(keypair.seed)
18
+ end
19
+
20
+ # Checks transaction signature.
21
+ post "/auth" do
22
+ begin
23
+ token = Mobius::Client::Auth::Token.new(keypair.seed, params[:xdr], params[:public_key])
24
+ token.validate!
25
+ token.hash(:hex)
26
+ rescue Mobius::Client::Error::Unauthorized
27
+ "Access denied!"
28
+ rescue Mobius::Client::Error::TokenExpired
29
+ "Session expired!"
30
+ rescue Mobius::Client::Error::TokenTooOld
31
+ "Challenge expired!"
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ StellarSdk.Network.useTestNetwork()
2
+
3
+ $(function() {
4
+ var xdr = null
5
+ var signedXdr = null
6
+ var keypair = StellarSdk.Keypair.random();
7
+
8
+ $('#public_key').val(keypair.publicKey());
9
+ $('#secret').val(keypair.secret());
10
+
11
+ $('#challenge').on('click', function() {
12
+ axios.get('/auth').then(function(response) {
13
+ xdr = response.data;
14
+ $('#challenge_xdr').val(xdr);
15
+ });
16
+ });
17
+
18
+ $('#sign').on('click', function() {
19
+ var tx = new StellarSdk.Transaction(xdr);
20
+ tx.sign(keypair);
21
+ signedXdr = tx.toEnvelope().toXDR("base64");
22
+ $('#signed_challenge_xdr').val(signedXdr);
23
+
24
+ axios({
25
+ url: '/auth',
26
+ method: 'post',
27
+ params: {
28
+ xdr: signedXdr,
29
+ public_key: keypair.publicKey()
30
+ }
31
+ }).then(function(response) {
32
+ $('#result').html(response.data)
33
+ });
34
+ })
35
+ })
@@ -0,0 +1,44 @@
1
+ html
2
+ head
3
+ css:
4
+ .field { margin-top: 0.5em; }
5
+ label { width: 500px; display: block; }
6
+ input, textarea { width: 500px; }
7
+ body
8
+ h1 Authorize application
9
+
10
+ .field
11
+ label for="app_public_key"
12
+ | App Public Key
13
+ input type="text" id="app_public_key" value="#{app_public_key}"
14
+
15
+ h2 Stage 1: Request Challenge
16
+ .field
17
+ textarea id="challenge_xdr" disabled="disabled" rows=10
18
+ .field
19
+ input type="button" value="Request challenge" id="challenge"
20
+
21
+ h2 Stage 2: Sign Challenge
22
+ form
23
+ .field
24
+ label for="public_key"
25
+ | Public key:
26
+ input type="text" id="public_key"
27
+
28
+ .field
29
+ label for="secret"
30
+ | Private key:
31
+ input type="text" id="secret"
32
+
33
+ .field
34
+ input type="button" value="Sign and send challenge" id="sign"
35
+
36
+ textarea id="signed_challenge_xdr" disabled="disabled" rows=10
37
+
38
+ h3#result None
39
+
40
+
41
+ script src="https://cdnjs.cloudflare.com/ajax/libs/stellar-sdk/0.8.0/stellar-sdk.js"
42
+ script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
43
+ script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"
44
+ script src="/app.js"
data/exe/mobius-cli ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mobius/client"
5
+ require "thor"
6
+
7
+ Mobius::Cli::App.start(ARGV)
@@ -0,0 +1,15 @@
1
+ require "thor"
2
+
3
+ class Mobius::Cli::App < Mobius::Cli::Base
4
+ def self.exit_on_failure?
5
+ true
6
+ end
7
+
8
+ class_option :production, desc: "Use production network", default: false, aliases: "-p", type: :boolean
9
+
10
+ desc "create", "Create various assets"
11
+ subcommand "create", Mobius::Cli::Create
12
+
13
+ desc "auth", "Authorize and authenticate user"
14
+ subcommand "auth", Mobius::Cli::Auth
15
+ end
@@ -0,0 +1,95 @@
1
+ require "uri"
2
+ require "thor"
3
+
4
+ class Mobius::Cli::Auth < Mobius::Cli::Base
5
+ desc "authorize <User secret> <App public>", "Authorize application to pay from user account"
6
+ def authorize(user_seed, app_public_key)
7
+ use_network
8
+
9
+ say "Adding cosigner..."
10
+ user_keypair = Mobius::Client.to_keypair(user_seed)
11
+ app_keypair = Mobius::Client.to_keypair(app_public_key)
12
+ Mobius::Client::Blockchain::AddCosigner.call(user_keypair, app_keypair)
13
+ say "#{app_keypair.address} is now authorized to withdraw from #{user_keypair.address}"
14
+ end
15
+
16
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
17
+ desc "fetch <URL> <User secret> <App public>", "Obtain auth token from application"
18
+ method_option :jwt, type: :string, aliases: "-j"
19
+ def fetch(url, user_seed, app_public)
20
+ use_network
21
+
22
+ keypair = Mobius::Client.to_keypair(user_seed)
23
+
24
+ say "Requesting challenge..."
25
+
26
+ uri = URI(url)
27
+ conn = http("#{uri.scheme}://#{uri.host}:#{uri.port}")
28
+
29
+ response = conn.get(uri.path)
30
+ validate_response!(response)
31
+ xdr = response.body
32
+
33
+ say "Challenge:"
34
+ say xdr
35
+ say "Requesting token..."
36
+
37
+ signed_xdr = Mobius::Client::Auth::Sign.call(keypair.seed, xdr, app_public)
38
+
39
+ say "Signed challenge:"
40
+ say signed_xdr
41
+
42
+ response = conn.post(uri.path, xdr: signed_xdr, public_key: keypair.address)
43
+ validate_response!(response)
44
+
45
+ token = response.body
46
+
47
+ say "Token (hash):"
48
+ if options[:jwt]
49
+ say Mobius::Client::Auth::Jwt.new(options[:jwt]).encode(token)
50
+ else
51
+ say token
52
+ end
53
+ rescue Mobius::Client::Error::Unauthorized
54
+ say "Application signature wrong! Check application public key.", :red
55
+ end
56
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
57
+
58
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
59
+ desc "token <User secret> <App secret>", "Generate auth token locally"
60
+ method_option :jwt, type: :string, aliases: "-j"
61
+ def token(user_seed, app_seed)
62
+ use_network
63
+
64
+ user_keypair = Mobius::Client.to_keypair(user_seed)
65
+ app_keypair = Mobius::Client.to_keypair(app_seed)
66
+
67
+ xdr = Mobius::Client::Auth::Challenge.call(app_seed)
68
+ signed_xdr = Mobius::Client::Auth::Sign.call(user_seed, xdr, app_keypair.address)
69
+ token = Mobius::Client::Auth::Token.new(app_seed, signed_xdr, user_keypair.address)
70
+
71
+ say "Token:"
72
+ if options[:jwt]
73
+ say Mobius::Client::Auth::Jwt.new(options[:jwt]).encode(token)
74
+ else
75
+ say token.hash(:hex)
76
+ end
77
+ end
78
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
79
+
80
+ no_commands do
81
+ def http(host)
82
+ Faraday.new(host) do |c|
83
+ c.request :url_encoded
84
+ c.response :json, content_type: /\bjson$/
85
+ c.adapter Faraday.default_adapter
86
+ end
87
+ end
88
+
89
+ def validate_response!(response)
90
+ return if response.success?
91
+ say "[ERROR]: #{response.status} #{response.body}", :red
92
+ exit(-1)
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,9 @@
1
+ class Mobius::Cli::Base < Thor
2
+ protected
3
+
4
+ no_commands do
5
+ def use_network
6
+ Mobius::Client.network = :public if options[:production]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,85 @@
1
+ require "thor"
2
+ require "erb"
3
+
4
+ class Mobius::Cli::Create < Mobius::Cli::Base
5
+ desc "dapp-account", "Create DApp Store account funded with MOBI and XLM (test network only)"
6
+ method_option :application, type: :string, aliases: "-a"
7
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
8
+ def dapp_account
9
+ keypair = create_dapp_account
10
+
11
+ say " * Public Key: #{keypair.address}"
12
+ say " * Private Key: #{keypair.seed}"
13
+ say " * MOBI balance: #{Mobius::Client::Blockchain::Account.new(keypair).balance}"
14
+
15
+ if options["application"]
16
+ app_keypair = Mobius::Client.to_keypair(options["application"])
17
+ add_cosigner(keypair, app_keypair)
18
+ end
19
+ say "Done!"
20
+ rescue StandardError => e
21
+ say "[ERROR] #{e.message}", :red
22
+ end
23
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
24
+
25
+ desc "account", "Create regular Stellar account funded with XLM only (test network)"
26
+ def account
27
+ keypair = create_account
28
+
29
+ say " * Public Key: #{keypair.address}"
30
+ say " * Private Key: #{keypair.seed}"
31
+ say " * XLM balance: #{Mobius::Client::Blockchain::Account.new(keypair).balance(:native)}"
32
+ say "Done!"
33
+ rescue StandardError => e
34
+ say "[ERROR] #{e.message}", :red
35
+ end
36
+
37
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
38
+ desc "dev-wallet", "Create wallet-dev.html"
39
+ def dev_wallet
40
+ app_keypair = create_dapp_account(0)
41
+ normal_keypair = create_dapp_account(1000)
42
+ add_cosigner(normal_keypair, app_keypair)
43
+ zero_balance_keypair = create_dapp_account(0)
44
+ unauthorized_keypair = create_account
45
+
46
+ vars = {
47
+ app: app_keypair,
48
+ normal: normal_keypair,
49
+ zero_balance: zero_balance_keypair,
50
+ unauthorized: unauthorized_keypair
51
+ }
52
+
53
+ t = File.read(TEMPLATE)
54
+ r = ERB.new(t).result(OpenStruct.new(vars).instance_eval { binding })
55
+ File.open("dev-wallet.html", "w+") { |f| f.puts r }
56
+
57
+ say "dev-wallet.html created. Copy it to your public web server directory and do not forget to change the URL!"
58
+ rescue StandardError => e
59
+ say "[ERROR] #{e.message}", :red
60
+ end
61
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
62
+
63
+ no_commands do
64
+ def create_dapp_account(amount = 1000)
65
+ say "Calling Mobius FriendBot..."
66
+ Stellar::KeyPair.random.tap do |keypair|
67
+ Mobius::Client::FriendBot.call(keypair.seed, amount)
68
+ end
69
+ end
70
+
71
+ def create_account
72
+ say "Calling Stellar FriendBot..."
73
+ Stellar::KeyPair.random.tap do |keypair|
74
+ Mobius::Client::Blockchain::FriendBot.call(keypair)
75
+ end
76
+ end
77
+
78
+ def add_cosigner(keypair, app_keypair)
79
+ say "Adding cosigner..."
80
+ Mobius::Client::Blockchain::AddCosigner.call(keypair, app_keypair)
81
+ end
82
+ end
83
+
84
+ TEMPLATE = File.join(File.dirname(__FILE__), "../../../template/dev-wallet.html.erb").freeze
85
+ end