BigW Consortium Gitlab

api_helpers_spec.rb 10.1 KB
Newer Older
1 2
require 'spec_helper'

3 4
describe API::Helpers, api: true do
  include API::Helpers
5
  include ApiHelpers
6
  include SentryHelper
7

8 9 10 11 12
  let(:user) { create(:user) }
  let(:admin) { create(:admin) }
  let(:key) { create(:key, user: user) }

  let(:params) { {} }
13 14
  let(:env) { { 'REQUEST_METHOD' => 'GET' } }
  let(:request) { Rack::Request.new(env) }
15 16 17 18

  def set_env(token_usr, identifier)
    clear_env
    clear_param
19 20
    env[API::Helpers::PRIVATE_TOKEN_HEADER] = token_usr.private_token
    env[API::Helpers::SUDO_HEADER] = identifier
21 22 23 24 25
  end

  def set_param(token_usr, identifier)
    clear_env
    clear_param
26 27
    params[API::Helpers::PRIVATE_TOKEN_PARAM] = token_usr.private_token
    params[API::Helpers::SUDO_PARAM] = identifier
28 29 30
  end

  def clear_env
31 32
    env.delete(API::Helpers::PRIVATE_TOKEN_HEADER)
    env.delete(API::Helpers::SUDO_HEADER)
33 34 35
  end

  def clear_param
36 37
    params.delete(API::Helpers::PRIVATE_TOKEN_PARAM)
    params.delete(API::Helpers::SUDO_PARAM)
38 39
  end

40 41 42 43 44 45 46 47 48
  def warden_authenticate_returns(value)
    warden = double("warden", authenticate: value)
    env['warden'] = warden
  end

  def doorkeeper_guard_returns(value)
    allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ value }
  end

49 50 51 52 53
  def error!(message, status)
    raise Exception
  end

  describe ".current_user" do
54 55
    subject { current_user }

56
    describe "Warden authentication" do
57 58
      before { doorkeeper_guard_returns false }

59 60 61 62 63
      context "with invalid credentials" do
        context "GET request" do
          before { env['REQUEST_METHOD'] = 'GET' }
          it { is_expected.to be_nil }
        end
64 65
      end

66
      context "with valid credentials" do
67 68
        before { warden_authenticate_returns user }

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
        context "GET request" do
          before { env['REQUEST_METHOD'] = 'GET' }
          it { is_expected.to eq(user) }
        end

        context "HEAD request" do
          before { env['REQUEST_METHOD'] = 'HEAD' }
          it { is_expected.to eq(user) }
        end

        context "PUT request" do
          before { env['REQUEST_METHOD'] = 'PUT' }
          it { is_expected.to be_nil }
        end

        context "POST request" do
          before { env['REQUEST_METHOD'] = 'POST' }
          it { is_expected.to be_nil }
        end

        context "DELETE request" do
          before { env['REQUEST_METHOD'] = 'DELETE' }
          it { is_expected.to be_nil }
        end
93 94 95
      end
    end

96
    describe "when authenticating using a user's private token" do
97
      it "returns nil for an invalid token" do
98
        env[API::Helpers::PRIVATE_TOKEN_HEADER] = 'invalid token'
99 100 101 102
        allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
        expect(current_user).to be_nil
      end

103
      it "returns nil for a user without access" do
104
        env[API::Helpers::PRIVATE_TOKEN_HEADER] = user.private_token
105
        allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
106 107
        expect(current_user).to be_nil
      end
108

109
      it "leaves user as is when sudo not specified" do
110
        env[API::Helpers::PRIVATE_TOKEN_HEADER] = user.private_token
111 112
        expect(current_user).to eq(user)
        clear_env
113
        params[API::Helpers::PRIVATE_TOKEN_PARAM] = user.private_token
114 115
        expect(current_user).to eq(user)
      end
116 117
    end

118 119 120
    describe "when authenticating using a user's personal access tokens" do
      let(:personal_access_token) { create(:personal_access_token, user: user) }

121
      it "returns nil for an invalid token" do
122
        env[API::Helpers::PRIVATE_TOKEN_HEADER] = 'invalid token'
123 124 125 126
        allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
        expect(current_user).to be_nil
      end

127
      it "returns nil for a user without access" do
128
        env[API::Helpers::PRIVATE_TOKEN_HEADER] = personal_access_token.token
129
        allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
130 131 132
        expect(current_user).to be_nil
      end

133
      it "leaves user as is when sudo not specified" do
134
        env[API::Helpers::PRIVATE_TOKEN_HEADER] = personal_access_token.token
135 136
        expect(current_user).to eq(user)
        clear_env
137
        params[API::Helpers::PRIVATE_TOKEN_PARAM] = personal_access_token.token
138 139 140 141 142
        expect(current_user).to eq(user)
      end

      it 'does not allow revoked tokens' do
        personal_access_token.revoke!
143
        env[API::Helpers::PRIVATE_TOKEN_HEADER] = personal_access_token.token
144 145 146 147 148 149
        allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
        expect(current_user).to be_nil
      end

      it 'does not allow expired tokens' do
        personal_access_token.update_attributes!(expires_at: 1.day.ago)
150
        env[API::Helpers::PRIVATE_TOKEN_HEADER] = personal_access_token.token
151 152 153
        allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
        expect(current_user).to be_nil
      end
154 155
    end

156
    it "changes current user to sudo when admin" do
157
      set_env(admin, user.id)
158
      expect(current_user).to eq(user)
159
      set_param(admin, user.id)
160
      expect(current_user).to eq(user)
161
      set_env(admin, user.username)
162
      expect(current_user).to eq(user)
163
      set_param(admin, user.username)
164
      expect(current_user).to eq(user)
165 166
    end

167
    it "throws an error when the current user is not an admin and attempting to sudo" do
168
      set_env(user, admin.id)
169
      expect { current_user }.to raise_error(Exception)
170
      set_param(user, admin.id)
171
      expect { current_user }.to raise_error(Exception)
172
      set_env(user, admin.username)
173
      expect { current_user }.to raise_error(Exception)
174
      set_param(user, admin.username)
175
      expect { current_user }.to raise_error(Exception)
176
    end
177

178
    it "throws an error when the user cannot be found for a given id" do
179
      id = user.id + admin.id
180 181
      expect(user.id).not_to eq(id)
      expect(admin.id).not_to eq(id)
182
      set_env(admin, id)
183
      expect { current_user }.to raise_error(Exception)
184 185

      set_param(admin, id)
186
      expect { current_user }.to raise_error(Exception)
187
    end
188

189
    it "throws an error when the user cannot be found for a given username" do
190
      username = "#{user.username}#{admin.username}"
191 192
      expect(user.username).not_to eq(username)
      expect(admin.username).not_to eq(username)
193
      set_env(admin, username)
194
      expect { current_user }.to raise_error(Exception)
195 196

      set_param(admin, username)
197
      expect { current_user }.to raise_error(Exception)
198
    end
199

200
    it "handles sudo's to oneself" do
201
      set_env(admin, admin.id)
202
      expect(current_user).to eq(admin)
203
      set_param(admin, admin.id)
204
      expect(current_user).to eq(admin)
205
      set_env(admin, admin.username)
206
      expect(current_user).to eq(admin)
207
      set_param(admin, admin.username)
208
      expect(current_user).to eq(admin)
209 210
    end

211
    it "handles multiple sudo's to oneself" do
212
      set_env(admin, user.id)
213 214
      expect(current_user).to eq(user)
      expect(current_user).to eq(user)
215
      set_env(admin, user.username)
216 217
      expect(current_user).to eq(user)
      expect(current_user).to eq(user)
218 219

      set_param(admin, user.id)
220 221
      expect(current_user).to eq(user)
      expect(current_user).to eq(user)
222
      set_param(admin, user.username)
223 224
      expect(current_user).to eq(user)
      expect(current_user).to eq(user)
225
    end
226

227
    it "handles multiple sudo's to oneself using string ids" do
228
      set_env(admin, user.id.to_s)
229 230
      expect(current_user).to eq(user)
      expect(current_user).to eq(user)
231 232

      set_param(admin, user.id.to_s)
233 234
      expect(current_user).to eq(user)
      expect(current_user).to eq(user)
235 236 237 238
    end
  end

  describe '.sudo_identifier' do
239
    it "returns integers when input is an int" do
240
      set_env(admin, '123')
241
      expect(sudo_identifier).to eq(123)
242
      set_env(admin, '0001234567890')
243
      expect(sudo_identifier).to eq(1234567890)
244 245

      set_param(admin, '123')
246
      expect(sudo_identifier).to eq(123)
247
      set_param(admin, '0001234567890')
248
      expect(sudo_identifier).to eq(1234567890)
249 250
    end

251
    it "returns string when input is an is not an int" do
252
      set_env(admin, '12.30')
253
      expect(sudo_identifier).to eq("12.30")
254
      set_env(admin, 'hello')
255
      expect(sudo_identifier).to eq('hello')
256
      set_env(admin, ' 123')
257
      expect(sudo_identifier).to eq(' 123')
258 259

      set_param(admin, '12.30')
260
      expect(sudo_identifier).to eq("12.30")
261
      set_param(admin, 'hello')
262
      expect(sudo_identifier).to eq('hello')
263
      set_param(admin, ' 123')
264
      expect(sudo_identifier).to eq(' 123')
265 266
    end
  end
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289

  describe '.to_boolean' do
    it 'converts a valid string to a boolean' do
      expect(to_boolean('true')).to be_truthy
      expect(to_boolean('YeS')).to be_truthy
      expect(to_boolean('t')).to be_truthy
      expect(to_boolean('1')).to be_truthy
      expect(to_boolean('ON')).to be_truthy
      expect(to_boolean('FaLse')).to be_falsy
      expect(to_boolean('F')).to be_falsy
      expect(to_boolean('NO')).to be_falsy
      expect(to_boolean('n')).to be_falsy
      expect(to_boolean('0')).to be_falsy
      expect(to_boolean('oFF')).to be_falsy
    end

    it 'converts an invalid string to nil' do
      expect(to_boolean('fals')).to be_nil
      expect(to_boolean('yeah')).to be_nil
      expect(to_boolean('')).to be_nil
      expect(to_boolean(nil)).to be_nil
    end
  end
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315

  describe '.handle_api_exception' do
    before do
      allow_any_instance_of(self.class).to receive(:sentry_enabled?).and_return(true)
      allow_any_instance_of(self.class).to receive(:rack_response)
    end

    it 'does not report a MethodNotAllowed exception to Sentry' do
      exception = Grape::Exceptions::MethodNotAllowed.new({ 'X-GitLab-Test' => '1' })
      allow(exception).to receive(:backtrace).and_return(caller)

      expect(Raven).not_to receive(:capture_exception).with(exception)

      handle_api_exception(exception)
    end

    it 'does report RuntimeError to Sentry' do
      exception = RuntimeError.new('test error')
      allow(exception).to receive(:backtrace).and_return(caller)

      expect_any_instance_of(self.class).to receive(:sentry_context)
      expect(Raven).to receive(:capture_exception).with(exception)

      handle_api_exception(exception)
    end
  end
316
end