require 'rails_helper' RSpec.describe Services::RsAuthsController, type: :controller do let(:user) { create :user } before do allow_any_instance_of(AppCatalog::WebApp).to receive(:update_metadata).and_return(true) allow_any_instance_of(RemoteStorageAuthorization).to receive(:remove_token_expiry_job).and_return(nil) allow_any_instance_of(LdapService).to receive(:fetch_users).and_return([ { services_enabled: ["remotestorage"] } ]) end describe "GET /services/storage/apps/:client_id/launch (storage-first)" do context "when user is signed in" do before do sign_in user end context "authorization exists" do before do @auth = user.remote_storage_authorizations.create!( permissions: %w(documents), client_id: "app.example.com", redirect_uri: "https://app.example.com", expire_at: 2.days.from_now ) get :launch_app, params: { client_id: @auth.client_id } end after do @auth.destroy end it "hands the app the user address in the URL fragment" do launch_url = "https://app.example.com#remotestorage=#{user.address}" expect(response).to redirect_to(launch_url) end it "does not hand over an access token" do expect(response.location).not_to include("access_token") end end context "when remoteStorage is not enabled for the user" do before do allow_any_instance_of(LdapService).to receive(:fetch_users).and_return([ { services_enabled: [] } ]) get :launch_app, params: { client_id: "app.example.com" } end it "responds with forbidden" do expect(response).to have_http_status(:forbidden) end end end end describe "GET /services/storage/apps/:client_id/launch for an unknown app" do before { sign_in user } it "returns 404 instead of raising" do get :launch_app, params: { client_id: "nonexistent.example.com" } expect(response).to have_http_status(:not_found) end end describe "DELETE /services/storage/rs_auths/:id" do context "when user is signed in" do before { sign_in user } it "revokes the authorization" do auth = user.remote_storage_authorizations.create!( permissions: %w(documents), client_id: "app.example.com", redirect_uri: "https://app.example.com", expire_at: 2.days.from_now ) expect { delete :destroy, params: { id: auth.id } }.to change(RemoteStorageAuthorization, :count).by(-1) expect(response).to redirect_to(apps_services_storage_url) end it "returns 404 for an unknown authorization" do delete :destroy, params: { id: 0 } expect(response).to have_http_status(:not_found) end it "returns 404 for another user's authorization" do other = create :user, id: 99, cn: "otheruser", email: "other@example.com" auth = other.remote_storage_authorizations.create!( permissions: %w(documents), client_id: "app.example.com", redirect_uri: "https://app.example.com" ) delete :destroy, params: { id: auth.id } expect(response).to have_http_status(:not_found) expect(RemoteStorageAuthorization.exists?(auth.id)).to be(true) end context "when another device remains connected to the app" do let!(:revoked) do user.remote_storage_authorizations.create!( permissions: %w(documents), client_id: "app.example.com", redirect_uri: "https://app.example.com", expire_at: 2.days.from_now ) end let!(:remaining) do user.remote_storage_authorizations.create!( permissions: %w(documents photos), client_id: "app.example.com", redirect_uri: "https://app.example.com", expire_at: 2.days.from_now ) end after do revoked.destroy remaining.destroy end it "streams the removed device row and the refreshed card and access list" do delete :destroy, params: { id: revoked.id }, format: :turbo_stream expect(response.media_type).to eq("text/vnd.turbo-stream.html") expect(response.body).to include( %(action="remove" target="#{ActionView::RecordIdentifier.dom_id(revoked, :device)}") ) expect(response.body).to include( %(action="replace" target="#{controller.helpers.rs_app_dom_id('app.example.com', :card)}") ) expect(response.body).to include( %(action="replace" target="#{controller.helpers.rs_app_dom_id('app.example.com', :access)}") ) end it "leaves the other device connected" do delete :destroy, params: { id: revoked.id }, format: :turbo_stream expect(RemoteStorageAuthorization.exists?(remaining.id)).to be(true) end end context "when the app's last device is revoked" do it "redirects so that the dashboard re-renders itself" do auth = user.remote_storage_authorizations.create!( permissions: %w(documents), client_id: "app.example.com", redirect_uri: "https://app.example.com", expire_at: 2.days.from_now ) delete :destroy, params: { id: auth.id }, format: :turbo_stream expect(response).to redirect_to(apps_services_storage_url) expect(flash[:success]).to eq("App authorization revoked") end end end end end