An unofficial, mostly Bitwarden-compatible API server written in Ruby (Sinatra and ActiveRecord)
1#
2# Copyright (c) 2017 joshua stein <jcs@jcs.org>
3#
4# Permission to use, copy, modify, and distribute this software for any
5# purpose with or without fee is hereby granted, provided that the above
6# copyright notice and this permission notice appear in all copies.
7#
8# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15#
16
17require 'sinatra/activerecord'
18require 'sinatra/namespace'
19
20require_relative 'helpers/request_helpers'
21require_relative 'helpers/attachment_helpers'
22
23require_relative 'routes/api'
24require_relative 'routes/icons'
25require_relative 'routes/identity'
26require_relative 'routes/attachments'
27
28module Rubywarden
29 class App < Sinatra::Base
30 register Sinatra::Namespace
31 register Sinatra::ActiveRecordExtension
32
33 set :root, File.expand_path("..", File.dirname(__FILE__))
34
35 configure do
36 enable :logging
37 end
38
39 helpers Rubywarden::RequestHelpers
40 helpers Rubywarden::AttachmentHelpers
41
42 before do
43 if request.content_type.to_s.match(/\Aapplication\/json(;|\z)/)
44 js = request.body.read.to_s
45 if !js.strip.blank?
46 params.merge!(JSON.parse(js))
47 end
48 end
49
50 # some bitwarden apps send params with uppercased first letter, some all
51 # lowercase. just standardize on all lowercase.
52 params.keys.each do |k|
53 params[k.downcase.to_sym] = params.delete(k)
54 end
55
56 # we're always going to reply with json
57 content_type :json
58
59 # set CORS headers for safari extension
60 response.headers["Access-Control-Allow-Origin"] = "file://"
61 # just parrot back whatever safari asked for
62 if request.env["HTTP_ACCESS_CONTROL_REQUEST_METHOD"]
63 response.headers["Access-Control-Allow-Methods"] =
64 request.env["HTTP_ACCESS_CONTROL_REQUEST_METHOD"]
65 end
66 if request.env["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"]
67 response.headers["Access-Control-Allow-Headers"] =
68 request.env["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"]
69 end
70 end
71
72 register Rubywarden::Routing::Api
73 register Rubywarden::Routing::Icons
74 register Rubywarden::Routing::Identity
75 register Rubywarden::Routing::Attachments
76
77 options /.*/ do
78 # empty response just to respond with CORS headers
79 end
80 end
81end