Files
ubicloud/spec/routes/web/project_spec.rb
Jeremy Evans 9d5f0a2cbc Validate Vm#public_key when saving
This implements a fairly loose validation of the public key format.
It does not check specific key types, nor try to decode the base64
key and check it for validity.  It does check that the format in
general is valid, and that at least one valid key is present.
It will reject public keys, because those don't have a space.

This requires changes to many specs to have them use a valid public
key format.  In many cases, that is done by changing a single
dash to a space.
2025-04-26 05:34:41 +09:00

730 lines
32 KiB
Ruby

# frozen_string_literal: true
require_relative "spec_helper"
RSpec.describe Clover, "project" do
let(:user) { create_account }
let(:user2) { create_account("user2@example.com") }
let(:project) { user.create_project_with_default_policy("project-1") }
let(:project_wo_permissions) { user.create_project_with_default_policy("project-2", default_policy: nil) }
describe "unauthenticated" do
it "can not list without login" do
visit "/project"
expect(page.title).to eq("Ubicloud - Login")
end
it "can not create without login" do
visit "/project/create"
expect(page.title).to eq("Ubicloud - Login")
end
end
describe "authenticated" do
before do
login(user.email)
end
describe "list" do
it "can list no projects" do
user.remove_all_projects
visit "/project"
expect(page.title).to eq("Ubicloud - Projects")
within ".empty-state" do
expect(page).to have_content "No projects"
click_link "Create Project"
end
expect(page.title).to eq("Ubicloud - Create Project")
end
it "can not list projects when does not invited" do
project
new_project = user2.create_project_with_default_policy("project-3")
visit "/project"
expect(page.title).to eq("Ubicloud - Projects")
expect(page).to have_content project.name
expect(page).to have_no_content new_project.name
end
end
describe "create" do
it "can create new project" do
name = "new-project"
visit "/project/create"
expect(project.accounts_dataset.count).to eq 1
expect(page.title).to eq("Ubicloud - Create Project")
fill_in "Name", with: name
click_button "Create"
expect(page.title).to eq("Ubicloud - #{name}")
expect(page).to have_content name
project = Project[name: name]
expect(project.accounts_dataset.count).to eq 1
expect(project.access_control_entries.count).to eq 2
expect(project.subject_tags.map(&:name).sort).to eq %w[Admin Member]
expect(user.projects).to include project
end
end
describe "dashboard" do
it "can view project dashboard always" do
visit "#{project_wo_permissions.path}/dashboard"
expect(page.title).to eq("Ubicloud - #{project_wo_permissions.name} Dashboard")
expect(page).to have_content project_wo_permissions.name
end
it "raises forbidden when user isn't added to project" do
new_project = Project.create_with_id(name: "new-project")
visit "#{new_project.path}/dashboard"
expect(page.title).to eq("Ubicloud - Forbidden")
expect(page.status_code).to eq(403)
expect(page).to have_content "Forbidden"
end
it "not show on sidebar when does not have permissions" do
visit "#{project_wo_permissions.path}/dashboard"
within "#desktop-menu" do
expect { click_link "Users" }.to raise_error Capybara::ElementNotFound
expect { click_link "Access Policy" }.to raise_error Capybara::ElementNotFound
expect { click_link "Billing" }.to raise_error Capybara::ElementNotFound
expect { click_link "Settings" }.to raise_error Capybara::ElementNotFound
end
end
it "shows content when user has permissions" do
visit "#{project.path}/dashboard"
within "#tiles" do
expect(page).to have_content "Virtual Machines"
expect(page).to have_content "Databases"
expect(page).to have_content "Load Balancers"
expect(page).to have_content "Firewalls"
if Config.github_app_name
expect(page).to have_content "GitHub Runners"
else
expect(page).to have_no_content "GitHub Runners"
end
expect(page).to have_content "Users"
end
within "#cards" do
expect(page).to have_content "Create Virtual Machine"
if Config.github_app_name
expect(page).to have_content "Use GitHub Runners"
else
expect(page).to have_no_content "GitHub Runners"
end
expect(page).to have_content "Create Managed Database"
expect(page).to have_content "Add User to Project"
expect(page).to have_content "Load Balance Your Traffic"
expect(page).to have_content "Create Access Token"
expect(page).to have_content "Documentation"
expect(page).to have_content "Get Support"
end
end
it "does not show content when user does not have permissions" do
visit "#{project_wo_permissions.path}/dashboard"
within "#tiles" do
expect(page).to have_no_content "Virtual Machines"
expect(page).to have_no_content "Databases"
expect(page).to have_no_content "Load Balancers"
expect(page).to have_no_content "Firewalls"
expect(page).to have_no_content "GitHub Runners"
expect(page).to have_no_content "Users"
end
within "#cards" do
expect(page).to have_no_content "Create Virtual Machine"
expect(page).to have_no_content "Use GitHub Runners"
expect(page).to have_no_content "Create Managed Database"
expect(page).to have_no_content "Add User to Project"
expect(page).to have_no_content "Distribute Your Traffic"
expect(page).to have_no_content "Create Access Token"
expect(page).to have_content "Documentation"
expect(page).to have_content "Get Support"
end
end
end
describe "details" do
it "can show project details" do
project.add_quota(quota_id: ProjectQuota.default_quotas["VmVCpu"]["id"], value: 0)
visit "/project"
expect(page.title).to eq("Ubicloud - Projects")
expect(page).to have_content project.name
find("#project-#{project.ubid}").click_link project.name
expect(page.title).to eq("Ubicloud - #{project.name} Dashboard")
expect(page).to have_content project.name
find_by_id("desktop-menu").click_link "Settings"
expect(page.title).to eq("Ubicloud - #{project.name}")
end
it "raises forbidden when does not have permissions" do
project_wo_permissions
visit "/project/#{project_wo_permissions.ubid}"
expect(page.title).to eq("Ubicloud - Forbidden")
expect(page.status_code).to eq(403)
expect(page).to have_content "Forbidden"
end
it "raises not found when project not exists" do
visit "/project/08s56d4kaj94xsmrnf5v5m3mav"
expect(page.title).to eq("Ubicloud - ResourceNotFound")
expect(page.status_code).to eq(404)
expect(page).to have_content "ResourceNotFound"
end
it "can update the project name" do
new_name = "New-Project-Name"
visit project.path
fill_in "name", with: new_name
click_button "Save"
expect(page).to have_content new_name
expect(project.reload.name).to eq(new_name)
end
it "can not update the project name when does not have permissions" do
visit project_wo_permissions.path
expect { click_button "Save" }.to raise_error Capybara::ElementNotFound
end
end
describe "users" do
it "can show project users" do
visit project.path
within "#desktop-menu" do
click_link "Users"
end
expect(page.title).to eq("Ubicloud - #{project.name} - Users")
expect(page).to have_content user.email
end
it "raises forbidden when does not have Project:user permissions" do
project_wo_permissions
visit "#{project_wo_permissions.path}/user"
expect(page.title).to eq("Ubicloud - Forbidden")
expect(page.status_code).to eq(403)
expect(page).to have_content "Forbidden"
project
AccessControlEntry.dataset.destroy
visit "#{project.path}/user"
expect(page.status_code).to eq(403)
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Project:user"])
page.refresh
expect(page.title).to eq("Ubicloud - project-1 - Users")
end
it "requires Project:user permissions to invite users, and SubjectTag:add to add to policies" do
visit "#{project.path}/user"
AccessControlEntry.dataset.destroy
fill_in "Email", with: user2.email
select "Admin", from: "policy"
click_button "Invite"
expect(page.status_code).to eq(403)
expect(Mail::TestMailer.deliveries.length).to eq 0
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Project:user"])
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:view"])
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:add"])
visit "#{project.path}/user"
fill_in "Email", with: user2.email
select "Admin", from: "policy"
click_button "Invite"
expect(ProjectInvitation.count).to eq 0
expect(Mail::TestMailer.deliveries.length).to eq 1
end
it "can invite existing user to project with a default policy" do
visit "#{project.path}/user"
expect(page).to have_content user.email
expect(page).to have_no_content user2.email
subject_tag = project.subject_tags.first
expect(ProjectInvitation.count).to eq 0
expect(DB[:applied_subject_tag].first(tag_id: subject_tag.id, subject_id: user2.id)).to be_nil
fill_in "Email", with: user2.email
select "Admin", from: "policy"
click_button "Invite"
expect(page).to have_content user.email
expect(page).to have_content user2.email
expect(ProjectInvitation.count).to eq 0
expect(DB[:applied_subject_tag].first(tag_id: subject_tag.id, subject_id: user2.id)).not_to be_nil
expect(Mail::TestMailer.deliveries.length).to eq 1
end
it "can only add existing invited user to subject tag if SubjectTag:add permissions are allowed for it" do
allowed = SubjectTag.create_with_id(project_id: project.id, name: "Allowed")
AccessControlEntry.dataset.destroy
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Project:user"])
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:view"])
visit "#{project.path}/user"
fill_in "Email", with: user2.email
select "Allowed", from: "policy"
click_button "Invite"
expect(page).to have_flash_error("You don't have permission to invite users with this subject tag.")
page.refresh
fill_in "Email", with: user2.email
select "No access", from: "policy"
click_button "Invite"
expect(page).to have_content user.email
expect(page).to have_content user2.email
expect(ProjectInvitation.count).to eq 0
expect(Mail::TestMailer.deliveries.length).to eq 1
expect(allowed.member_ids).to be_empty
end
it "can invite existing user to project without a default policy" do
visit "#{project.path}/user"
subject_tag = project.subject_tags.first
expect(DB[:applied_subject_tag].first(tag_id: subject_tag.id, subject_id: user2.id)).to be_nil
fill_in "Email", with: user2.email
select "No access", from: "policy"
click_button "Invite"
expect(DB[:applied_subject_tag].first(tag_id: subject_tag.id, subject_id: user2.id)).to be_nil
expect(page).to have_content user2.email
expect(Mail::TestMailer.deliveries.length).to eq 1
end
it "can only set subject tag for new invited user if SubjectTag:add permissions are allowed for it" do
allowed = SubjectTag.create_with_id(project_id: project.id, name: "Allowed")
AccessControlEntry.dataset.destroy
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Project:user"])
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:view"])
visit "#{project.path}/user"
fill_in "Email", with: user2.email
select "Allowed", from: "policy"
click_button "Invite"
expect(page).to have_flash_error("You don't have permission to invite users with this subject tag.")
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:add"], object_id: allowed.id)
visit "#{project.path}/user"
new_email = "newUpper@example.com"
expect(page).to have_content user.email
fill_in "Email", with: new_email
select "No access", from: "policy"
click_button "Invite"
expect(page).to have_flash_notice("Invitation sent successfully to 'newUpper@example.com'.")
expect(page).to have_content user.email
expect(page).to have_content new_email
expect(page).to have_content "Invitation sent successfully to '#{new_email}'."
expect(Mail::TestMailer.deliveries.length).to eq 1
expect(ProjectInvitation.where(email: new_email, policy: nil).count).to eq 1
end
it "can invite non-existent user to project" do
visit "#{project.path}/user"
new_email = "newUpper@example.com"
expect(page).to have_content user.email
fill_in "Email", with: new_email
select "Admin", from: "policy"
click_button "Invite"
expect(page).to have_content user.email
expect(page).to have_content new_email
expect(page).to have_flash_notice(/Invitation sent successfully to '#{new_email}'.*/)
expect(page).to have_select("invitation_policies[#{new_email}]", selected: "Admin")
expect(Mail::TestMailer.deliveries.length).to eq 1
expect(ProjectInvitation.where(email: new_email).count).to eq 1
fill_in "Email", with: new_email.downcase
click_button "Invite"
expect(page).to have_flash_error("'#{new_email.downcase}' already invited to join the project.")
end
it "requires Project:user permissions to remove users from project" do
user2.add_project(project)
visit "#{project.path}/user"
AccessControlEntry.dataset.destroy
btn = find "#user-#{user2.ubid} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(page.status_code).to eq 403
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Project:user"])
visit "#{project.path}/user"
btn = find "#user-#{user2.ubid} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(page.status_code).to eq 204
end
it "can remove user from project" do
user2.add_project(project)
project.subject_tags_dataset.first(name: "Admin").add_subject(user2.id)
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user2.id)
visit "#{project.path}/user"
expect(page).to have_content user.email
expect(page).to have_content user2.email
# We send delete request manually instead of just clicking to button because delete action triggered by JavaScript.
# UI tests run without a JavaScript enginer.
btn = find "#user-#{user2.ubid} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(page.body).to be_empty
DB.transaction(rollback: :always) do
DB[:account_password_hashes].where(id: user2.id).delete(force: true)
user2.destroy
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}, "HTTP_ACCEPT" => "application/json"
expect(page.status_code).to eq(404)
expect(JSON.parse(page.body).dig("error", "code")).to eq(404)
end
visit "#{project.path}/user"
expect(page).to have_content user.email
expect(page).to have_flash_notice("Removed #{user2.email} from #{project.name}")
visit "#{project.path}/user"
expect(page).to have_content user.email
expect(page).to have_no_content user2.email
expect(DB[:applied_subject_tag].where(tag_id: project.subject_tags_dataset.first(name: "Admin").id, subject_id: user2.id).all).to be_empty
expect(AccessControlEntry.where(project_id: project.id, subject_id: user2.id).all).to be_empty
end
it "requires Project:user permissions to remove invited users from project" do
invited_email = "invited@example.com"
project.add_invitation(email: invited_email, inviter_id: "bd3479c6-5ee3-894c-8694-5190b76f84cf", expires_at: Time.now + 7 * 24 * 60 * 60)
visit "#{project.path}/user"
AccessControlEntry.dataset.destroy
btn = find "#invitation-#{invited_email.gsub(/\W+/, "")} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(page.status_code).to eq 403
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Project:user"])
visit "#{project.path}/user"
btn = find "#invitation-#{invited_email.gsub(/\W+/, "")} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(page.status_code).to eq 204
end
it "can remove invited user from project" do
invited_email = "invited@example.com"
project.add_invitation(email: invited_email, inviter_id: "bd3479c6-5ee3-894c-8694-5190b76f84cf", expires_at: Time.now + 7 * 24 * 60 * 60)
visit "#{project.path}/user"
expect(page).to have_content invited_email
# We send delete request manually instead of just clicking to button because delete action triggered by JavaScript.
# UI tests run without a JavaScript enginer.
btn = find "#invitation-#{invited_email.gsub(/\W+/, "")} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
visit "#{project.path}/user"
expect(page).to have_flash_notice("Invitation for '#{invited_email}' is removed successfully.")
visit "#{project.path}/user"
expect(page).to have_no_content invited_email
expect { find "#invitation-#{invited_email.gsub(/\W+/, "")} .delete-btn" }.to raise_error Capybara::ElementNotFound
end
it "requires Project:user permissions to update default policy of invited user, and SubjectTag:add for access to subject tag" do
invited_email = "invited@example.com"
project.add_invitation(email: invited_email, inviter_id: "bd3479c6-5ee3-894c-8694-5190b76f84cf", expires_at: Time.now + 7 * 24 * 60 * 60)
visit "#{project.path}/user"
AccessControlEntry.dataset.destroy
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:view"])
within "form#managed-policy" do
select "Admin", from: "invitation_policies[#{invited_email}]"
click_button "Update"
end
expect(page.status_code).to eq 403
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Project:user"])
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:add"])
visit "#{project.path}/user"
within "form#managed-policy" do
select "Admin", from: "invitation_policies[#{invited_email}]"
click_button "Update"
end
expect(page).to have_flash_notice("1 members added to Admin")
end
it "can update default policy of invited user" do
invited_email = "invited@example.com"
project.add_invitation(email: invited_email, policy: "Member", inviter_id: "bd3479c6-5ee3-894c-8694-5190b76f84cf", expires_at: Time.now + 7 * 24 * 60 * 60)
inv2 = project.add_invitation(email: "invited2@example.com", policy: "Member", inviter_id: "bd3479c6-5ee3-894c-8694-5190b76f84cf", expires_at: Time.now + 7 * 24 * 60 * 60)
visit "#{project.path}/user"
inv2.destroy
expect(page).to have_select("invitation_policies[#{invited_email}]", selected: "Member")
within "form#managed-policy" do
select "Admin", from: "invitation_policies[#{invited_email}]"
click_button "Update"
end
expect(page).to have_flash_notice("1 members added to Admin, 1 members removed from Member")
expect(page).to have_select("invitation_policies[#{invited_email}]", selected: "Admin")
end
it "can only update default policy of invited user if new policy is allowed subject tag" do
allowed = SubjectTag.create_with_id(project_id: project.id, name: "Allowed")
to_be_removed = SubjectTag.create_with_id(project_id: project.id, name: "ToBeRemoved")
AccessControlEntry.dataset.destroy
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Project:user"])
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:view"])
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:add"], object_id: allowed.id)
ace = AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:add"], object_id: to_be_removed.id)
invited_email = "invited@example.com"
project.add_invitation(email: invited_email, policy: "Allowed", inviter_id: "bd3479c6-5ee3-894c-8694-5190b76f84cf", expires_at: Time.now + 7 * 24 * 60 * 60)
visit "#{project.path}/user"
within "form#managed-policy" do
click_button "Update"
end
within "form#managed-policy" do
select "ToBeRemoved", from: "invitation_policies[#{invited_email}]"
click_button "Update"
end
expect(page).to have_flash_notice("No change in user policies")
expect(page).to have_flash_error("You don't have permission to remove invitation from 'Allowed' tag")
expect(page).to have_select("invitation_policies[#{invited_email}]", selected: "Allowed")
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:remove"], object_id: allowed.id)
within "form#managed-policy" do
select "ToBeRemoved", from: "invitation_policies[#{invited_email}]"
ace.destroy
click_button "Update"
end
expect(page).to have_flash_notice("No change in user policies")
expect(page).to have_flash_error("You don't have permission to add invitation to 'ToBeRemoved' tag")
expect(page).to have_select("invitation_policies[#{invited_email}]", selected: "Allowed")
within "form#managed-policy" do
select "No access", from: "invitation_policies[#{invited_email}]"
click_button "Update"
end
expect(page).to have_flash_notice("1 members removed from Allowed")
expect(page).to have_select("invitation_policies[#{invited_email}]", selected: nil)
end
it "can update default policy of existing user" do
tag1 = SubjectTag.create_with_id(project_id: project.id, name: "FirstTag")
tag2 = SubjectTag.create_with_id(project_id: project.id, name: "SecondTag")
AccessControlEntry.dataset.destroy
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Project:user"])
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:view"])
ace = AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:add"], object_id: tag1.id)
remove_ace = AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:remove"], object_id: tag1.id)
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:add"], object_id: tag2.id)
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:remove"], object_id: tag2.id)
user2.add_project(project)
tag1.add_subject(user2.id)
visit "#{project.path}/user"
admin_tag = project.subject_tags_dataset.first(name: "Admin")
within "form#managed-policy" do
select "SecondTag", from: "user_policies[#{user2.ubid}]"
admin_tag.add_subject(user2.id)
click_button "Update"
end
expect(page).to have_flash_notice("No change in user policies")
expect(page).to have_flash_error("Cannot change the policy for user, as they are in multiple subject tags")
expect(page.find_by_id("user-#{user2.ubid}")).to have_content "Admin, FirstTag"
admin_tag.remove_members(user2.id)
page.refresh
DB.transaction(rollback: :always) do
within "form#managed-policy" do
select "SecondTag", from: "user_policies[#{user2.ubid}]"
user2.remove_project(project)
click_button "Update"
end
expect(page).to have_flash_notice("No change in user policies")
expect(page).to have_flash_error("Cannot change the policy for user, as they are not associated to project")
end
page.refresh
remove_ace.destroy
within "form#managed-policy" do
select "SecondTag", from: "user_policies[#{user2.ubid}]"
click_button "Update"
end
expect(page).to have_flash_notice("No change in user policies")
expect(page).to have_flash_error("You don't have permission to remove members from 'FirstTag' tag")
noremove = page.find_by_id("user-#{user2.ubid}-noremove")
expect(noremove["title"]).to eq "You cannot change the policy for this user"
expect(noremove.text).to eq "FirstTag"
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:remove"], object_id: tag1.id)
page.refresh
within "form#managed-policy" do
select "SecondTag", from: "user_policies[#{user2.ubid}]"
click_button "Update"
end
expect(page).to have_flash_notice("1 members added to SecondTag, 1 members removed from FirstTag")
expect(page).to have_select("user_policies[#{user2.ubid}]", selected: "SecondTag")
within "form#managed-policy" do
select "FirstTag", from: "user_policies[#{user2.ubid}]"
ace.destroy
click_button "Update"
end
expect(page).to have_flash_notice("No change in user policies")
expect(page).to have_flash_error("You don't have permission to add members to 'FirstTag' tag")
expect(page).to have_select("user_policies[#{user2.ubid}]", selected: "SecondTag")
within "form#managed-policy" do
select "No access", from: "user_policies[#{user2.ubid}]"
click_button "Update"
end
expect(page).to have_flash_notice("1 members removed from SecondTag")
expect(page).to have_select("user_policies[#{user2.ubid}]", selected: nil)
within "form#managed-policy" do
select "SecondTag", from: "user_policies[#{user2.ubid}]"
click_button "Update"
end
expect(page).to have_flash_notice("1 members added to SecondTag")
expect(page).to have_select("user_policies[#{user2.ubid}]", selected: "SecondTag")
AccessControlEntry.create_with_id(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["SubjectTag:remove"], object_id: admin_tag.id)
page.refresh
expect(page).to have_select("user_policies[#{user.ubid}]", selected: "Admin")
within "form#managed-policy" do
select "No access", from: "user_policies[#{user.ubid}]"
click_button "Update"
end
expect(page).to have_flash_error("The project must have at least one admin.")
end
it "can not have more than 50 pending invitations" do
visit "#{project.path}/user"
expect(Project).to receive(:from_ubid).and_return(project).at_least(:once)
expect(project).to receive(:invitations_dataset).and_return(instance_double(Sequel::Dataset, count: 50))
expect(project).to receive(:invitations_dataset).and_call_original
fill_in "Email", with: "new@example.com"
click_button "Invite"
expect(page).to have_no_content "new@example.com"
expect(page).to have_flash_error("You can't have more than 50 pending invitations.")
end
it "raises bad request when it's the last user" do
user
visit "#{project.path}/user"
# We send delete request manually instead of just clicking to button because delete action triggered by JavaScript.
# UI tests run without a JavaScript enginer.
btn = find "#user-#{user.ubid} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(page.status_code).to eq(400)
expect(page.body).to eq({error: {message: "You can't remove the last user from '#{project.name}' project. Delete project instead."}}.to_json)
visit "#{project.path}/user"
expect(page).to have_content user.email
end
it "raises not found when user not exists" do
visit "#{project.path}/user/08s56d4kaj94xsmrnf5v5m3mav"
expect(page.title).to eq("Ubicloud - ResourceNotFound")
expect(page.status_code).to eq(404)
expect(page).to have_content "ResourceNotFound"
end
end
describe "delete" do
it "can delete project" do
visit project.path
# We send delete request manually instead of just clicking to button because delete action triggered by JavaScript.
# UI tests run without a JavaScript enginer.
btn = find ".delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(page.status_code).to eq(204)
expect(Project[project.id].visible).to be_falsey
expect(DB[:access_tag].where(project_id: project.id).count).to eq(0)
expect(AccessControlEntry.where(project_id: project.id).count).to eq(0)
expect(SubjectTag.where(project_id: project.id).count).to eq(0)
end
it "can not delete project when it has resources" do
Prog::Vm::Nexus.assemble("k y", project.id, name: "vm1")
visit project.path
# We send delete request manually instead of just clicking to button because delete action triggered by JavaScript.
# UI tests run without a JavaScript enginer.
btn = find ".delete-btn"
Capybara.current_session.driver.header "Accept", "application/json"
response = page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(response).to have_api_error(409, "'#{project.name}' project has some resources. Delete all related resources first.")
visit "/project"
expect(page).to have_content project.name
end
it "can not delete project when does not have permissions" do
# Give permission to view, so we can see the detail page
AccessControlEntry.create_with_id(project_id: project_wo_permissions.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Project:view"])
visit project_wo_permissions.path
expect(page.title).to eq "Ubicloud - project-2"
expect { find ".delete-btn" }.to raise_error Capybara::ElementNotFound
end
end
end
end