mobius-client 0.1.0

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