An unofficial, mostly Bitwarden-compatible API server written in Ruby (Sinatra and ActiveRecord)
at master 199 lines 6.8 kB view raw
1require_relative "spec_helper.rb" 2 3describe "identity module" do 4 before do 5 User.all.delete_all 6 end 7 8 it "should return successful response to account creation" do 9 post "/api/accounts/register", { 10 :name => nil, 11 :email => "nobody@example.com", 12 :masterPasswordHash => Bitwarden.hashPassword("asdf", 13 "nobody@example.com", User::DEFAULT_KDF_TYPE, 14 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 15 :masterPasswordHint => nil, 16 :key => Bitwarden.makeEncKey( 17 Bitwarden.makeKey("adsf", "nobody@example.com", User::DEFAULT_KDF_TYPE, 18 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 19 ), 20 :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 21 :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 22 } 23 last_response.status.must_equal 200 24 end 25 26 it "should not allow duplicate signups" do 27 2.times do |x| 28 post "/api/accounts/register", { 29 :name => nil, 30 :email => "nobody2@example.com", 31 :masterPasswordHash => Bitwarden.hashPassword("asdf", 32 "nobody2@example.com", User::DEFAULT_KDF_TYPE, 33 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 34 :masterPasswordHint => nil, 35 :key => Bitwarden.makeEncKey( 36 Bitwarden.makeKey("adsf", "nobody2@example.com", 37 User::DEFAULT_KDF_TYPE, 38 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 39 ), 40 :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 41 :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 42 } 43 if x == 0 44 last_response.status.must_equal 200 45 else 46 last_response.status.wont_equal 200 47 end 48 end 49 end 50 51 it "validates required parameters" do 52 okh = { 53 :name => nil, 54 :email => "nobody3@example.com", 55 :masterPasswordHash => Bitwarden.hashPassword("asdf", 56 "nobody3@example.com", User::DEFAULT_KDF_TYPE, 57 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 58 :masterPasswordHint => nil, 59 :key => Bitwarden.makeEncKey( 60 Bitwarden.makeKey("adsf", "nobody3@example.com", 61 User::DEFAULT_KDF_TYPE, 62 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 63 ), 64 :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 65 :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 66 } 67 68 post "/api/accounts/register", okh.merge({ 69 :masterPasswordHash => "", 70 }) 71 last_response.status.wont_equal 200 72 73 post "/api/accounts/register", okh.merge({ 74 :key => "junk", 75 }) 76 last_response.status.wont_equal 200 77 78 post "/api/accounts/register", okh.merge({ 79 :kdf => 100, 80 }) 81 last_response.status.wont_equal 200 82 83 post "/api/accounts/register", okh.merge({ 84 :kdfIterations => 5, 85 }) 86 last_response.status.wont_equal 200 87 88 post "/api/accounts/register", okh 89 last_response.status.must_equal 200 90 end 91 92 it "actually creates the user account and allows logging in" do 93 post "/api/accounts/register", { 94 :name => nil, 95 :email => "nobody4@example.com", 96 :masterPasswordHash => Bitwarden.hashPassword("asdf", 97 "nobody4@example.com", User::DEFAULT_KDF_TYPE, 98 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 99 :masterPasswordHint => nil, 100 :key => Bitwarden.makeEncKey( 101 Bitwarden.makeKey("adsf", "nobody4@example.com", 102 User::DEFAULT_KDF_TYPE, 103 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 104 ), 105 :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 106 :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 107 } 108 last_response.status.must_equal 200 109 110 (u = User.find_by_email("nobody4@example.com")).wont_be_nil 111 u.uuid.wont_be_nil 112 u.password_hash.must_equal "uQOY5dffPoKCueMu3cMXl2KOL52NerIQlwCEpQ6mW6s=" 113 114 post "/api/accounts/prelogin", { 115 :email => "nobody4@example.com", 116 } 117 last_response.status.must_equal 200 118 last_json_response["KdfIterations"].must_equal( 119 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 120 121 post "/identity/connect/token", { 122 :grant_type => "password", 123 :username => "nobody4@example.com", 124 :password => Bitwarden.hashPassword("asdf", "nobody4@example.com", 125 User::DEFAULT_KDF_TYPE, 126 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 127 :scope => "api offline_access", 128 :client_id => "browser", 129 :deviceType => 3, 130 :deviceIdentifier => SecureRandom.uuid, 131 :deviceName => "firefox", 132 :devicePushToken => "" 133 } 134 135 last_response.status.must_equal 200 136 (access_token = last_json_response["access_token"]).wont_be_nil 137 138 get "/api/sync", {}, { 139 "HTTP_AUTHORIZATION" => "Bearer #{access_token}", 140 } 141 last_response.status.must_equal 200 142 end 143 144 it "enforces token validity period" do 145 post "/api/accounts/register", { 146 :name => nil, 147 :email => "nobody5@example.com", 148 :masterPasswordHash => Bitwarden.hashPassword("asdf", 149 "nobody5@example.com", User::DEFAULT_KDF_TYPE, 150 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 151 :masterPasswordHint => nil, 152 :key => Bitwarden.makeEncKey( 153 Bitwarden.makeKey("adsf", "nobody5@example.com", 154 User::DEFAULT_KDF_TYPE, 155 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]) 156 ), 157 :kdf => Bitwarden::KDF::TYPE_IDS[User::DEFAULT_KDF_TYPE], 158 :kdfIterations => Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE], 159 } 160 last_response.status.must_equal 200 161 162 post "/identity/connect/token", { 163 :grant_type => "password", 164 :username => "nobody5@example.com", 165 :password => Bitwarden.hashPassword("asdf", "nobody5@example.com", 166 User::DEFAULT_KDF_TYPE, 167 Bitwarden::KDF::DEFAULT_ITERATIONS[User::DEFAULT_KDF_TYPE]), 168 :scope => "api offline_access", 169 :client_id => "browser", 170 :deviceType => 3, 171 :deviceIdentifier => SecureRandom.uuid, 172 :deviceName => "firefox", 173 :devicePushToken => "" 174 } 175 176 access_token = last_json_response["access_token"] 177 178 get "/api/sync", {}, { 179 "HTTP_AUTHORIZATION" => "Bearer #{access_token}", 180 } 181 last_response.status.must_equal 200 182 183 d = Device.find_by_access_token(access_token) 184 d.regenerate_tokens!(1) 185 d.save 186 187 get "/api/sync", {}, { 188 "HTTP_AUTHORIZATION" => "Bearer #{d.access_token}", 189 } 190 last_response.status.must_equal 200 191 192 sleep 2 193 194 get "/api/sync", {}, { 195 "HTTP_AUTHORIZATION" => "Bearer #{d.access_token}", 196 } 197 last_response.status.wont_equal 200 198 end 199end