Completed
Push — gem ( 68d4ba...3cb05e )
by Joska
01:13
created

Engine.unauth_request()   A

Complexity

Conditions 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 5
rs 9.4286
1
require 'midb/server_controller'
2
require 'midb/server_model'
3
require 'midb/server_view'
4
require 'midb/errors_view'
5
require 'midb/security_controller'
6
require 'midb/hooks'
7
8
require 'yaml'
9
require 'socket'
10
require 'uri'
11
require 'json'
12
require 'sqlite3'
13
14
module MIDB
15
  module API
16
    # @author unrar
17
    # This class handles runs the server engine using sockets and a loop.
18
    class Engine
19
      # Attribute declaration here
20
      # @!attribute config
21
      #   @return [Hash] Contains the project's configuration, saved in .midb.yaml
22
      # @!attribute db
23
      #   @return [String] Database name (if SQLite is the engine, file name without extension)
24
      # @!attribute http_status
25
      #   @return [String] HTTP status code and string representation for the header
26
      # @!attribute h
27
      #   @return [Object] MIDB::API::Hooks instance
28
      attr_accessor :config, :db, :http_status, :hooks
29
30
      # Handle an unauthorized request
31
      def unauth_request
32
        @http_status = "401 Unauthorized"
33
        MIDB::Interface::Server.info(:no_auth)
34
        MIDB::Interface::Server.json_error(401, "Unauthorized").to_json
35
      end
36
37
      # Constructor
38
      #
39
      # @param db   [String] Database to which the server will bind.
40
      # @param stat [Fixnum] HTTP Status
41
      # @param cnf  [Hash] Config from the server controller.
42
      def initialize(db, stat, cnf, hooks=nil)
43
        @config = cnf
44
        @db = db
45
        @http_status = stat
46
        if hooks == nil
47
          @hooks = MIDB::API::Hooks.new
48
        else
49
          @hooks = hooks
50
        end
51
      end
52
53
      # Starts the server on a given port (default: 8081)
54
      #
55
      # @param port [Fixnum] Port to which the server will listen.
56
      def start(port=8081)
57
        serv = TCPServer.new("localhost", port)
58
        MIDB::Interface::Server.info(:start, port)
59
60
        # Manage the requests
61
        loop do
62
          socket = serv.accept
63
          MIDB::Interface::Server.info(:incoming_request, socket.addr[3])
64
65
          request = self.parse_request(socket.gets)
66
67
          # Get a hash with the headers
68
          headers = {}
69
          while line = socket.gets.split(' ', 2)
0 ignored issues
show
Bug introduced by
You have used an assignment in a condition. Did you perhaps intend to use == instead of =?
Loading history...
70
            break if line[0] == "" 
71
            headers[line[0].chop] = line[1].strip
72
          end
73
          data = socket.read(headers["Content-Length"].to_i)
74
75
76
          MIDB::Interface::Server.info(:request, request)
77
          response_json = Hash.new()
78
79
          # Endpoint syntax: ["", FILE, ID, (ACTION)]
80
          endpoint = request[1].split("/")
81
          ep_file = endpoint[1].split("?")[0]
82
83
          method = request[0]
84
          endpoints = [] # Valid endpoints
85
86
          # Load the JSON served files
87
          @config["serves"].each do |js|
88
            # The filename is a valid endpoint
89
            endpoints.push File.basename(js, ".*")
90
          end
91
92
          # Load the endpoints
93
          found = false
94
          endpoints.each do |ep|
95
            if ep_file == ep
96
              found = true
97
              MIDB::Interface::Server.info(:match_json, ep)
98
              # Create the model
99
              dbop = MIDB::API::Model.new(ep, @db, self)
100
              # Analyze the request and pass it to the model
101
              # Is the method accepted?
102
              accepted_methods = ["GET", "POST", "PUT", "DELETE"]
103
              unless accepted_methods.include? method
104
                @http_status = "405 Method Not Allowed"
105
                response_json = MIDB::Interface::Server.json_error(405, "Method Not Allowed").to_json
106
              else
107
                # Do we need authentication?
108
                auth_req = false
109
                unauthenticated = false
110
                if @config["privacy#{method.downcase}"] == true
111
                  MIDB::Interface::Server.info(:auth_required)
112
                  auth_req = true
113
                  # If it's a GET request and we have a different key for GET methods...
114
                  if method == "GET"
115
                    data = ep_file
116
                  end
117
                  if (@config["apigetkey"] != nil) && (method == "GET")
118
                    unauthenticated = (not headers.has_key? "Authentication") ||
119
                     (not MIDB::API::Security.check?(headers["Authentication"], data, @config["apigetkey"]))
120
                  else
121
                    unauthenticated = (not headers.has_key? "Authentication") ||
122
                       (not MIDB::API::Security.check?(headers["Authentication"], data, @config["apikey"]))
123
                  end
124
                end
125
                # Proceed to handle the request
126
                if unauthenticated
127
                  response_json = self.unauth_request
128
                else
129
                  MIDB::Interface::Server.info(:auth_success) if (not unauthenticated) && auth_req
130
                  if method == "GET"
131
                    case endpoint.length
132
                    when 2
133
                      # No ID has been specified. Return all the entries
134
                      # Pass it to the model and get the JSON
135
                      response_json = dbop.get_all_entries().to_json
136
                    when 3
137
                      # An ID has been specified. Should it exist, return all of its entries.
138
                      response_json = dbop.get_entries(endpoint[2]).to_json
139
                    end
140
                  elsif method == "POST"
141
                    response_json = dbop.post(data).to_json
142
                  else
143
                    if endpoint.length >= 3
144
                      if method == "DELETE"
145
                        response_json = dbop.delete(endpoint[2]).to_json 
146
                      elsif method == "PUT"
147
                        response_json = dbop.put(endpoint[2], data).to_json
148
                      end
149
                    else
150
                      @http_status = "404 Not Found"
151
                      response_json = MIDB::Interface::Server.json_error(404, "Must specify an ID.").to_json
152
                    end
153
                  end
154
                end
155
              end 
156
              MIDB::Interface::Server.info(:response, response_json)
157
              # Return the results via HTTP
158
              socket.print "HTTP/1.1 #{@http_status}\r\n" +
159
                          "Content-Type: text/json\r\n" +
160
                          "Content-Length: #{response_json.size}\r\n" +
161
                          "Connection: close\r\n"
162
              socket.print "\r\n"
163
              socket.print response_json
164
              socket.print "\r\n"
165
              MIDB::Interface::Server.info(:success)
166
            end
167
          end
168
          unless found
169
            MIDB::Interface::Server.info(:not_found)
170
            response = MIDB::Interface::Server.json_error(404, "Invalid API endpoint.").to_json
171
172
            socket.print "HTTP/1.1 404 Not Found\r\n" +
173
                         "Content-Type: text/json\r\n" +
174
                         "Content-Length: #{response.size}\r\n" +
175
                         "Connection: close\r\n"
176
            socket.print "\r\n"
177
            socket.print response
178
          end
179
        end
180
      end
181
182
      # Method: parse_request
183
      # Parses an HTTP requests and returns an array [method, uri]
184
      def parse_request(req)
185
        [req.split(" ")[0], req.split(" ")[1]]
186
      end
187
    end
188
  end
189
end