redfish_tools 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d591794d6dd81a861a334116e4ddc69d0e583f5f
4
- data.tar.gz: b801a69d947f93e521bc9f910727ad03485e5e07
3
+ metadata.gz: 62d5e62a684fe41e5f66474396da984161869dc0
4
+ data.tar.gz: 401e723d7f305dd2576e45352e19fe9b5da901e7
5
5
  SHA512:
6
- metadata.gz: 4c862e78f28fe165dda817dd3ce34a9501ec4ffd0cd511821f8a63dab0658e0e34b60b6cfd69b1367ccf10e7a28a68ea2dbd63147a52bb26105fe8376bc047fd
7
- data.tar.gz: 8beb0d8f83e83dbf03f00dade9c78a16d778efd94415f1cafef8cf3dd71405c98ad558c9d93944b9eb7a89db76008c16d8ba568c0238547165c7a44552617d79
6
+ metadata.gz: 26f1eb827d58a02555259bb9b618ac0444c5483599ffd5b2477e57fe15f4e10559431b3d041a21b2f1684370df60daf0fd73e50135f5f8136528165079fa9663
7
+ data.tar.gz: 7ec1d90be137f6006b9cf18dd75e622e03d5efccda563ef682200d3954a5a60f43acb8a1ef70f5b65e508e2cd6015d9079d1e18a1aedd988d91e9dc1ee603994
data/.travis.yml CHANGED
@@ -1,5 +1,14 @@
1
1
  sudo: false
2
2
  language: ruby
3
+ cache: bundler
3
4
  rvm:
4
- - 2.4.3
5
- before_install: gem install bundler -v 1.16.1
5
+ - 2.4.3
6
+ script: true
7
+ deploy:
8
+ provider: rubygems
9
+ api_key:
10
+ secure: r3WiTmAZTHbqi30saYw0Iv2YEUZNAv4dp2RPzDoc7K9l1yV+Pt+fol/NCOg8gVBXPn5WHHrXUpw4jz158CneOPlmp7opdzW8SZymUlphXBl7kcI9qEsDJnqZOAJkj1YVwMvjMwRqE1UjWs43Ok93wVQlt2M9FIztLBXHhKtm+M0T6GpeFisAntgeIYiRl0/XTYr36SG23dTcWogkKEHahX24/W1ap5Z2O+p0wZNyRbhFXMMkvMVqwS6EFn9CR1GuJ2f93WYzsIZWm4LhjDZisAxh4oHtcGfXyOSHTU4d4fOnoBd0H7Ejyf/4mr407gnfSqpuXJUIiTsNPDrlMTGT0l/sSJYPWY6P1nygOTlCd7oLG+E3nOUKD5L0KPdzLkAxqylrNbCFx9dPMfyuT6wUin4XW/NulwCAy7qLrMFn0MuugxZINMJE09dWCMd5B+guMPtmpj7eVerrDi26H7hEO7Mdvg1rmhEfUVLFbiiAOzi3SvHhFYZC7Rr1WFt+8zUxgAUxwuAfAyJX4Zolkj1xcQF1Qssj4g8wAtaQ1ZWhdWWoJjEziJcqZPOIckeB+uuvauXVSd1a8ASsHLBCrnZUYKp6ZOn7a3t5zaoUjUTl4BuPZFfZ0gAqP3YMEmGboeDPFRM4uNk7l5eA5qc0s0HT2wgjYXeL+M2ZmHk5ACInWAw=
11
+ gem: redfish_tools
12
+ on:
13
+ tags: true
14
+ repo: xlab-si/redfish_tools
data/README.md CHANGED
@@ -1,16 +1,21 @@
1
- # RedfishTools
1
+ # Redfish tools
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/redfish_tools`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ [![Build Status](https://travis-ci.com/xlab-si/redfish_tools.svg?branch=master)](https://travis-ci.com/xlab-si/redfish_tools)
4
+
5
+
6
+ This repository contains source code for redfish_tools gem that contains tools
7
+ for testing application that know how to work with Redfish API.
8
+
9
+ The only tool that is currently available is mock server, but in the near
10
+ future, we will also add a mock creator tool and interactive inspector for
11
+ Redfish services.
4
12
 
5
- TODO: Delete this and the text above, and describe your gem
6
13
 
7
14
  ## Installation
8
15
 
9
16
  Add this line to your application's Gemfile:
10
17
 
11
- ```ruby
12
- gem 'redfish_tools'
13
- ```
18
+ gem "redfish_tools"
14
19
 
15
20
  And then execute:
16
21
 
@@ -20,16 +25,38 @@ Or install it yourself as:
20
25
 
21
26
  $ gem install redfish_tools
22
27
 
28
+
23
29
  ## Usage
24
30
 
25
- TODO: Write usage instructions here
31
+ The simplest way to start using Redfish tools is to simply run the `redfish`
32
+ command and read the provided help. At the moment, the output should look like
33
+ this:
34
+
35
+ $ redfish
36
+ Commands:
37
+ redfish help [COMMAND] # Describe available commands
38
+ redfish serve [OPTIONS] PATH # serve mock from PATH
39
+
40
+ To start serving existing Redfish recording, we run the `serve` command:
41
+
42
+ $ redfish serve --ssl --user test --pass demo path/to/recording
43
+
44
+ To get the description of all available options, use `help` command or add
45
+ `-h` flag anywhere in the command.
46
+
26
47
 
27
48
  ## Development
28
49
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
50
+ After checking out the repo, run `bin/setup` to install dependencies.
51
+ Unfortunately, this gem contains no tests at the moment, so if you feel like
52
+ contributing, this would be a great place to start.
53
+
54
+ To create new release, increment the version number, commit the change, tag
55
+ the commit and push tag to the GitHub. Travis CI will pick from there on and
56
+ create new release, publishing it on https://rubygems.org.
30
57
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
58
 
33
59
  ## Contributing
34
60
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/redfish_tools.
61
+ Bug reports and pull requests are welcome on GitHub at
62
+ https://github.com/xlab-si/redfish_tools.
@@ -14,8 +14,12 @@ module RedfishTools
14
14
  def run
15
15
  datastore = RedfishTools::DataStore.new(@path)
16
16
  server = RedfishTools::Server.new(datastore,
17
+ @options[:user],
18
+ @options[:pass],
17
19
  Port: @options[:port],
18
- BindAddress: @options[:bind])
20
+ BindAddress: @options[:bind],
21
+ SSLEnable: @options[:ssl],
22
+ SSLCertName: [%w[CN localhost]])
19
23
  trap("INT") { server.shutdown }
20
24
  server.start
21
25
  end
@@ -11,6 +11,10 @@ module RedfishTools
11
11
  super(args)
12
12
  end
13
13
 
14
+ def self.exit_on_failure?
15
+ true
16
+ end
17
+
14
18
  desc "serve [OPTIONS] PATH", "serve mock from PATH"
15
19
  option :port,
16
20
  desc: "port that should be used to serve the mock",
@@ -19,18 +23,24 @@ module RedfishTools
19
23
  option :bind,
20
24
  desc: "address that server should bind to",
21
25
  default: "127.0.0.1"
26
+ option :ssl,
27
+ desc: "use SSL",
28
+ default: false,
29
+ type: :boolean
30
+ option :user,
31
+ desc: "username to use"
32
+ option :pass,
33
+ desc: "password to use"
22
34
  def serve(path)
35
+ user = options[:user]
36
+ pass = options[:pass]
37
+ raise Thor::Error, "Missing password" if user && pass.nil?
38
+ raise Thor::Error, "Missing username" if user.nil? && pass
39
+
23
40
  require "redfish_tools/cli/serve"
24
41
  Serve.new(path, options).run
25
- end
26
-
27
- desc "query [OPTIONS] URL QUERY", "query service at URL using QUERY"
28
- def query(url, *query
29
-
30
- desc "record [OPTIONS] URL PATH", "record service at URL"
31
- def record(url, path)
32
- require "redfish_tools/cli/record"
33
- Record.new(url, path, options).run
42
+ rescue StandardError => e
43
+ raise Thor::Error, e.to_s
34
44
  end
35
45
  end
36
46
  end
@@ -7,14 +7,16 @@ module RedfishTools
7
7
  Resource = Struct.new(:id, :body, :headers, :time)
8
8
 
9
9
  def initialize(base_path)
10
- # TODO(tadeboro): check for folder and determine mode of operation
11
10
  @base_path = File.expand_path(base_path)
12
11
  @overlay = {}
12
+
13
+ root_file = File.join(@base_path, "redfish", "v1", "index.json")
14
+ raise "Invalid recording folder" unless File.file?(root_file)
13
15
  end
14
16
 
15
17
  def get(id)
16
18
  id = id.chomp("/")
17
- @overlay[id] || load_resource(id)
19
+ @overlay[id] ||= load_resource(id)
18
20
  end
19
21
 
20
22
  def set(id, body, headers: nil, time: nil)
@@ -28,24 +30,21 @@ module RedfishTools
28
30
  end
29
31
 
30
32
  def load_body(id)
31
- load(File.join(@base_path, id, "index.json"))
33
+ load_json(File.join(@base_path, id, "index.json"))
32
34
  end
33
35
 
34
36
  def load_headers(id)
35
- load_json(File.join(@base_path, id, "headers.json"))["GET"]
37
+ headers = load_json(File.join(@base_path, id, "headers.json"))
38
+ headers && headers["GET"]
36
39
  end
37
40
 
38
41
  def load_time(id)
39
- load_json(File.join(@base_path, id, "time.json"))["GET_Time"].to_f
40
- end
41
-
42
- def load(path)
43
- File.readable?(path) ? File.read(path) : nil
42
+ times = load_json(File.join(@base_path, id, "time.json"))
43
+ times && times["GET_Time"]&.to_f
44
44
  end
45
45
 
46
46
  def load_json(path)
47
- content = load(path)
48
- content ? JSON.parse(content) : {}
47
+ File.readable?(path) ? JSON.parse(File.read(path)) : nil
49
48
  end
50
49
  end
51
50
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "base64"
3
4
  require "openssl"
4
5
  require "webrick"
5
6
  require "webrick/https"
@@ -8,12 +9,23 @@ require "redfish_tools/servlet"
8
9
 
9
10
  module RedfishTools
10
11
  class Server < WEBrick::HTTPServer
11
- def initialize(datastore, config = {})
12
+ def initialize(datastore, username, password, config = {})
12
13
  super(config)
13
14
 
14
- root = JSON.parse(datastore.get("/redfish/v1").body)
15
- login_path = root.dig("Links", "Sessions", "@odata.id")
16
- mount("/", Servlet, datastore, login_path)
15
+ @datastore = datastore
16
+ root = datastore.get("/redfish/v1").body
17
+ @login_path = root.dig("Links", "Sessions", "@odata.id")&.chomp("/")
18
+ @username = username
19
+ @password = password
20
+
21
+ mount("/", Servlet)
22
+ end
23
+
24
+ attr_reader :datastore, :login_path, :username, :password
25
+
26
+ def basic_auth_header
27
+ @basic_auth_header ||= "Basic " +
28
+ Base64.strict_encode64("#{username}:#{password}")
17
29
  end
18
30
  end
19
31
  end
@@ -1,38 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
4
+ require "json"
5
+ require "securerandom"
3
6
  require "set"
4
7
  require "webrick"
5
8
 
6
9
  module RedfishTools
7
10
  class Servlet < WEBrick::HTTPServlet::AbstractServlet
11
+ extend Forwardable
12
+
13
+ def_delegators :@server,
14
+ :datastore, :login_path, :username, :password,
15
+ :basic_auth_header
16
+
8
17
  BAD_HEADERS = Set.new(["connection", "content-length", "keep-alive"])
9
18
  DEFAULT_HEADERS = {
10
19
  "content-type" => "application/json"
11
20
  }.freeze
12
21
 
13
- def initialize(server, datastore, login_path)
14
- super(server)
15
- @datastore = datastore
16
- @login_path = login_path.chomp("/")
22
+ def service(request, response)
23
+ return response.status = 401 unless authorized?(request)
24
+ return response.status = 404 unless datastore.get(request.path).body
25
+
26
+ super
17
27
  end
18
28
 
19
29
  def do_GET(request, response)
20
- return response.status = 401 unless authorized?(request)
21
-
22
- resource = @datastore.get(request.path)
23
- response.body = resource.body
24
- set_headers(response, resource.headers)
25
- response.status = response.body ? 200 : 404
30
+ item = datastore.get(request.path)
31
+ response.status = 200
32
+ set_headers(response, item.headers)
33
+ response.body = item.body.to_json
26
34
  end
27
35
 
28
36
  def do_POST(request, response)
29
- return response.status = 401 unless authorized?(request)
37
+ item = datastore.get(request.path)
38
+ return response.status = 405 unless item.body["Members"]
30
39
 
31
- if login_path?(request.path)
32
- login(request, response)
33
- else
34
- response.status = 501
35
- end
40
+ data = JSON.parse(request.body)
41
+ item_n = login_path?(request) ? login(item, data) : new_item(item, data)
42
+ return response.status = 400 unless item_n.body
43
+
44
+ response.status = 201
45
+ set_headers(response, item_n.headers)
46
+ response.body = item_n.body.to_json
47
+ rescue JSON::ParserError
48
+ response.status = 400
36
49
  end
37
50
 
38
51
  def do_PUT(_request, response)
@@ -49,24 +62,65 @@ module RedfishTools
49
62
 
50
63
  private
51
64
 
52
- def login_path?(path)
53
- @login_path == path.chomp("/")
65
+ def login_path?(request)
66
+ login_path == request.path.chomp("/")
54
67
  end
55
68
 
56
- def login(request, response)
57
- credentials = JSON.parse(request.body)
58
- response.body = {
59
- "Name" => "User Session",
60
- "Description" => "User Session",
61
- "UserName" => credentials["UserName"]
62
- }.to_json
63
- set_headers(response, DEFAULT_HEADERS.merge("X-Auth-Token" => "dummy"))
64
- response.status = 201
69
+ def login(item, data)
70
+ user = data["UserName"]
71
+ pass = data["Password"]
72
+ return nil unless username == user && password == pass
73
+
74
+ res = new_item(item,
75
+ "@odata.type" => "#Session.v1_1_0.Session",
76
+ "UserName" => user,
77
+ "Password" => nil)
78
+ res.headers = DEFAULT_HEADERS.merge("X-Auth-Token" => res.body["Id"])
79
+ res
80
+ end
81
+
82
+ def new_item(item, data)
83
+ id = SecureRandom.uuid
84
+ oid = item.id.chomp("/") + "/" + id
85
+ item.body["Members@odata.count"] += 1
86
+ item.body["Members"].push("@odata.id" => oid)
87
+
88
+ base = { "@odata.id" => oid, "Id" => id, "Name" => id }
89
+ datastore.set(id, base.merge(data))
90
+ end
91
+
92
+ def authorized?(request)
93
+ username.nil? || # Server has no credentials set
94
+ always_allow?(request.path) || # Non-protected endpoints
95
+ authorized_basic?(request) ||
96
+ authorized_session?(request) ||
97
+ (request.request_method == "POST" && login_path?(request))
98
+ end
99
+
100
+ def always_allow?(path)
101
+ [
102
+ "/redfish",
103
+ "/redfish/v1",
104
+ "/redfish/v1/$metadata",
105
+ "/redfish/v1/odata"
106
+ ].include?(path.chomp("/"))
107
+ end
108
+
109
+ def authorized_basic?(request)
110
+ request["authorization"] == basic_auth_header
111
+ end
112
+
113
+ def authorized_session?(request)
114
+ return false if login_path.nil? || request["X-Auth-Token"].nil?
115
+ remove_stale_sessions
116
+ datastore.get(login_path).body["Members"].any? do |session|
117
+ session["@odata.id"].index(request["X-Auth-Token"])
118
+ end
65
119
  end
66
120
 
67
- def authorized?(_request)
68
- # TODO(tadeboro): Add checks as per Redfish standard
69
- true
121
+ def remove_stale_sessions
122
+ # TODO(tadeboro): implement session expiration
123
+ nil
70
124
  end
71
125
 
72
126
  def set_headers(response, headers)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RedfishTools
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redfish_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tadej Borovšak
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-06-12 00:00:00.000000000 Z
11
+ date: 2018-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redfish_client