diff --git a/Dockerfile b/Dockerfile index 352d42ed7..ad7b8de85 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,20 +3,20 @@ FROM ubuntu:bionic ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ - apt-get upgrade -y --no-install-recommends && \ - apt-get install -y --no-install-recommends \ - build-essential \ - ca-certificates \ - carton \ - curl \ - git \ - libssl-dev \ - libzip-dev \ - perl-doc \ - unzip \ - postgresql \ - libpq-dev \ - && apt-get clean + apt-get upgrade -y --no-install-recommends && \ + apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + carton \ + curl \ + git \ + libssl-dev \ + libzip-dev \ + perl-doc \ + unzip \ + postgresql \ + libpq-dev \ + && apt-get clean RUN mkdir -p /app/conch WORKDIR /app/conch diff --git a/Dockerfile.dev b/Dockerfile.dev index 05d46e80e..0ee2361ad 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -3,20 +3,20 @@ FROM ubuntu:bionic ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ - apt-get upgrade -y --no-install-recommends && \ - apt-get install -y --no-install-recommends \ - build-essential \ - ca-certificates \ - carton \ - curl \ - git \ - libssl-dev \ - libzip-dev \ - perl-doc \ - unzip \ - postgresql \ - libpq-dev \ - && apt-get clean + apt-get upgrade -y --no-install-recommends && \ + apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + carton \ + curl \ + git \ + libssl-dev \ + libzip-dev \ + perl-doc \ + unzip \ + postgresql \ + libpq-dev \ + && apt-get clean RUN mkdir -p /app/conch WORKDIR /app/conch diff --git a/docker/buildbot-prerelease.bash b/docker/buildbot-prerelease.bash index c6dbea5c5..069ea15ab 100755 --- a/docker/buildbot-prerelease.bash +++ b/docker/buildbot-prerelease.bash @@ -14,7 +14,7 @@ IFS=$'\n\t' PREFIX=${PREFIX} LABEL=${LABEL} docker/builder.sh --file Dockerfile.dev . docker run \ - --mount type=volume,src=${PREFIX}-conch-api-carton,dst=/app/conch/local \ - --rm \ - --name ${PREFIX}_${BUILDNUMBER} \ - ${PREFIX}/conch-api:${LABEL} + --mount type=volume,src=${PREFIX}-conch-api-carton,dst=/app/conch/local \ + --rm \ + --name ${PREFIX}_${BUILDNUMBER} \ + ${PREFIX}/conch-api:${LABEL} diff --git a/docker/buildbot-release.bash b/docker/buildbot-release.bash index ed85ed95b..a4a226586 100755 --- a/docker/buildbot-release.bash +++ b/docker/buildbot-release.bash @@ -8,8 +8,8 @@ LABEL=$(echo "${LABEL}" | sed 's/\//_/g') PREFIX=${PREFIX} LABEL=${LABEL} docker/builder.sh --no-cache --file Dockerfile . docker run \ - --name ${PREFIX}_${BUILDNUMBER} \ - --rm \ - --entrypoint=make \ - ${PREFIX}/conch-api:${LABEL} \ - test + --name ${PREFIX}_${BUILDNUMBER} \ + --rm \ + --entrypoint=make \ + ${PREFIX}/conch-api:${LABEL} \ + test diff --git a/docker/builder.sh b/docker/builder.sh index 3094bef68..e52c0591e 100755 --- a/docker/builder.sh +++ b/docker/builder.sh @@ -9,8 +9,8 @@ HASH=`git rev-parse HEAD` LABEL=$(echo "${LABEL}" | sed 's/\//_/g') docker build \ - --force-rm \ - -t ${PREFIX}/conch-api:${LABEL} \ - --build-arg VERSION=${TAG} \ - --build-arg VCS_REF=${HASH} \ - $@ + --force-rm \ + -t ${PREFIX}/conch-api:${LABEL} \ + --build-arg VERSION=${TAG} \ + --build-arg VCS_REF=${HASH} \ + $@ diff --git a/docker/dev_test.bash b/docker/dev_test.bash index bd510b13f..17c4074d8 100755 --- a/docker/dev_test.bash +++ b/docker/dev_test.bash @@ -5,6 +5,6 @@ docker volume create ${PREFIX}-api-test-carton PREFIX=${PREFIX} docker/builder.sh --file Dockerfile.dev . docker run \ - --mount type=volume,src=${PREFIX}-api-test-carton,dst=/app/conch/local \ - --rm \ - ${PREFIX}/conch-api + --mount type=volume,src=${PREFIX}-api-test-carton,dst=/app/conch/local \ + --rm \ + ${PREFIX}/conch-api diff --git a/docs/_resources/full-schema.er b/docs/_resources/full-schema.er index b237ee1a6..c36e35955 100644 --- a/docs/_resources/full-schema.er +++ b/docs/_resources/full-schema.er @@ -11,8 +11,8 @@ # Cardinality Syntax # 0 or 1 ? # exactly 1 1 -# 0 or more * (primary key) -# 1 or more + (foreign key) +# 0 or more * (marks primary key on table declaration) +# 1 or more + (marks foreign key on table declaration) title {label: "Conch database schema (as of v3.0.0-b1, 2019-10-21)", size: "50"} @@ -204,11 +204,6 @@ deactivated *+build_id role {label: "ro, rw, admin"} -[organization_workspace_role] {bgcolor: "#d0e0d0"} -*+organization_id -*+workspace_id -role {label: "ro, rw, admin"} - [rack] {bgcolor: "#fcecec"} *id +datacenter_room_id @@ -247,6 +242,7 @@ deactivated created updated last_seen ++user_id [user_account] {bgcolor: "#d0e0d0"} *id @@ -271,12 +267,6 @@ role {label: "ro, rw, admin"} *+organization_id role {label: "ro, rw, admin"} -[user_relay_connection] {bgcolor: "#d0e0d0"} -*+user_id -*+relay_id -first_seen -last_seen - [user_session_token] {bgcolor: "#d0e0d0"} *id +user_id @@ -378,19 +368,16 @@ hardware_product *--1 validation_plan hardware_product *--1 hardware_vendor organization_build_role *--1 build organization_build_role *--1 organization -organization_workspace_role *--1 organization -organization_workspace_role *--1 workspace rack *--? build rack *--1 datacenter_room rack_layout *--1 hardware_product rack_layout *--1 rack rack *--1 rack_role +relay +--1 user_account user_build_role +--1 build user_build_role *--1 user_account user_organization_role +--1 organization user_organization_role *--1 user_account -user_relay_connection *--1 relay -user_relay_connection *--1 user_account user_session_token *--1 user_account user_setting *--1 user_account user_workspace_role *--1 user_account diff --git a/docs/images/full-schema.png b/docs/images/full-schema.png index 8fc5f0b43..86f4bb283 100644 Binary files a/docs/images/full-schema.png and b/docs/images/full-schema.png differ diff --git a/docs/json-schema/request.json b/docs/json-schema/request.json index fcb157f0a..1fae95d80 100644 --- a/docs/json-schema/request.json +++ b/docs/json-schema/request.json @@ -152,7 +152,7 @@ "$ref" : "common.json#/definitions/device_serial_number" }, "sku" : { - "type" : "string" + "$ref" : "common.json#/definitions/mojo_standard_placeholder" } }, "required" : [ @@ -384,7 +384,17 @@ }, "DeviceSettings" : { "additionalProperties" : { - "$ref" : "common.json#/definitions/non_empty_string" + "anyOf" : [ + { + "$ref" : "common.json#/definitions/non_empty_string" + }, + { + "type" : "number" + }, + { + "type" : "boolean" + } + ] }, "minProperties" : 1, "propertyNames" : { @@ -1108,22 +1118,6 @@ }, "type" : "object" }, - "WorkspaceAddOrganization" : { - "additionalProperties" : false, - "properties" : { - "organization_id" : { - "$ref" : "common.json#/definitions/uuid" - }, - "role" : { - "$ref" : "common.json#/definitions/role" - } - }, - "required" : [ - "organization_id", - "role" - ], - "type" : "object" - }, "WorkspaceAddRack" : { "additionalProperties" : false, "properties" : { diff --git a/docs/json-schema/response.json b/docs/json-schema/response.json index 0194831f6..8ed6303e0 100644 --- a/docs/json-schema/response.json +++ b/docs/json-schema/response.json @@ -597,7 +597,7 @@ "$ref" : "common.json#/definitions/device_serial_number" }, "sku" : { - "type" : "string" + "$ref" : "common.json#/definitions/mojo_standard_placeholder" }, "system_uuid" : { "oneOf" : [ @@ -798,7 +798,7 @@ "$ref" : "common.json#/definitions/device_serial_number" }, "sku" : { - "type" : "string" + "$ref" : "common.json#/definitions/mojo_standard_placeholder" }, "system_uuid" : { "oneOf" : [ @@ -912,7 +912,7 @@ "type" : "string" }, "sku" : { - "type" : "string" + "$ref" : "common.json#/definitions/mojo_standard_placeholder" } }, "required" : [ @@ -1221,7 +1221,7 @@ "$ref" : "common.json#/definitions/uuid" }, "sku" : { - "type" : "string" + "$ref" : "common.json#/definitions/mojo_standard_placeholder" } }, "required" : [ @@ -1836,9 +1836,6 @@ "minItems" : 1, "type" : "array", "uniqueItems" : true - }, - "workspaces" : { - "$ref" : "/definitions/WorkspacesAndRoles" } }, "required" : [ @@ -1847,7 +1844,6 @@ "description", "created", "users", - "workspaces", "builds" ], "type" : "object" @@ -2186,6 +2182,9 @@ "format" : "date-time", "type" : "string" }, + "user_id" : { + "$ref" : "common.json#/definitions/uuid" + }, "version" : { "oneOf" : [ { @@ -2206,7 +2205,8 @@ "ssh_port", "created", "updated", - "last_seen" + "last_seen", + "user_id" ], "type" : "object" }, @@ -2857,9 +2857,6 @@ "role" : { "$ref" : "common.json#/definitions/role" }, - "role_via_organization_id" : { - "$ref" : "common.json#/definitions/uuid" - }, "role_via_workspace_id" : { "$ref" : "common.json#/definitions/uuid" } @@ -2970,53 +2967,6 @@ "type" : "array", "uniqueItems" : true }, - "WorkspaceOrganizations" : { - "items" : { - "additionalProperties" : false, - "properties" : { - "admins" : { - "items" : { - "$ref" : "/definitions/UserTerse" - }, - "minItems" : 1, - "type" : "array", - "uniqueItems" : true - }, - "description" : { - "oneOf" : [ - { - "type" : "null" - }, - { - "type" : "string" - } - ] - }, - "id" : { - "$ref" : "common.json#/definitions/uuid" - }, - "name" : { - "type" : "string" - }, - "role" : { - "$ref" : "common.json#/definitions/role" - }, - "role_via_workspace_id" : { - "$ref" : "common.json#/definitions/uuid" - } - }, - "required" : [ - "id", - "name", - "description", - "role", - "admins" - ], - "type" : "object" - }, - "type" : "array", - "uniqueItems" : true - }, "WorkspaceRackSummary" : { "additionalProperties" : { "items" : { @@ -3157,6 +3107,9 @@ "format" : "date-time", "type" : "string" }, + "user_id" : { + "$ref" : "common.json#/definitions/uuid" + }, "version" : { "oneOf" : [ { @@ -3178,6 +3131,7 @@ "created", "updated", "last_seen", + "user_id", "location", "num_devices" ], @@ -3202,9 +3156,6 @@ "role" : { "$ref" : "common.json#/definitions/role" }, - "role_via_organization_id" : { - "$ref" : "common.json#/definitions/uuid" - }, "role_via_workspace_id" : { "$ref" : "common.json#/definitions/uuid" } diff --git a/docs/modules/Conch::Command::check_workspace_racks.md b/docs/modules/Conch::Command::check_workspace_racks.md index d75f94f3c..5efa4171e 100644 --- a/docs/modules/Conch::Command::check_workspace_racks.md +++ b/docs/modules/Conch::Command::check_workspace_racks.md @@ -15,8 +15,8 @@ bin/conch check_workspace_racks [long options...] # DESCRIPTION For all racks, checks that necessary `workspace_rack` rows exist (for every parent to the -workspace referenced by existing `workspace_rack` entries). Missing rows are populated, -if `--dry-run` not provided. Errors are identified, if `--verbose` is provided. +workspace referenced by existing `workspace_rack` entries). Missing rows are populated, +if `--dry-run` not provided. Errors are identified, if `--verbose` is provided. # EXIT CODE diff --git a/docs/modules/Conch::Controller::DeviceLocation.md b/docs/modules/Conch::Controller::DeviceLocation.md index def7f280c..880849b91 100644 --- a/docs/modules/Conch::Controller::DeviceLocation.md +++ b/docs/modules/Conch::Controller::DeviceLocation.md @@ -6,7 +6,7 @@ Conch::Controller::DeviceLocation ## get -Retrieves location data for the current device. **Note:** This information is not considered to +Retrieves location data for the current device. **Note:** This information is not considered to be canonical if the device is in the 'production' phase or later. Response uses the DeviceLocation json schema. diff --git a/docs/modules/Conch::Controller::Organization.md b/docs/modules/Conch::Controller::Organization.md index cb40b63c7..85e5c82f5 100644 --- a/docs/modules/Conch::Controller::Organization.md +++ b/docs/modules/Conch::Controller::Organization.md @@ -9,11 +9,6 @@ Conch::Controller::Organization If the user is a system admin, retrieve a list of all active organizations in the database; otherwise, limits the list to those organizations of which the user is a member. -Note: the only workspaces and roles listed are those reachable via the organization, even if -the user might have direct access to the workspace at a greater role. For comprehensive -information about what workspaces the user can access, and at what role, please use `GET -/workspace` or `GET /user/me`. - Response uses the Organizations json schema. ## create @@ -36,11 +31,6 @@ continue; otherwise the user must have the 'admin' role. Get the details of a single organization. Requires the 'admin' role on the organization. -Note: the only workspaces and roles listed are those reachable via the organization, even if -the user might have direct access to the workspace at a greater role. For comprehensive -information about what workspaces the user can access, and at what role, please use -`GET /workspace` or `GET /user/me`. - Response uses the Organization json schema. ## update diff --git a/docs/modules/Conch::Controller::WorkspaceOrganization.md b/docs/modules/Conch::Controller::WorkspaceOrganization.md deleted file mode 100644 index 8c004ec6d..000000000 --- a/docs/modules/Conch::Controller::WorkspaceOrganization.md +++ /dev/null @@ -1,41 +0,0 @@ -# NAME - -Conch::Controller::WorkspaceOrganization - -# METHODS - -## list\_workspace\_organizations - -Get a list of organizations for the current workspace. -Requires the 'admin' role on the workspace. - -Response uses the WorkspaceOrganizations json schema. - -## add\_workspace\_organization - -Adds a organization to the current workspace, or upgrades an existing role entry to access the -workspace. -Requires the 'admin' role on the workspace. - -Optionally takes a query parameter `send_mail` (defaulting to true), to send an email -to all organization members and all workspace admins. - -## remove\_workspace\_organization - -Removes the indicated organization from the workspace, as well as all sub-workspaces. -Requires the 'admin' role on the workspace. - -Note this may not have the desired effect if the organization is getting access to the -workspace via a parent workspace. When in doubt, check at `GET -/workspace/:workspace_id/organization`. - -Optionally takes a query parameter `send_mail` (defaulting to true), to send an email -to all organization members and to all workspace admins. - -# LICENSING - -Copyright Joyent, Inc. - -This Source Code Form is subject to the terms of the Mozilla Public License, -v.2.0. If a copy of the MPL was not distributed with this file, You can obtain -one at [http://mozilla.org/MPL/2.0/](http://mozilla.org/MPL/2.0/). diff --git a/docs/modules/Conch::DB::Result::Organization.md b/docs/modules/Conch::DB::Result::Organization.md index 2ef63820f..f0f75f37c 100644 --- a/docs/modules/Conch::DB::Result::Organization.md +++ b/docs/modules/Conch::DB::Result::Organization.md @@ -59,12 +59,6 @@ Type: has\_many Related object: [Conch::DB::Result::OrganizationBuildRole](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AOrganizationBuildRole) -## organization\_workspace\_roles - -Type: has\_many - -Related object: [Conch::DB::Result::OrganizationWorkspaceRole](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AOrganizationWorkspaceRole) - ## user\_organization\_roles Type: has\_many @@ -83,17 +77,11 @@ Type: many\_to\_many Composing rels: ["user\_organization\_roles"](#user_organization_roles) -> user\_account -## workspaces - -Type: many\_to\_many - -Composing rels: ["organization\_workspace\_roles"](#organization_workspace_roles) -> workspace - # METHODS ## TO\_JSON -Include information about the organization's admins, workspaces and builds, if available. +Include information about the organization's admins and builds, if available. # LICENSING diff --git a/docs/modules/Conch::DB::Result::OrganizationWorkspaceRole.md b/docs/modules/Conch::DB::Result::OrganizationWorkspaceRole.md deleted file mode 100644 index e0bae1bbb..000000000 --- a/docs/modules/Conch::DB::Result::OrganizationWorkspaceRole.md +++ /dev/null @@ -1,63 +0,0 @@ -# NAME - -Conch::DB::Result::OrganizationWorkspaceRole - -# BASE CLASS: [Conch::DB::Result](../modules/Conch%3A%3ADB%3A%3AResult) - -# TABLE: `organization_workspace_role` - -# ACCESSORS - -## organization\_id - -``` -data_type: 'uuid' -is_foreign_key: 1 -is_nullable: 0 -size: 16 -``` - -## workspace\_id - -``` -data_type: 'uuid' -is_foreign_key: 1 -is_nullable: 0 -size: 16 -``` - -## role - -``` -data_type: 'enum' -default_value: 'ro' -extra: {custom_type_name => "role_enum",list => ["ro","rw","admin"]} -is_nullable: 0 -``` - -# PRIMARY KEY - -- ["organization\_id"](#organization_id) -- ["workspace\_id"](#workspace_id) - -# RELATIONS - -## organization - -Type: belongs\_to - -Related object: [Conch::DB::Result::Organization](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AOrganization) - -## workspace - -Type: belongs\_to - -Related object: [Conch::DB::Result::Workspace](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AWorkspace) - -# LICENSING - -Copyright Joyent, Inc. - -This Source Code Form is subject to the terms of the Mozilla Public License, -v.2.0. If a copy of the MPL was not distributed with this file, You can obtain -one at [http://mozilla.org/MPL/2.0/](http://mozilla.org/MPL/2.0/). diff --git a/docs/modules/Conch::DB::Result::Relay.md b/docs/modules/Conch::DB::Result::Relay.md index 21d36767f..ee3bb9992 100644 --- a/docs/modules/Conch::DB::Result::Relay.md +++ b/docs/modules/Conch::DB::Result::Relay.md @@ -86,6 +86,15 @@ is_nullable: 0 original: {default_value => \"now()"} ``` +## user\_id + +``` +data_type: 'uuid' +is_foreign_key: 1 +is_nullable: 0 +size: 16 +``` + # PRIMARY KEY - ["id"](#id) @@ -104,11 +113,11 @@ Type: has\_many Related object: [Conch::DB::Result::DeviceRelayConnection](../modules/Conch%3A%3ADB%3A%3AResult%3A%3ADeviceRelayConnection) -## user\_relay\_connections +## user\_account -Type: has\_many +Type: belongs\_to -Related object: [Conch::DB::Result::UserRelayConnection](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AUserRelayConnection) +Related object: [Conch::DB::Result::UserAccount](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AUserAccount) ## devices @@ -116,12 +125,6 @@ Type: many\_to\_many Composing rels: ["device\_relay\_connections"](#device_relay_connections) -> device -## user\_accounts - -Type: many\_to\_many - -Composing rels: ["user\_relay\_connections"](#user_relay_connections) -> user\_account - # LICENSING Copyright Joyent, Inc. diff --git a/docs/modules/Conch::DB::Result::UserAccount.md b/docs/modules/Conch::DB::Result::UserAccount.md index 4829805c1..f0c08d251 100644 --- a/docs/modules/Conch::DB::Result::UserAccount.md +++ b/docs/modules/Conch::DB::Result::UserAccount.md @@ -104,23 +104,23 @@ Type: has\_many Related object: [Conch::DB::Result::Build](../modules/Conch%3A%3ADB%3A%3AResult%3A%3ABuild) -## user\_build\_roles +## relays Type: has\_many -Related object: [Conch::DB::Result::UserBuildRole](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AUserBuildRole) +Related object: [Conch::DB::Result::Relay](../modules/Conch%3A%3ADB%3A%3AResult%3A%3ARelay) -## user\_organization\_roles +## user\_build\_roles Type: has\_many -Related object: [Conch::DB::Result::UserOrganizationRole](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AUserOrganizationRole) +Related object: [Conch::DB::Result::UserBuildRole](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AUserBuildRole) -## user\_relay\_connections +## user\_organization\_roles Type: has\_many -Related object: [Conch::DB::Result::UserRelayConnection](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AUserRelayConnection) +Related object: [Conch::DB::Result::UserOrganizationRole](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AUserOrganizationRole) ## user\_session\_tokens @@ -152,12 +152,6 @@ Type: many\_to\_many Composing rels: ["user\_organization\_roles"](#user_organization_roles) -> organization -## relays - -Type: many\_to\_many - -Composing rels: ["user\_relay\_connections"](#user_relay_connections) -> relay - ## workspaces Type: many\_to\_many diff --git a/docs/modules/Conch::DB::Result::UserRelayConnection.md b/docs/modules/Conch::DB::Result::UserRelayConnection.md deleted file mode 100644 index 991cfd185..000000000 --- a/docs/modules/Conch::DB::Result::UserRelayConnection.md +++ /dev/null @@ -1,72 +0,0 @@ -# NAME - -Conch::DB::Result::UserRelayConnection - -# BASE CLASS: [Conch::DB::Result](../modules/Conch%3A%3ADB%3A%3AResult) - -# TABLE: `user_relay_connection` - -# ACCESSORS - -## user\_id - -``` -data_type: 'uuid' -is_foreign_key: 1 -is_nullable: 0 -size: 16 -``` - -## first\_seen - -``` -data_type: 'timestamp with time zone' -default_value: current_timestamp -is_nullable: 0 -original: {default_value => \"now()"} -``` - -## last\_seen - -``` -data_type: 'timestamp with time zone' -default_value: current_timestamp -is_nullable: 0 -original: {default_value => \"now()"} -``` - -## relay\_id - -``` -data_type: 'uuid' -is_foreign_key: 1 -is_nullable: 0 -size: 16 -``` - -# PRIMARY KEY - -- ["user\_id"](#user_id) -- ["relay\_id"](#relay_id) - -# RELATIONS - -## relay - -Type: belongs\_to - -Related object: [Conch::DB::Result::Relay](../modules/Conch%3A%3ADB%3A%3AResult%3A%3ARelay) - -## user\_account - -Type: belongs\_to - -Related object: [Conch::DB::Result::UserAccount](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AUserAccount) - -# LICENSING - -Copyright Joyent, Inc. - -This Source Code Form is subject to the terms of the Mozilla Public License, -v.2.0. If a copy of the MPL was not distributed with this file, You can obtain -one at [http://mozilla.org/MPL/2.0/](http://mozilla.org/MPL/2.0/). diff --git a/docs/modules/Conch::DB::Result::Workspace.md b/docs/modules/Conch::DB::Result::Workspace.md index bfa571e13..2be7cb4c9 100644 --- a/docs/modules/Conch::DB::Result::Workspace.md +++ b/docs/modules/Conch::DB::Result::Workspace.md @@ -52,12 +52,6 @@ size: 16 # RELATIONS -## organization\_workspace\_roles - -Type: has\_many - -Related object: [Conch::DB::Result::OrganizationWorkspaceRole](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AOrganizationWorkspaceRole) - ## parent\_workspace Type: belongs\_to @@ -82,12 +76,6 @@ Type: has\_many Related object: [Conch::DB::Result::Workspace](../modules/Conch%3A%3ADB%3A%3AResult%3A%3AWorkspace) -## organizations - -Type: many\_to\_many - -Composing rels: ["organization\_workspace\_roles"](#organization_workspace_roles) -> organization - ## racks Type: many\_to\_many @@ -113,11 +101,6 @@ Accessor for informational column, which is by the serializer in the result data Accessor for informational column, which is used by the serializer to signal we should fetch and include inherited role data for the user. -## organization\_id\_for\_role - -Accessor for informational column, which is used by the serializer to signal we should fetch -and include inherited role data for the organization. - # LICENSING Copyright Joyent, Inc. diff --git a/docs/modules/Conch::DB::ResultSet::Build.md b/docs/modules/Conch::DB::ResultSet::Build.md index 026d6d675..852fa15c2 100644 --- a/docs/modules/Conch::DB::ResultSet::Build.md +++ b/docs/modules/Conch::DB::ResultSet::Build.md @@ -10,7 +10,7 @@ Interface to queries involving builds. ## admins -All the 'admin' users for the provided build(s). Pass a true argument to also include all +All the 'admin' users for the provided build(s). Pass a true argument to also include all system admin users in the result. ## with\_user\_role diff --git a/docs/modules/Conch::DB::ResultSet::Device.md b/docs/modules/Conch::DB::ResultSet::Device.md index c19d397fe..d0a11501c 100644 --- a/docs/modules/Conch::DB::ResultSet::Device.md +++ b/docs/modules/Conch::DB::ResultSet::Device.md @@ -17,7 +17,7 @@ parent workspaces. This is a nested query which searches all workspaces and builds in the database, so only use this query when its impact is outweighed by the impact of filtering a large resultset of -devices in the database. (That is, usually you should start with a single device and then +devices in the database. (That is, usually you should start with a single device and then apply `$device_rs->user_has_role($user_id, $role)` to it.) ## user\_has\_role @@ -36,6 +36,9 @@ Restrict results to those that do not have a registered location. Restrict results to those that have sent a device report proxied by a relay registered using the provided user's credentials. +Note: this is not accurate if the relay is now registered to a different user than that which +sent the report. + ## latest\_device\_report Returns a resultset that finds the most recent device report matching the device(s). This is @@ -58,7 +61,7 @@ $self->search(undef, { ## device\_settings\_as\_hash -Returns a hash of all (active) device settings for the specified device(s). (Will return +Returns a hash of all (active) device settings for the specified device(s). (Will return merged results when passed a resultset referencing multiple devices, which is probably not what you want, so don't do that.) diff --git a/docs/modules/Conch::DB::ResultSet::Organization.md b/docs/modules/Conch::DB::ResultSet::Organization.md index 75ac72515..55a8821f5 100644 --- a/docs/modules/Conch::DB::ResultSet::Organization.md +++ b/docs/modules/Conch::DB::ResultSet::Organization.md @@ -10,7 +10,7 @@ Interface to queries involving organizations. ## admins -All the 'admin' users for the provided organization(s). Pass a true argument to also include all +All the 'admin' users for the provided organization(s). Pass a true argument to also include all system admin users in the result. # LICENSING diff --git a/docs/modules/Conch::DB::ResultSet::Workspace.md b/docs/modules/Conch::DB::ResultSet::Workspace.md index 01b645ea9..68707bf46 100644 --- a/docs/modules/Conch::DB::ResultSet::Workspace.md +++ b/docs/modules/Conch::DB::ResultSet::Workspace.md @@ -49,15 +49,14 @@ Query for workspace(s) with an extra field attached to the result, containing in the effective role the user has for the workspace. The indicated role is used directly, with no additional queries done (consequently "role\_via" -will not appear in the serialized data). This is intended to be used in preference to +will not appear in the serialized data). This is intended to be used in preference to ["with\_role\_via\_data\_for\_user"](#with_role_via_data_for_user) when the user is a system admin. ## with\_role\_via\_data\_for\_user Query for workspace(s) with an extra field attached to the query which will signal the -workspace serializer to include the "role", "role\_via\_workspace\_id" and -"role\_via\_organization\_id" columns, containing information about the effective role the user -has for the workspace. +workspace serializer to include the "role" and "role\_via\_workspace\_id" columns, containing +information about the effective role the user has for the workspace. Only one user\_id can be calculated at a time. If you need to generate workspace-and-role data for multiple users at once, you can manually do: @@ -70,25 +69,15 @@ before serializing the workspace object. ## role\_via\_for\_user -For a given workspace\_id and user\_id, find the user\_workspace\_role or -organization\_workspace\_role row that is responsible for providing the user access to the -workspace (the row with the greatest role that is attached to an ancestor workspace). +For a given workspace\_id and user\_id, find the user\_workspace\_role row that is responsible for +providing the user access to the workspace (the row with the greatest role that is attached to +an ancestor workspace). How the role is calculated: - The role on the user\_organization\_role role is **not** used. - The number of workspaces between `$workspace_id` and the workspace attached to the -user\_workspace\_role or organization\_workspace\_role row is **not** used. -- When both a user\_workspace\_role and organization\_workspace\_role row are found with the same -role, the record directly associated with the workspace (if there is one) is preferred; -otherwise, the user\_workspace\_role row is preferred. - -## role\_via\_for\_organization - -For a given workspace\_id and organization\_id, find the organization\_workspace\_role row that is -responsible for providing the organization access to the workspace (the -organization\_workspace\_role with the greatest role that is attached to an ancestor -workspace). +user\_workspace\_role row is **not** used. ## admins @@ -107,9 +96,6 @@ Checks that the provided user\_id has (at least) the specified role in at least the resultset. (Does not search recursively; add `->and_workspaces_above($workspace_id)` to your resultset first, if this is what you want.) -Both direct `user_workspace_role` entries and joined -`user_organization_role` -> `organization_workspace_role` entries are checked. - Returns a boolean. ## \_workspaces\_subquery diff --git a/docs/modules/Conch::Plugin::Rollbar.md b/docs/modules/Conch::Plugin::Rollbar.md index da4e78b78..c00128e73 100644 --- a/docs/modules/Conch::Plugin::Rollbar.md +++ b/docs/modules/Conch::Plugin::Rollbar.md @@ -32,7 +32,7 @@ and therefore other helpers). ## send\_exception\_to\_rollbar Asynchronously send exception details to Rollbar (if the `rollbar` `access_token` is -configured). Returns a unique uuid suitable for logging, to correlate with the Rollbar entry +configured). Returns a unique uuid suitable for logging, to correlate with the Rollbar entry thus created. ## send\_message\_to\_rollbar diff --git a/docs/modules/Test::Conch.md b/docs/modules/Test::Conch.md index 3e48686ee..c8702828f 100644 --- a/docs/modules/Test::Conch.md +++ b/docs/modules/Test::Conch.md @@ -174,7 +174,7 @@ See ["\_generate\_definition" in Test::Conch::Fixtures](../modules/Test%3A%3ACon ## authenticate Authenticates a user in the current test instance. Uses default (superuser) credentials if not -provided. Optionally will bail out of **all** tests on failure. +provided. Optionally will bail out of **all** tests on failure. This will set 'user' in the session (`$t->app->session('user')`), so a token is not needed on subsequent requests. diff --git a/docs/modules/index.md b/docs/modules/index.md index 4c88c8be9..f07c0d30b 100644 --- a/docs/modules/index.md +++ b/docs/modules/index.md @@ -38,7 +38,6 @@ * [Conch::Controller::ValidationState](../modules/Conch::Controller::ValidationState) * [Conch::Controller::Workspace](../modules/Conch::Controller::Workspace) * [Conch::Controller::WorkspaceDevice](../modules/Conch::Controller::WorkspaceDevice) -* [Conch::Controller::WorkspaceOrganization](../modules/Conch::Controller::WorkspaceOrganization) * [Conch::Controller::WorkspaceRack](../modules/Conch::Controller::WorkspaceRack) * [Conch::Controller::WorkspaceRelay](../modules/Conch::Controller::WorkspaceRelay) * [Conch::Controller::WorkspaceUser](../modules/Conch::Controller::WorkspaceUser) @@ -68,7 +67,6 @@ * [Conch::DB::Result::Migration](../modules/Conch::DB::Result::Migration) * [Conch::DB::Result::Organization](../modules/Conch::DB::Result::Organization) * [Conch::DB::Result::OrganizationBuildRole](../modules/Conch::DB::Result::OrganizationBuildRole) -* [Conch::DB::Result::OrganizationWorkspaceRole](../modules/Conch::DB::Result::OrganizationWorkspaceRole) * [Conch::DB::Result::Rack](../modules/Conch::DB::Result::Rack) * [Conch::DB::Result::RackLayout](../modules/Conch::DB::Result::RackLayout) * [Conch::DB::Result::RackRole](../modules/Conch::DB::Result::RackRole) @@ -76,7 +74,6 @@ * [Conch::DB::Result::UserAccount](../modules/Conch::DB::Result::UserAccount) * [Conch::DB::Result::UserBuildRole](../modules/Conch::DB::Result::UserBuildRole) * [Conch::DB::Result::UserOrganizationRole](../modules/Conch::DB::Result::UserOrganizationRole) -* [Conch::DB::Result::UserRelayConnection](../modules/Conch::DB::Result::UserRelayConnection) * [Conch::DB::Result::UserSessionToken](../modules/Conch::DB::Result::UserSessionToken) * [Conch::DB::Result::UserSetting](../modules/Conch::DB::Result::UserSetting) * [Conch::DB::Result::UserWorkspaceRole](../modules/Conch::DB::Result::UserWorkspaceRole) diff --git a/json-schema/request.yaml b/json-schema/request.yaml index da7a3529c..11e842d55 100644 --- a/json-schema/request.yaml +++ b/json-schema/request.yaml @@ -511,7 +511,10 @@ definitions: DeviceSettings: type: object additionalProperties: - $ref: common.yaml#/definitions/non_empty_string + anyOf: + - $ref: common.yaml#/definitions/non_empty_string + - type: number + - type: boolean minProperties: 1 propertyNames: $ref: common.yaml#/definitions/device_setting_key @@ -589,17 +592,6 @@ definitions: $ref: common.yaml#/definitions/email_address role: $ref: common.yaml#/definitions/role - WorkspaceAddOrganization: - type: object - additionalProperties: false - required: - - organization_id - - role - properties: - organization_id: - $ref: common.yaml#/definitions/uuid - role: - $ref: common.yaml#/definitions/role BuildCreate: type: object additionalProperties: false @@ -710,7 +702,7 @@ definitions: - $ref: common.yaml#/definitions/device_asset_tag - type: 'null' sku: - type: string + $ref: common.yaml#/definitions/mojo_standard_placeholder links: type: array uniqueItems: true diff --git a/json-schema/response.yaml b/json-schema/response.yaml index 1428fc987..7e6cc493c 100644 --- a/json-schema/response.yaml +++ b/json-schema/response.yaml @@ -133,7 +133,7 @@ definitions: hardware_product_id: $ref: common.yaml#/definitions/uuid sku: - type: string + $ref: common.yaml#/definitions/mojo_standard_placeholder health: $ref: common.yaml#/definitions/device_health hostname: @@ -363,7 +363,7 @@ definitions: hardware_product_id: $ref: common.yaml#/definitions/uuid sku: - type: string + $ref: common.yaml#/definitions/mojo_standard_placeholder health: $ref: common.yaml#/definitions/device_health hostname: @@ -573,7 +573,7 @@ definitions: hardware_product_id: $ref: common.yaml#/definitions/uuid sku: - type: string + $ref: common.yaml#/definitions/mojo_standard_placeholder WorkspaceDevicePXEs: type: array uniqueItems: true @@ -673,7 +673,7 @@ definitions: description: Hardware product alias type: string sku: - type: string + $ref: common.yaml#/definitions/mojo_standard_placeholder hardware_vendor_id: $ref: common.yaml#/definitions/uuid DeviceSetting: @@ -1085,6 +1085,7 @@ definitions: - created - updated - last_seen + - user_id - location - num_devices properties: @@ -1117,6 +1118,8 @@ definitions: last_seen: type: string format: date-time + user_id: + $ref: common.yaml#/definitions/uuid location: $comment: location should not be considered canonical when device 'phase' is 'production' or later description: Last reported location of the relay @@ -1167,8 +1170,6 @@ definitions: $ref: common.yaml#/definitions/role role_via_workspace_id: $ref: common.yaml#/definitions/uuid - role_via_organization_id: - $ref: common.yaml#/definitions/uuid WorkspacesAndRoles: type: array uniqueItems: true @@ -1196,8 +1197,6 @@ definitions: $ref: common.yaml#/definitions/role role_via_workspace_id: $ref: common.yaml#/definitions/uuid - role_via_organization_id: - $ref: common.yaml#/definitions/uuid Datacenters: type: array uniqueItems: true @@ -1286,6 +1285,7 @@ definitions: - created - updated - last_seen + - user_id properties: id: $ref: common.yaml#/definitions/uuid @@ -1316,6 +1316,8 @@ definitions: oneOf: - type: string - type: 'null' + user_id: + $ref: common.yaml#/definitions/uuid Version: type: object additionalProperties: false @@ -1800,7 +1802,6 @@ definitions: - description - created - users - - workspaces - builds properties: id: @@ -1835,8 +1836,6 @@ definitions: $ref: common.yaml#/definitions/email_address role: $ref: common.yaml#/definitions/role - workspaces: - $ref: /definitions/WorkspacesAndRoles builds: type: array uniqueItems: true @@ -1864,37 +1863,6 @@ definitions: uniqueItems: true items: $ref: /definitions/Organization - WorkspaceOrganizations: - type: array - uniqueItems: true - items: - type: object - additionalProperties: false - required: - - id - - name - - description - - role - - admins - properties: - id: - $ref: common.yaml#/definitions/uuid - name: - type: string - description: - oneOf: - - type: 'null' - - type: string - role: - $ref: common.yaml#/definitions/role - role_via_workspace_id: - $ref: common.yaml#/definitions/uuid - admins: - type: array - uniqueItems: true - minItems: 1 - items: - $ref: /definitions/UserTerse Build: type: object additionalProperties: false diff --git a/lib/Conch.pm b/lib/Conch.pm index 5bd01815e..779b5c026 100644 --- a/lib/Conch.pm +++ b/lib/Conch.pm @@ -110,7 +110,7 @@ Helper method for setting the response status code and json content. $payload //= { error => 'Unauthorized' } if $code == 401; $payload //= { error => 'Forbidden' } if $code == 403; - $payload //= { error => 'Not Found' } if $code == 404; + $payload //= { error => 'Entity Not Found' } if $code == 404; $payload //= { error => 'Unimplemented' } if $code == 501; if (not $payload) { diff --git a/lib/Conch/Command/check_workspace_racks.pm b/lib/Conch/Command/check_workspace_racks.pm index ef57fb55a..4b9f268d7 100644 --- a/lib/Conch/Command/check_workspace_racks.pm +++ b/lib/Conch/Command/check_workspace_racks.pm @@ -17,8 +17,8 @@ check_workspace_racks - Utility to check all workspace_rack entries are correct =head1 DESCRIPTION For all racks, checks that necessary C rows exist (for every parent to the -workspace referenced by existing C entries). Missing rows are populated, -if C<--dry-run> not provided. Errors are identified, if C<--verbose> is provided. +workspace referenced by existing C entries). Missing rows are populated, +if C<--dry-run> not provided. Errors are identified, if C<--verbose> is provided. =head1 EXIT CODE diff --git a/lib/Conch/Controller/Build.pm b/lib/Conch/Controller/Build.pm index 874331924..85ffd9d15 100644 --- a/lib/Conch/Controller/Build.pm +++ b/lib/Conch/Controller/Build.pm @@ -459,7 +459,6 @@ to all organization members and all build admins. sub add_organization ($c) { # Note: this method is very similar to Conch::Controller::WorkspaceUser::add_user - # and Conch::Controller::WorkspaceOrganization::add_workspace_organization my $params = $c->validate_query_params('NotifyUsers'); return if not $params; diff --git a/lib/Conch/Controller/DeviceLocation.pm b/lib/Conch/Controller/DeviceLocation.pm index 5fb3449aa..e4a48e5d3 100644 --- a/lib/Conch/Controller/DeviceLocation.pm +++ b/lib/Conch/Controller/DeviceLocation.pm @@ -12,7 +12,7 @@ Conch::Controller::DeviceLocation =head2 get -Retrieves location data for the current device. B This information is not considered to +Retrieves location data for the current device. B This information is not considered to be canonical if the device is in the 'production' phase or later. Response uses the DeviceLocation json schema. diff --git a/lib/Conch/Controller/DeviceReport.pm b/lib/Conch/Controller/DeviceReport.pm index e29411e0f..7755b0e7f 100644 --- a/lib/Conch/Controller/DeviceReport.pm +++ b/lib/Conch/Controller/DeviceReport.pm @@ -37,8 +37,11 @@ sub process ($c) { return $c->status(409, { error => 'Could not find hardware product with sku '.$unserialized_report->{sku} }) if not $hardware_product_id; if ($unserialized_report->{relay} and my $relay_serial = $unserialized_report->{relay}{serial}) { + my $relay_rs = $c->db_relays->active->search({ serial_number => $relay_serial }); return $c->status(409, { error => 'relay serial '.$relay_serial.' is not registered' }) - if not $c->db_relays->active->search({ serial_number => $relay_serial })->exists; + if not $relay_rs->exists; + return $c->status(409, { error => 'relay serial '.$relay_serial.' is not registered by user '.$c->stash('user')->name }) + if not $relay_rs->search({ user_id => $c->stash('user_id') })->exists; } my $device = $c->db_devices->find({ serial_number => $c->stash('device_serial_number') }); @@ -47,12 +50,6 @@ sub process ($c) { return $c->status(404); } - if ($hardware_product_id ne $device->hardware_product_id) { - $device->health('error'); - $device->update({ updated => \'now()' }) if $device->is_changed; - return $c->status(409, { error => 'Report sku does not match expected hardware_product for device '.$c->stash('device_serial_number') }); - } - if ($device->phase eq 'decommissioned') { $c->log->error('report submitted for decommissioned device '.$c->stash('device_serial_number')); return $c->status(409, { error => 'device is decommissioned' }); @@ -73,6 +70,7 @@ sub process ($c) { my $prev_uptime = $device->uptime_since; $c->txn_wrapper(sub ($c) { $device->update({ + hardware_product_id => $hardware_product_id, system_uuid => $unserialized_report->{system_uuid}, last_seen => \'now()', exists $unserialized_report->{uptime_since} ? ( uptime_since => $unserialized_report->{uptime_since} ) : (), diff --git a/lib/Conch/Controller/Login.pm b/lib/Conch/Controller/Login.pm index a8ffbdf0f..bd98c6d34 100644 --- a/lib/Conch/Controller/Login.pm +++ b/lib/Conch/Controller/Login.pm @@ -38,6 +38,9 @@ sub _respond_with_jwt ($c, $user_id, $expires_delta = undef) { ); return if $c->res->code; + + $c->res->headers->last_modified(Mojo::Date->new($session_token->created->epoch)); + $c->res->headers->expires(Mojo::Date->new($session_token->expires->epoch)); return $c->status(200, { jwt_token => $jwt }); } @@ -220,6 +223,8 @@ sub session_login ($c) { ->search({ user_id => $c->stash('user_id') }) ->search(\[ '(expires - now()) >= (now() - created)' ]); if (my $token = $token_rs->order_by({ -desc => 'created' })->rows(1)->single) { + $c->res->headers->last_modified(Mojo::Date->new($token->created->epoch)); + $c->res->headers->expires(Mojo::Date->new($token->expires->epoch)); return $c->status(200, { jwt_token => $c->generate_jwt_from_token($token) }); } diff --git a/lib/Conch/Controller/Organization.pm b/lib/Conch/Controller/Organization.pm index b08864268..b9e58879e 100644 --- a/lib/Conch/Controller/Organization.pm +++ b/lib/Conch/Controller/Organization.pm @@ -17,11 +17,6 @@ Conch::Controller::Organization If the user is a system admin, retrieve a list of all active organizations in the database; otherwise, limits the list to those organizations of which the user is a member. -Note: the only workspaces and roles listed are those reachable via the organization, even if -the user might have direct access to the workspace at a greater role. For comprehensive -information about what workspaces the user can access, and at what role, please use C or C. - Response uses the Organizations json schema. =cut @@ -31,10 +26,9 @@ sub list ($c) { ->active ->prefetch({ user_organization_roles => 'user_account', - organization_workspace_roles => 'workspace', organization_build_roles => 'build', }) - ->order_by([ map $_.'.name', qw(organization user_account workspace build) ]); + ->order_by([ map $_.'.name', qw(organization user_account build) ]); return $c->status(200, [ $rs->all ]) if $c->is_system_admin; @@ -44,23 +38,7 @@ sub list ($c) { ->get_column('organization_id')->as_query } }); - my @data = map $_->TO_JSON, $rs->all; - my %workspace_ids; - @workspace_ids{map $_->{id}, $_->{workspaces}->@*} = () foreach @data; - - foreach my $org (@data) { - foreach my $ws ($org->{workspaces}->@*) { - undef $ws->{parent_workspace_id} - if $ws->{parent_workspace_id} - and not exists $workspace_ids{$ws->{parent_workspace_id}} - and not $c->db_workspaces - ->and_workspaces_above($ws->{parent_workspace_id}) - ->related_resultset('user_workspace_roles') - ->exists; - } - } - - $c->status(200, \@data); + $c->status(200, [ $rs->all ]); } =head2 create @@ -146,11 +124,6 @@ sub find_organization ($c) { Get the details of a single organization. Requires the 'admin' role on the organization. -Note: the only workspaces and roles listed are those reachable via the organization, even if -the user might have direct access to the workspace at a greater role. For comprehensive -information about what workspaces the user can access, and at what role, please use -C or C. - Response uses the Organization json schema. =cut @@ -159,26 +132,11 @@ sub get ($c) { my $rs = $c->stash('organization_rs') ->prefetch({ user_organization_roles => 'user_account', - organization_workspace_roles => 'workspace', organization_build_roles => 'build', }) - ->order_by([ map $_.'.name', qw(user_account workspace build) ]); - - return $c->status(200, ($rs->all)[0]) if $c->is_system_admin; - - my $org_data = ($rs->all)[0]->TO_JSON; - my %workspace_ids; @workspace_ids{map $_->{id}, $org_data->{workspaces}->@*} = (); - foreach my $ws ($org_data->{workspaces}->@*) { - undef $ws->{parent_workspace_id} - if $ws->{parent_workspace_id} - and not exists $workspace_ids{$ws->{parent_workspace_id}} - and not $c->db_workspaces - ->and_workspaces_above($ws->{parent_workspace_id}) - ->related_resultset('user_workspace_roles') - ->exists; - } + ->order_by([ map $_.'.name', qw(user_account build) ]); - return $c->status(200, $org_data); + return $c->status(200, ($rs->all)[0]); } =head2 update @@ -215,24 +173,15 @@ sub delete ($c) { ->related_resultset('user_organization_roles') ->delete; - my $direct_workspaces_rs = $c->stash('organization_rs') - ->related_resultset('organization_workspace_roles') - ->get_column('workspace_id'); - my $workspace_count = $c->db_workspaces - ->and_workspaces_beneath($direct_workspaces_rs->as_query) - ->count; - my $build_count = 0+$c->stash('organization_rs') ->related_resultset('organization_build_roles') ->delete; - $c->stash('organization_rs')->related_resultset('organization_workspace_roles')->delete; $c->stash('organization_rs')->deactivate; $c->log->debug('Deactivated organization '.$c->stash('organization_id_or_name') .', removing '.$user_count.' user memberships' - .' and removing from '.$workspace_count.' workspaces' - .' and '.$build_count.' builds'); + .' and removing from '.$build_count.' builds'); return $c->status(204); } diff --git a/lib/Conch/Controller/Relay.pm b/lib/Conch/Controller/Relay.pm index 0090aba2c..e18f45f35 100644 --- a/lib/Conch/Controller/Relay.pm +++ b/lib/Conch/Controller/Relay.pm @@ -27,7 +27,11 @@ sub register ($c) { if $c->stash('relay_serial_number') ne $input->{serial}; my $relay = $c->db_relays->find_or_new({ serial_number => delete $input->{serial} }); - $relay->set_columns({ $input->%*, deactivated => undef }); + $relay->set_columns({ + $input->%*, + user_id => $c->stash('user_id'), + deactivated => undef, + }); if (not $relay->in_storage) { $relay->insert; @@ -39,11 +43,6 @@ sub register ($c) { $c->status(204); } - $relay->update_or_create_related('user_relay_connections', { - user_id => $c->stash('user_id'), - last_seen => \'now()', - }); - $c->res->headers->location($c->url_for('/relay/'.$relay->id)); return; } @@ -84,7 +83,7 @@ sub find_relay ($c) { return $c->status(410) if not $rs->exists; if (not $c->is_system_admin) { - if (not $rs->search({ user_id => $c->stash('user_id') }, { join => 'user_relay_connections' })->exists) { + if (not $rs->search({ user_id => $c->stash('user_id') })->exists) { $c->log->debug('User cannot access unregistered relay '.$identifier); return $c->status(403); } @@ -115,12 +114,10 @@ sub delete ($c) { my $rs = $c->stash('relay_rs'); my $drc_count = $rs->related_resultset('device_relay_connections')->delete; - my $urc_count = $rs->related_resultset('user_relay_connections')->delete; $rs->deactivate; $c->log->debug('Deactivated relay '.$c->stash('relay_id_or_serial_number') - .', removing '.$drc_count.' associated device connections and ' - .$urc_count.' associated user connections'); + .', removing '.$drc_count.' associated device connections'); return $c->status(204); } diff --git a/lib/Conch/Controller/User.pm b/lib/Conch/Controller/User.pm index f3e76f19a..23104d4df 100644 --- a/lib/Conch/Controller/User.pm +++ b/lib/Conch/Controller/User.pm @@ -342,7 +342,6 @@ sub get ($c) { ->prefetch({ user_workspace_roles => 'workspace', user_organization_roles => { organization => { - organization_workspace_roles => 'workspace', organization_build_roles => 'build', } }, user_build_roles => 'build', @@ -441,7 +440,6 @@ sub list ($c) { ->prefetch({ user_workspace_roles => 'workspace', user_organization_roles => { organization => { - organization_workspace_roles => 'workspace', organization_build_roles => 'build', } }, user_build_roles => 'build', @@ -615,6 +613,8 @@ sub create_api_token ($c) { my ($token, $jwt) = $c->generate_jwt($user->id, $expires_abs, $input->{name}); return if $c->res->code; + $c->res->headers->last_modified(Mojo::Date->new($token->created->epoch)); + $c->res->headers->expires(Mojo::Date->new($token->expires->epoch)); $c->res->headers->location($c->url_for('/user/' .($user->id eq $c->stash('user_id') ? 'me' : $user->id) .'/token/'.$input->{name})); diff --git a/lib/Conch/Controller/Workspace.pm b/lib/Conch/Controller/Workspace.pm index b9a0505a7..eac6255c2 100644 --- a/lib/Conch/Controller/Workspace.pm +++ b/lib/Conch/Controller/Workspace.pm @@ -87,15 +87,7 @@ sub list ($c) { ->related_resultset('user_workspace_roles') ->related_resultset('workspace'); - my $organization_workspaces_rs = $c->stash('user') - ->related_resultset('user_organization_roles') - ->related_resultset('organization') - ->related_resultset('organization_workspace_roles') - ->related_resultset('workspace'); - - my $direct_workspace_ids_rs = $user_workspaces_rs->union_all($organization_workspaces_rs) - ->distinct - ->get_column('id'); + my $direct_workspace_ids_rs = $user_workspaces_rs->get_column('id'); my @data = $c->db_workspaces ->and_workspaces_beneath($direct_workspace_ids_rs) diff --git a/lib/Conch/Controller/WorkspaceOrganization.pm b/lib/Conch/Controller/WorkspaceOrganization.pm deleted file mode 100644 index 45d8e20b0..000000000 --- a/lib/Conch/Controller/WorkspaceOrganization.pm +++ /dev/null @@ -1,264 +0,0 @@ -package Conch::Controller::WorkspaceOrganization; - -use Mojo::Base 'Mojolicious::Controller', -signatures; - -=pod - -=head1 NAME - -Conch::Controller::WorkspaceOrganization - -=head1 METHODS - -=head2 list_workspace_organizations - -Get a list of organizations for the current workspace. -Requires the 'admin' role on the workspace. - -Response uses the WorkspaceOrganizations json schema. - -=cut - -sub list_workspace_organizations ($c) { - my $workspace_id = $c->stash('workspace_id'); - - # organizations which can access any ancestor of this workspace - my $rs = $c->db_workspaces - ->and_workspaces_above($workspace_id) - ->related_resultset('organization_workspace_roles') - ->search_related('organization', - { 'user_organization_roles.role' => 'admin' }, - { join => { user_organization_roles => 'user_account' }, collapse => 1 }, - ) - ->active - ->columns([ - (map 'organization.'.$_, qw(id name description)), - (map 'user_organization_roles.'.$_, qw(organization_id user_id)), - { map +('user_organization_roles.user_account.'.$_ => 'user_account.'.$_), qw(id name email) }, - ]) - ->order_by(['organization.name', 'user_account.name']) - ->hri; - - my $org_data = [ - map { - my $org = $_; - my $role_via = $c->db_workspaces->role_via_for_organization($workspace_id, $org->{id}); - +{ - role => $role_via->role, - $role_via->workspace_id ne $workspace_id ? ( role_via_workspace_id => $role_via->workspace_id ) : (), - admins => [ map $_->{user_account}, (delete $org->{user_organization_roles})->@* ], - $org->%*, - } - } - $rs->all - ]; - - $c->log->debug('Found '.scalar($org_data->@*).' organizations'); - $c->status(200, $org_data); -} - -=head2 add_workspace_organization - -Adds a organization to the current workspace, or upgrades an existing role entry to access the -workspace. -Requires the 'admin' role on the workspace. - -Optionally takes a query parameter C (defaulting to true), to send an email -to all organization members and all workspace admins. - -=cut - -sub add_workspace_organization ($c) { - # Note: this method is very similar to Conch::Controller::WorkspaceUser::add_user - - my $params = $c->validate_query_params('NotifyUsers'); - return if not $params; - - my $input = $c->validate_request('WorkspaceAddOrganization'); - return if not $input; - - my $organization = $c->db_organizations->active->find($input->{organization_id}); - return $c->status(404) if not $organization; - - my $workspace_id = $c->stash('workspace_id'); - - # check if the organization already has access to this workspace - if (my $existing_role_via = $c->db_workspaces - ->role_via_for_organization($workspace_id, $organization->id)) { - if ((my $role_cmp = $existing_role_via->role_cmp($input->{role})) >= 0) { - my $str = 'organization "'.$organization->name.'" already has '.$existing_role_via->role - .' access to workspace '.$workspace_id - .($existing_role_via->workspace_id ne $workspace_id - ? (' via workspace '.$existing_role_via->workspace_id) : ''); - - $c->log->debug($str.': nothing to do'), return $c->status(204) - if $role_cmp == 0; - - return $c->status(409, { error => $str.': cannot downgrade role to '.$input->{role} }) - if $role_cmp > 0; - } - - my $rs = $organization->search_related('organization_workspace_roles', - { workspace_id => $workspace_id }); - if ($rs->exists) { - $rs->update({ role => $input->{role} }); - } - else { - $organization->create_related('organization_workspace_roles', { - workspace_id => $workspace_id, - role => $input->{role}, - }); - } - - $c->log->info('Upgraded organization '.$organization->id.' in workspace '.$workspace_id.' to '.$input->{role}); - - my $workspace_name = $c->stash('workspace_name') // $c->stash('workspace_rs')->get_column('name')->single; - if ($params->{send_mail} // 1) { - $c->send_mail( - template_file => 'workspace_organization_update_members', - To => $c->construct_address_list($organization->user_accounts->order_by('user_account.name')), - From => 'noreply@'.$c->host, - Subject => 'Your Conch access has changed', - organization => $organization->name, - workspace => $workspace_name, - role => $input->{role}, - ); - my @workspace_admins = $c->db_workspaces - ->and_workspaces_above($workspace_id) - ->admins('with_sysadmins') - ->search({ - 'user_account.id' => { -not_in => $organization - ->related_resultset('user_organization_roles') - ->get_column('user_id') - ->as_query }, - }); - $c->send_mail( - template_file => 'workspace_organization_update_admins', - To => $c->construct_address_list(@workspace_admins), - From => 'noreply@'.$c->host, - Subject => 'We modified an organization\'s access to your workspace', - organization => $organization->name, - workspace => $workspace_name, - role => $input->{role}, - ) if @workspace_admins; - } - - return $c->status(204); - } - - $organization->create_related('organization_workspace_roles', { - workspace_id => $workspace_id, - role => $input->{role}, - }); - $c->log->info('Added organization '.$organization->id.' to workspace '.$workspace_id.' with the '.$input->{role}.' role'); - - if ($params->{send_mail} // 1) { - my $workspace_name = $c->stash('workspace_name') // $c->stash('workspace_rs')->get_column('name')->single; - $c->send_mail( - template_file => 'workspace_organization_add_members', - To => $c->construct_address_list($organization->user_accounts->order_by('user_account.name')), - From => 'noreply@'.$c->host, - Subject => 'Your Conch access has changed', - organization => $organization->name, - workspace => $workspace_name, - role => $input->{role}, - ); - my @workspace_admins = $c->db_workspaces - ->and_workspaces_above($workspace_id) - ->admins('with_sysadmins') - ->search({ - 'user_account.id' => { -not_in => $organization - ->related_resultset('user_organization_roles') - ->get_column('user_id') - ->as_query }, - }); - $c->send_mail( - template_file => 'workspace_organization_add_admins', - To => $c->construct_address_list(@workspace_admins), - From => 'noreply@'.$c->host, - Subject => 'We added an organization to your workspace', - organization => $organization->name, - workspace => $workspace_name, - role => $input->{role}, - ) if @workspace_admins; - } - - $c->status(204); -} - -=head2 remove_workspace_organization - -Removes the indicated organization from the workspace, as well as all sub-workspaces. -Requires the 'admin' role on the workspace. - -Note this may not have the desired effect if the organization is getting access to the -workspace via a parent workspace. When in doubt, check at C<< GET -/workspace/:workspace_id/organization >>. - -Optionally takes a query parameter C (defaulting to true), to send an email -to all organization members and to all workspace admins. - -=cut - -sub remove_workspace_organization ($c) { - # Note: this method is very similar to Conch::Controller::WorkspaceUser::remove - - my $params = $c->validate_query_params('NotifyUsers'); - return if not $params; - - my $organization = $c->stash('organization_rs')->single; - - my $rs = $c->db_workspaces - ->and_workspaces_beneath($c->stash('workspace_id')) - ->search_related('organization_workspace_roles', { organization_id => $organization->id }); - - my $num_rows = $rs->count; - return $c->status(204) if not $num_rows; - - my $workspace_name = $c->stash('workspace_name') // $c->stash('workspace_rs')->get_column('name')->single; - $c->log->debug('removing organization '.$organization->name.' from workspace ' - .$workspace_name.' and all sub-workspaces ('.$num_rows.'rows in total)'); - - $rs->delete; - - if ($params->{send_mail} // 1) { - $c->send_mail( - template_file => 'workspace_organization_remove_members', - To => $c->construct_address_list($organization->user_accounts->order_by('user_account.name')), - From => 'noreply@'.$c->host, - Subject => 'Your Conch workspaces have been updated', - organization => $organization->name, - workspace => $workspace_name, - ); - my @workspace_admins = $c->db_workspaces - ->and_workspaces_above($c->stash('workspace_id')) - ->admins('with_sysadmins') - ->search({ 'user_account.id' => { -not_in => $organization->user_accounts->get_column('id')->as_query } }); - $c->send_mail( - template_file => 'workspace_organization_remove_admins', - To => $c->construct_address_list(@workspace_admins), - From => 'noreply@'.$c->host, - Subject => 'We removed an organization from your workspace', - organization => $organization->name, - workspace => $workspace_name, - ) if @workspace_admins; - } - - return $c->status(204); -} - -1; -__END__ - -=pod - -=head1 LICENSING - -Copyright Joyent, Inc. - -This Source Code Form is subject to the terms of the Mozilla Public License, -v.2.0. If a copy of the MPL was not distributed with this file, You can obtain -one at L. - -=cut -# vim: set ts=4 sts=4 sw=4 et : diff --git a/lib/Conch/Controller/WorkspaceUser.pm b/lib/Conch/Controller/WorkspaceUser.pm index cdaa83426..28d582558 100644 --- a/lib/Conch/Controller/WorkspaceUser.pm +++ b/lib/Conch/Controller/WorkspaceUser.pm @@ -23,20 +23,10 @@ sub list ($c) { my $workspace_id = $c->stash('workspace_id'); # users who can access any ancestor of this workspace (directly) - my $direct_users_rs = $c->db_workspaces + my $users_rs = $c->db_workspaces ->and_workspaces_above($workspace_id) ->related_resultset('user_workspace_roles') - ->related_resultset('user_account'); - - # users who can access any ancestor of this workspace (through an organization) - my $organization_users_rs = $c->db_workspaces - ->and_workspaces_above($workspace_id) - ->related_resultset('organization_workspace_roles') - ->related_resultset('organization') - ->related_resultset('user_organization_roles') - ->related_resultset('user_account'); - - my $users_rs = $direct_users_rs->union_all($organization_users_rs) + ->related_resultset('user_account') ->active ->distinct ->order_by('user_account.name') @@ -51,7 +41,6 @@ sub list ($c) { role => $role_via->role, $role_via->workspace_id ne $workspace_id ? ( role_via_workspace_id => $role_via->workspace_id ) : (), - $role_via->can('organization_id') ? ( role_via_organization_id => $role_via->organization_id ) : (), } } $users_rs->all @@ -90,18 +79,15 @@ sub add_user ($c) { $c->stash('target_user', $user); my $workspace_id = $c->stash('workspace_id'); - # check if the user already has access to this workspace (whether directly, through a - # parent workspace, through an organization etc) + # check if the user already has access to this workspace (whether directly or through a + # parent workspace etc) if (my $existing_role_via = $c->db_workspaces ->role_via_for_user($workspace_id, $user->id)) { if ((my $role_cmp = $existing_role_via->role_cmp($input->{role})) >= 0) { my $str = 'user '.$user->name.' already has '.$existing_role_via->role .' access to workspace '.$workspace_id; - my $str2 = join(' and', - ($existing_role_via->workspace_id ne $workspace_id - ? (' workspace '.$existing_role_via->workspace_id) : ()), - ($existing_role_via->can('organization_id') - ? (' organization '.$existing_role_via->organization_id) : ())); + my $str2 = $existing_role_via->workspace_id ne $workspace_id + ? (' workspace '.$existing_role_via->workspace_id) : ''; $str .= ' via'.$str2 if $str2; $c->log->debug($str.': nothing to do'), return $c->status(204) diff --git a/lib/Conch/DB/Result/DeviceRelayConnection.pm b/lib/Conch/DB/Result/DeviceRelayConnection.pm index f8fadc782..2e1a842ba 100644 --- a/lib/Conch/DB/Result/DeviceRelayConnection.pm +++ b/lib/Conch/DB/Result/DeviceRelayConnection.pm @@ -129,13 +129,6 @@ __PACKAGE__->belongs_to( # Created by DBIx::Class::Schema::Loader v0.07049 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:xo1APS+oLWoO+HXXniyYQw -__PACKAGE__->has_many( - "user_relay_connections", - "Conch::DB::Result::UserRelayConnection", - { "foreign.relay_id" => "self.relay_id" }, - { on_delete => "NO ACTION", on_update => "NO ACTION" }, -); - 1; __END__ diff --git a/lib/Conch/DB/Result/Organization.pm b/lib/Conch/DB/Result/Organization.pm index b7670636c..97a769b63 100644 --- a/lib/Conch/DB/Result/Organization.pm +++ b/lib/Conch/DB/Result/Organization.pm @@ -111,21 +111,6 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 organization_workspace_roles - -Type: has_many - -Related object: L - -=cut - -__PACKAGE__->has_many( - "organization_workspace_roles", - "Conch::DB::Result::OrganizationWorkspaceRole", - { "foreign.organization_id" => "self.id" }, - { cascade_copy => 0, cascade_delete => 0 }, -); - =head2 user_organization_roles Type: has_many @@ -161,19 +146,9 @@ Composing rels: L -> user_account __PACKAGE__->many_to_many("user_accounts", "user_organization_roles", "user_account"); -=head2 workspaces - -Type: many_to_many - -Composing rels: L -> workspace - -=cut - -__PACKAGE__->many_to_many("workspaces", "organization_workspace_roles", "workspace"); - # Created by DBIx::Class::Schema::Loader v0.07049 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dyt9DMzLJ4omZdla+GBHiQ +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:25Kvlbfpw7/C1PDx/1tyRQ __PACKAGE__->add_columns( '+deactivated' => { is_serializable => 0 }, @@ -185,7 +160,7 @@ use experimental 'signatures'; =head2 TO_JSON -Include information about the organization's admins, workspaces and builds, if available. +Include information about the organization's admins and builds, if available. =cut @@ -203,34 +178,6 @@ sub TO_JSON ($self) { $self->related_resultset('user_organization_roles')->get_cache->@* ]; - # add workspace data (very similar to Conch::DB::Result::UserAccount::TO_JSON) - my $cached_owrs = $self->related_resultset('organization_workspace_roles')->get_cache; - my %seen_workspaces; - $data->{workspaces} = [ - # we process the direct owr+workspace entries first so we do not produce redundant rows - (map { - my $workspace = $_->workspace; - ++$seen_workspaces{$workspace->id}; - +{ - $workspace->TO_JSON->%*, - role => $_->role, - }, - } $cached_owrs->@*), - - (map +( - map +( - # $_ is a workspace where the organization inherits a role - $seen_workspaces{$_->id} ? () : do { - ++$seen_workspaces{$_->id}; - # instruct the workspace serializer to fill in the role fields - $_->organization_id_for_role($self->id); - $_->TO_JSON - } - ), $self->result_source->schema->resultset('workspace') - ->workspaces_beneath($_->workspace_id) - ), $cached_owrs->@*), - ]; - my $cached_obrs = $self->related_resultset('organization_build_roles')->get_cache; $data->{builds} = [ (map { diff --git a/lib/Conch/DB/Result/OrganizationWorkspaceRole.pm b/lib/Conch/DB/Result/OrganizationWorkspaceRole.pm deleted file mode 100644 index 2548b80d6..000000000 --- a/lib/Conch/DB/Result/OrganizationWorkspaceRole.pm +++ /dev/null @@ -1,134 +0,0 @@ -use utf8; -package Conch::DB::Result::OrganizationWorkspaceRole; - -# Created by DBIx::Class::Schema::Loader -# DO NOT MODIFY THE FIRST PART OF THIS FILE - -=head1 NAME - -Conch::DB::Result::OrganizationWorkspaceRole - -=cut - -use strict; -use warnings; - - -=head1 BASE CLASS: L - -=cut - -use base 'Conch::DB::Result'; - -=head1 TABLE: C - -=cut - -__PACKAGE__->table("organization_workspace_role"); - -=head1 ACCESSORS - -=head2 organization_id - - data_type: 'uuid' - is_foreign_key: 1 - is_nullable: 0 - size: 16 - -=head2 workspace_id - - data_type: 'uuid' - is_foreign_key: 1 - is_nullable: 0 - size: 16 - -=head2 role - - data_type: 'enum' - default_value: 'ro' - extra: {custom_type_name => "role_enum",list => ["ro","rw","admin"]} - is_nullable: 0 - -=cut - -__PACKAGE__->add_columns( - "organization_id", - { data_type => "uuid", is_foreign_key => 1, is_nullable => 0, size => 16 }, - "workspace_id", - { data_type => "uuid", is_foreign_key => 1, is_nullable => 0, size => 16 }, - "role", - { - data_type => "enum", - default_value => "ro", - extra => { custom_type_name => "role_enum", list => ["ro", "rw", "admin"] }, - is_nullable => 0, - }, -); - -=head1 PRIMARY KEY - -=over 4 - -=item * L - -=item * L - -=back - -=cut - -__PACKAGE__->set_primary_key("organization_id", "workspace_id"); - -=head1 RELATIONS - -=head2 organization - -Type: belongs_to - -Related object: L - -=cut - -__PACKAGE__->belongs_to( - "organization", - "Conch::DB::Result::Organization", - { id => "organization_id" }, - { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, -); - -=head2 workspace - -Type: belongs_to - -Related object: L - -=cut - -__PACKAGE__->belongs_to( - "workspace", - "Conch::DB::Result::Workspace", - { id => "workspace_id" }, - { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, -); - - -# Created by DBIx::Class::Schema::Loader v0.07049 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZZaSreTpDXoTMxhiWIz9zQ - -__PACKAGE__->load_components('+Conch::DB::Helper::Row::WithRole'); - -1; -__END__ - -=pod - -=head1 LICENSING - -Copyright Joyent, Inc. - -This Source Code Form is subject to the terms of the Mozilla Public License, -v.2.0. If a copy of the MPL was not distributed with this file, You can obtain -one at L. - -=cut -# vim: set ts=4 sts=4 sw=4 et : diff --git a/lib/Conch/DB/Result/Relay.pm b/lib/Conch/DB/Result/Relay.pm index c3edd406c..719b4f11d 100644 --- a/lib/Conch/DB/Result/Relay.pm +++ b/lib/Conch/DB/Result/Relay.pm @@ -86,6 +86,13 @@ __PACKAGE__->table("relay"); is_nullable: 0 original: {default_value => \"now()"} +=head2 user_id + + data_type: 'uuid' + is_foreign_key: 1 + is_nullable: 0 + size: 16 + =cut __PACKAGE__->add_columns( @@ -129,6 +136,8 @@ __PACKAGE__->add_columns( is_nullable => 0, original => { default_value => \"now()" }, }, + "user_id", + { data_type => "uuid", is_foreign_key => 1, is_nullable => 0, size => 16 }, ); =head1 PRIMARY KEY @@ -174,19 +183,19 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 user_relay_connections +=head2 user_account -Type: has_many +Type: belongs_to -Related object: L +Related object: L =cut -__PACKAGE__->has_many( - "user_relay_connections", - "Conch::DB::Result::UserRelayConnection", - { "foreign.relay_id" => "self.id" }, - { cascade_copy => 0, cascade_delete => 0 }, +__PACKAGE__->belongs_to( + "user_account", + "Conch::DB::Result::UserAccount", + { id => "user_id" }, + { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); =head2 devices @@ -199,19 +208,9 @@ Composing rels: L -> device __PACKAGE__->many_to_many("devices", "device_relay_connections", "device"); -=head2 user_accounts - -Type: many_to_many - -Composing rels: L -> user_account - -=cut - -__PACKAGE__->many_to_many("user_accounts", "user_relay_connections", "user_account"); - # Created by DBIx::Class::Schema::Loader v0.07049 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:xMpqCXk+kpN2bu77AEmjUA +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:8r42EA82AMoASKBMCa6Jpw __PACKAGE__->add_columns( '+deactivated' => { is_serializable => 0 }, diff --git a/lib/Conch/DB/Result/UserAccount.pm b/lib/Conch/DB/Result/UserAccount.pm index 4fcffe2b3..0c8c6e675 100644 --- a/lib/Conch/DB/Result/UserAccount.pm +++ b/lib/Conch/DB/Result/UserAccount.pm @@ -156,47 +156,47 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 user_build_roles +=head2 relays Type: has_many -Related object: L +Related object: L =cut __PACKAGE__->has_many( - "user_build_roles", - "Conch::DB::Result::UserBuildRole", + "relays", + "Conch::DB::Result::Relay", { "foreign.user_id" => "self.id" }, { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 user_organization_roles +=head2 user_build_roles Type: has_many -Related object: L +Related object: L =cut __PACKAGE__->has_many( - "user_organization_roles", - "Conch::DB::Result::UserOrganizationRole", + "user_build_roles", + "Conch::DB::Result::UserBuildRole", { "foreign.user_id" => "self.id" }, { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 user_relay_connections +=head2 user_organization_roles Type: has_many -Related object: L +Related object: L =cut __PACKAGE__->has_many( - "user_relay_connections", - "Conch::DB::Result::UserRelayConnection", + "user_organization_roles", + "Conch::DB::Result::UserOrganizationRole", { "foreign.user_id" => "self.id" }, { cascade_copy => 0, cascade_delete => 0 }, ); @@ -266,16 +266,6 @@ Composing rels: L -> organization __PACKAGE__->many_to_many("organizations", "user_organization_roles", "organization"); -=head2 relays - -Type: many_to_many - -Composing rels: L -> relay - -=cut - -__PACKAGE__->many_to_many("relays", "user_relay_connections", "relay"); - =head2 workspaces Type: many_to_many @@ -288,7 +278,7 @@ __PACKAGE__->many_to_many("workspaces", "user_workspace_roles", "workspace"); # Created by DBIx::Class::Schema::Loader v0.07049 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:anK22J/WA9Xfg5DXYRA3Ng +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:mYY3hXrpzC2XLZV001VlZA use DBIx::Class::PassphraseColumn 0.04 (); __PACKAGE__->load_components('PassphraseColumn'); @@ -381,11 +371,6 @@ sub TO_JSON ($self) { $cached_uors->@*) ); - # we assume we prefetched our user(s) with: - # { user_organization_roles => { organization => { organization_workspace_roles => 'workspace' } } } - my @cached_owrs = map - $_->organization->related_resultset('organization_workspace_roles')->get_cache->@*, - $cached_uors->@*; my %seen_workspaces; $data->{workspaces} = [ @@ -398,18 +383,6 @@ sub TO_JSON ($self) { } } $cached_uwrs->@*), - # direct owr+workspace entries - (map +( - $seen_workspaces{$_->id}++ ? () : do { - my $workspace = $_->workspace; - +{ - $workspace->TO_JSON->%*, - role => $_->role, - role_via_organization_id => $_->organization_id, - } - } - ), @cached_owrs), - # all the workspaces the user can reach indirectly (map +( map +( @@ -422,23 +395,6 @@ sub TO_JSON ($self) { ), $self->result_source->schema->resultset('workspace') ->workspaces_beneath($_->workspace_id) ), $cached_uwrs->@*), - - # all the workspaces the user's organization(s) can reach indirectly - (map { - my $owr = $_; - map +( - # $_ is a workspace where the organization inherits a role - $seen_workspaces{$_->id}++ ? () : do { - # instruct the workspace serializer to fill in the role fields - $self->is_admin ? $_->role('admin') : $_->organization_id_for_role($owr->organization_id); - +{ - $_->TO_JSON->%*, - role_via_organization_id => $owr->organization_id, - } - } - ), $self->result_source->schema->resultset('workspace') - ->workspaces_beneath($_->workspace_id) - } @cached_owrs), ]; } diff --git a/lib/Conch/DB/Result/UserRelayConnection.pm b/lib/Conch/DB/Result/UserRelayConnection.pm deleted file mode 100644 index 039eab8c3..000000000 --- a/lib/Conch/DB/Result/UserRelayConnection.pm +++ /dev/null @@ -1,146 +0,0 @@ -use utf8; -package Conch::DB::Result::UserRelayConnection; - -# Created by DBIx::Class::Schema::Loader -# DO NOT MODIFY THE FIRST PART OF THIS FILE - -=head1 NAME - -Conch::DB::Result::UserRelayConnection - -=cut - -use strict; -use warnings; - - -=head1 BASE CLASS: L - -=cut - -use base 'Conch::DB::Result'; - -=head1 TABLE: C - -=cut - -__PACKAGE__->table("user_relay_connection"); - -=head1 ACCESSORS - -=head2 user_id - - data_type: 'uuid' - is_foreign_key: 1 - is_nullable: 0 - size: 16 - -=head2 first_seen - - data_type: 'timestamp with time zone' - default_value: current_timestamp - is_nullable: 0 - original: {default_value => \"now()"} - -=head2 last_seen - - data_type: 'timestamp with time zone' - default_value: current_timestamp - is_nullable: 0 - original: {default_value => \"now()"} - -=head2 relay_id - - data_type: 'uuid' - is_foreign_key: 1 - is_nullable: 0 - size: 16 - -=cut - -__PACKAGE__->add_columns( - "user_id", - { data_type => "uuid", is_foreign_key => 1, is_nullable => 0, size => 16 }, - "first_seen", - { - data_type => "timestamp with time zone", - default_value => \"current_timestamp", - is_nullable => 0, - original => { default_value => \"now()" }, - }, - "last_seen", - { - data_type => "timestamp with time zone", - default_value => \"current_timestamp", - is_nullable => 0, - original => { default_value => \"now()" }, - }, - "relay_id", - { data_type => "uuid", is_foreign_key => 1, is_nullable => 0, size => 16 }, -); - -=head1 PRIMARY KEY - -=over 4 - -=item * L - -=item * L - -=back - -=cut - -__PACKAGE__->set_primary_key("user_id", "relay_id"); - -=head1 RELATIONS - -=head2 relay - -Type: belongs_to - -Related object: L - -=cut - -__PACKAGE__->belongs_to( - "relay", - "Conch::DB::Result::Relay", - { id => "relay_id" }, - { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, -); - -=head2 user_account - -Type: belongs_to - -Related object: L - -=cut - -__PACKAGE__->belongs_to( - "user_account", - "Conch::DB::Result::UserAccount", - { id => "user_id" }, - { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, -); - - -# Created by DBIx::Class::Schema::Loader v0.07049 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:yuxJRLzF48Nk/Eyom+u0gw - -1; -__END__ - -=pod - -=head1 LICENSING - -Copyright Joyent, Inc. - -This Source Code Form is subject to the terms of the Mozilla Public License, -v.2.0. If a copy of the MPL was not distributed with this file, You can obtain -one at L. - -=cut -# vim: set ts=4 sts=4 sw=4 et : diff --git a/lib/Conch/DB/Result/Workspace.pm b/lib/Conch/DB/Result/Workspace.pm index 42451c34c..302150c66 100644 --- a/lib/Conch/DB/Result/Workspace.pm +++ b/lib/Conch/DB/Result/Workspace.pm @@ -98,21 +98,6 @@ __PACKAGE__->add_unique_constraint("workspace_name_key", ["name"]); =head1 RELATIONS -=head2 organization_workspace_roles - -Type: has_many - -Related object: L - -=cut - -__PACKAGE__->has_many( - "organization_workspace_roles", - "Conch::DB::Result::OrganizationWorkspaceRole", - { "foreign.workspace_id" => "self.id" }, - { cascade_copy => 0, cascade_delete => 0 }, -); - =head2 parent_workspace Type: belongs_to @@ -178,20 +163,6 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 organizations - -Type: many_to_many - -Composing rels: L -> organization - -=cut - -__PACKAGE__->many_to_many( - "organizations", - "organization_workspace_roles", - "organization", -); - =head2 racks Type: many_to_many @@ -214,7 +185,7 @@ __PACKAGE__->many_to_many("user_accounts", "user_workspace_roles", "user_account # Created by DBIx::Class::Schema::Loader v0.07049 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Urk+hHQeLcSJ6VUznFO6Hg +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UTkb6H9/XmVkYnMTHm2uQw use experimental 'signatures'; use Sub::Install; @@ -239,15 +210,6 @@ sub TO_JSON ($self) { my $role_via = $self->result_source->resultset->role_via_for_user($self->id, $user_id); Carp::croak('tried to get role data for a user that has no role for this workspace: workspace_id ', $self->id, ', user_id ', $user_id) if not $role_via; - $data->{role} = $role_via->role; - $data->{role_via_workspace_id} = $role_via->workspace_id if $role_via->workspace_id ne $self->id; - $data->{role_via_organization_id} = $role_via->organization_id if $role_via->can('organization_id'); - } - # we are fetching workspace data from the perspective of a particular organization - elsif (my $organization_id = $self->organization_id_for_role) { - my $role_via = $self->result_source->resultset->role_via_for_organization($self->id, $organization_id); - Carp::croak('tried to get role data for an organization that has no role for this workspace: workspace_id ', $self->id, ', organization_id ', $organization_id) if not $role_via; - $data->{role} = $role_via->role; $data->{role_via_workspace_id} = $role_via->workspace_id if $role_via->workspace_id ne $self->id; } @@ -264,14 +226,9 @@ Accessor for informational column, which is by the serializer in the result data Accessor for informational column, which is used by the serializer to signal we should fetch and include inherited role data for the user. -=head2 organization_id_for_role - -Accessor for informational column, which is used by the serializer to signal we should fetch -and include inherited role data for the organization. - =cut -foreach my $column (qw(role user_id_for_role organization_id_for_role)) { +foreach my $column (qw(role user_id_for_role)) { Sub::Install::install_sub({ as => $column, code => sub { diff --git a/lib/Conch/DB/ResultSet/Build.pm b/lib/Conch/DB/ResultSet/Build.pm index b431c92d1..4c68b1d27 100644 --- a/lib/Conch/DB/ResultSet/Build.pm +++ b/lib/Conch/DB/ResultSet/Build.pm @@ -19,7 +19,7 @@ Interface to queries involving builds. =head2 admins -All the 'admin' users for the provided build(s). Pass a true argument to also include all +All the 'admin' users for the provided build(s). Pass a true argument to also include all system admin users in the result. =cut @@ -94,14 +94,16 @@ sub user_has_role ($self, $user_id, $role) { my $via_user_rs = $self ->search_related('user_build_roles', { user_id => $user_id }) ->with_role($role) - ->related_resultset('user_account'); + ->related_resultset('user_account') + ->columns(['id']); my $via_org_rs = $self ->related_resultset('organization_build_roles') ->with_role($role) ->related_resultset('organization') ->search_related('user_organization_roles', { user_id => $user_id }) - ->related_resultset('user_account'); + ->related_resultset('user_account') + ->columns(['id']); return $via_user_rs->union_all($via_org_rs)->exists; } diff --git a/lib/Conch/DB/ResultSet/Device.pm b/lib/Conch/DB/ResultSet/Device.pm index e31c5775f..a5bf1151c 100644 --- a/lib/Conch/DB/ResultSet/Device.pm +++ b/lib/Conch/DB/ResultSet/Device.pm @@ -24,7 +24,7 @@ parent workspaces. This is a nested query which searches all workspaces and builds in the database, so only use this query when its impact is outweighed by the impact of filtering a large resultset of -devices in the database. (That is, usually you should start with a single device and then +devices in the database. (That is, usually you should start with a single device and then apply C<< $device_rs->user_has_role($user_id, $role) >> to it.) =cut @@ -92,7 +92,8 @@ sub user_has_role ($self, $user_id, $role) { ->related_resultset('build') ->search_related('user_build_roles', { user_id => $user_id }) ->with_role($role) - ->related_resultset('user_account'); + ->related_resultset('user_account') + ->columns(['id']); my $via_org_rs = $self ->related_resultset('build') @@ -100,13 +101,13 @@ sub user_has_role ($self, $user_id, $role) { ->with_role($role) ->related_resultset('organization') ->search_related('user_organization_roles', { user_id => $user_id }) - ->related_resultset('user_account'); + ->related_resultset('user_account') + ->columns(['id']); return 1 if $via_user_rs->union_all($via_org_rs)->exists; # this checks: # device -> rack -> workspace -> user_workspace_role -> user - # device -> rack -> workspace -> organization_workspace_role -> organization -> user # device -> rack -> build -> user_build_role -> user # device -> rack -> build -> organization_build_role -> organization -> user $self @@ -135,12 +136,15 @@ sub devices_without_location ($self) { Restrict results to those that have sent a device report proxied by a relay registered using the provided user's credentials. +Note: this is not accurate if the relay is now registered to a different user than that which +sent the report. + =cut sub devices_reported_by_user_relay ($self, $user_id) { $self->search( - { 'user_relay_connections.user_id' => $user_id }, - { join => { device_relay_connections => { relay => 'user_relay_connections' } } }, + { 'relay.user_id' => $user_id }, + { join => { device_relay_connections => 'relay' } }, ); } @@ -172,7 +176,7 @@ sub latest_device_report ($self) { =head2 device_settings_as_hash -Returns a hash of all (active) device settings for the specified device(s). (Will return +Returns a hash of all (active) device settings for the specified device(s). (Will return merged results when passed a resultset referencing multiple devices, which is probably not what you want, so don't do that.) diff --git a/lib/Conch/DB/ResultSet/Organization.pm b/lib/Conch/DB/ResultSet/Organization.pm index 8128c690e..bbb5b9b12 100644 --- a/lib/Conch/DB/ResultSet/Organization.pm +++ b/lib/Conch/DB/ResultSet/Organization.pm @@ -17,7 +17,7 @@ Interface to queries involving organizations. =head2 admins -All the 'admin' users for the provided organization(s). Pass a true argument to also include all +All the 'admin' users for the provided organization(s). Pass a true argument to also include all system admin users in the result. =cut diff --git a/lib/Conch/DB/ResultSet/Rack.pm b/lib/Conch/DB/ResultSet/Rack.pm index 3601f9c4f..25ac4feb4 100644 --- a/lib/Conch/DB/ResultSet/Rack.pm +++ b/lib/Conch/DB/ResultSet/Rack.pm @@ -96,22 +96,15 @@ sub user_has_role ($self, $user_id, $role) { # since every workspace_rack entry has an equivalent entry in the parent workspace, we do # not need to search the workspace heirarchy here, but simply look for a role entry for any # workspace the rack is associated with. - my $ws_rs = $self - ->related_resultset('workspace_racks') - ->related_resultset('workspace'); # this is Conch::DB::ResultSet::Workspace::user_has_role, unrolled - my $ws_via_user_rs = $ws_rs + my $ws_via_user_rs = $self + ->related_resultset('workspace_racks') + ->related_resultset('workspace') ->search_related('user_workspace_roles', { user_id => $user_id }) ->with_role($role) - ->related_resultset('user_account'); - - my $ws_via_org_rs = $ws_rs - ->related_resultset('organization_workspace_roles') - ->with_role($role) - ->related_resultset('organization') - ->search_related('user_organization_roles', { user_id => $user_id }) - ->related_resultset('user_account'); + ->related_resultset('user_account') + ->columns(['id']); my $build_rs = $self->related_resultset('build'); @@ -119,17 +112,18 @@ sub user_has_role ($self, $user_id, $role) { my $build_via_user_rs = $build_rs ->search_related('user_build_roles', { user_id => $user_id }) ->with_role($role) - ->related_resultset('user_account'); + ->related_resultset('user_account') + ->columns(['id']); my $build_via_org_rs = $build_rs ->related_resultset('organization_build_roles') ->with_role($role) ->related_resultset('organization') ->search_related('user_organization_roles', { user_id => $user_id }) - ->related_resultset('user_account'); + ->related_resultset('user_account') + ->columns(['id']); return $ws_via_user_rs - ->union_all($ws_via_org_rs) ->union_all($build_via_user_rs) ->union_all($build_via_org_rs) ->exists; diff --git a/lib/Conch/DB/ResultSet/Workspace.pm b/lib/Conch/DB/ResultSet/Workspace.pm index d7126cc5e..8f5ef38d7 100644 --- a/lib/Conch/DB/ResultSet/Workspace.pm +++ b/lib/Conch/DB/ResultSet/Workspace.pm @@ -149,7 +149,7 @@ Query for workspace(s) with an extra field attached to the result, containing in the effective role the user has for the workspace. The indicated role is used directly, with no additional queries done (consequently "role_via" -will not appear in the serialized data). This is intended to be used in preference to +will not appear in the serialized data). This is intended to be used in preference to L when the user is a system admin. =cut @@ -166,9 +166,8 @@ sub add_role_column ($self, $role) { =head2 with_role_via_data_for_user Query for workspace(s) with an extra field attached to the query which will signal the -workspace serializer to include the "role", "role_via_workspace_id" and -"role_via_organization_id" columns, containing information about the effective role the user -has for the workspace. +workspace serializer to include the "role" and "role_via_workspace_id" columns, containing +information about the effective role the user has for the workspace. Only one user_id can be calculated at a time. If you need to generate workspace-and-role data for multiple users at once, you can manually do: @@ -189,9 +188,9 @@ sub with_role_via_data_for_user ($self, $user_id) { =head2 role_via_for_user -For a given workspace_id and user_id, find the user_workspace_role or -organization_workspace_role row that is responsible for providing the user access to the -workspace (the row with the greatest role that is attached to an ancestor workspace). +For a given workspace_id and user_id, find the user_workspace_role row that is responsible for +providing the user access to the workspace (the row with the greatest role that is attached to +an ancestor workspace). How the role is calculated: @@ -200,11 +199,7 @@ How the role is calculated: =item * The role on the user_organization_role role is B used. =item * The number of workspaces between C<$workspace_id> and the workspace attached to the -user_workspace_role or organization_workspace_role row is B used. - -=item * When both a user_workspace_role and organization_workspace_role row are found with the same -role, the record directly associated with the workspace (if there is one) is preferred; -otherwise, the user_workspace_role row is preferred. +user_workspace_role row is B used. =back @@ -221,47 +216,7 @@ sub role_via_for_user ($self, $workspace_id, $user_id) { ->rows(1) ->single; - return $uwr if $uwr and $uwr->workspace_id eq $workspace_id and $uwr->role eq 'admin'; - - # there could be more than one organization that grants the user this role, but it - # shouldn't matter which one we single out in the result. - my $owr = $self->and_workspaces_above($workspace_id) - ->search_related('organization_workspace_roles', - { user_id => $user_id }, { join => { organization => 'user_organization_roles' } }) - ->order_by({ -desc => 'organization_workspace_roles.role' }) - ->rows(1) - ->single; - - my (undef, $role_via) = sort { - (!defined $a ? -1 : !defined $b ? 1 : 0) - || $a->role_cmp($b->role) - || ($a->workspace_id eq $workspace_id ? 1 : 0) - || ($b->workspace_id eq $workspace_id ? -1 : 0) - || 1 # give up; go with $a ($uwr) - } ($uwr, $owr); - - return $role_via; -} - -=head2 role_via_for_organization - -For a given workspace_id and organization_id, find the organization_workspace_role row that is -responsible for providing the organization access to the workspace (the -organization_workspace_role with the greatest role that is attached to an ancestor -workspace). - -=cut - -sub role_via_for_organization ($self, $workspace_id, $organization_id) { - Carp::croak('resultset should not have conditions') if $self->{cond}; - - # because we check for duplicate role entries when creating organization_workspace_role rows, - # we "should" only have *one* row with the greatest role in the entire hierarchy... - $self->and_workspaces_above($workspace_id) - ->search_related('organization_workspace_roles', { organization_id => $organization_id }) - ->order_by({ -desc => 'role' }) - ->rows(1) - ->single; + return $uwr; } =head2 admins @@ -272,17 +227,9 @@ system admin users in the result. =cut sub admins ($self, $include_sysadmins = undef) { - my $direct_users_rs = $self->search_related('user_workspace_roles', { role => 'admin' }) + my $rs = $self->search_related('user_workspace_roles', { role => 'admin' }) ->related_resultset('user_account'); - my $organization_users_rs = $self->search_related('organization_workspace_roles', - { 'organization_workspace_roles.role' => 'admin' }) - ->related_resultset('organization') - ->related_resultset('user_organization_roles') - ->related_resultset('user_account'); - - my $rs = $direct_users_rs->union_all($organization_users_rs); - $rs = $rs->union_all($self->result_source->schema->resultset('user_account')->search_rs({ is_admin => 1 })) if $include_sysadmins; @@ -314,14 +261,7 @@ sub with_user_role ($self, $user_id, $role) { { join => 'user_workspace_roles' }, ); - my $via_org_rs = $self->search( - { - $role ne 'ro' ? ('organization_workspace_roles.role' => { '>=' => \[ '?::role_enum', $role ] }) : (), - 'user_organization_roles.user_id' => $user_id, - }, - { join => { organization_workspace_roles => { organization => 'user_organization_roles' } } } ); - - return $via_user_rs->union_all($via_org_rs)->distinct; + return $via_user_rs; } =head2 user_has_role @@ -330,9 +270,6 @@ Checks that the provided user_id has (at least) the specified role in at least o the resultset. (Does not search recursively; add C<< ->and_workspaces_above($workspace_id) >> to your resultset first, if this is what you want.) -Both direct C entries and joined -C -> C entries are checked. - Returns a boolean. =cut @@ -347,13 +284,7 @@ sub user_has_role ($self, $user_id, $role) { ->with_role($role) ->related_resultset('user_account'); - my $via_org_rs = $self->related_resultset('organization_workspace_roles') - ->with_role($role) - ->related_resultset('organization') - ->search_related('user_organization_roles', { user_id => $user_id }) - ->related_resultset('user_account'); - - return $via_user_rs->union_all($via_org_rs)->exists; + return $via_user_rs->exists; } =head2 _workspaces_subquery diff --git a/lib/Conch/Plugin/Rollbar.pm b/lib/Conch/Plugin/Rollbar.pm index 5f87352c0..c9b6926c6 100644 --- a/lib/Conch/Plugin/Rollbar.pm +++ b/lib/Conch/Plugin/Rollbar.pm @@ -91,7 +91,7 @@ and therefore other helpers). =head2 send_exception_to_rollbar Asynchronously send exception details to Rollbar (if the C C is -configured). Returns a unique uuid suitable for logging, to correlate with the Rollbar entry +configured). Returns a unique uuid suitable for logging, to correlate with the Rollbar entry thus created. =cut diff --git a/lib/Conch/Route.pm b/lib/Conch/Route.pm index ed706894b..9cf8be948 100644 --- a/lib/Conch/Route.pm +++ b/lib/Conch/Route.pm @@ -123,7 +123,7 @@ sub all_routes ( $c->log->error('no endpoint found for: '.$c->req->method.' '.$c->req->url->path); $c->send_message_to_rollbar('warning', 'no endpoint found for: '.$c->req->method.' '.$c->req->url->path) if $c->feature('rollbar') and any { $c->req->url->path =~ m{^$_/} } @top_level_paths; - $c->status(404); + $c->status(404, { error => 'Route Not Found' }); })->name('catchall'); } diff --git a/lib/Conch/Route/Workspace.pm b/lib/Conch/Route/Workspace.pm index 72f7e369e..6ff574746 100644 --- a/lib/Conch/Route/Workspace.pm +++ b/lib/Conch/Route/Workspace.pm @@ -75,17 +75,6 @@ sub routes { # DELETE /workspace/:workspace_id_or_name/user/#target_user_id_or_email?send_mail=<1|0> $with_workspace_admin->under('/user/#target_user_id_or_email')->to('user#find_user') ->delete('/')->to('workspace_user#remove'); - - # GET /workspace/:workspace_id_or_name/organization - $with_workspace_admin->get('/organization')->to('workspace_organization#list_workspace_organizations'); - - # POST /workspace/:workspace_id_or_name/organization?send_mail=<1|0> - $with_workspace_admin->post('/organization')->to('workspace_organization#add_workspace_organization'); - - # DELETE /workspace/:workspace_id_or_name/organization/:organization_id_or_name?send_mail=<1|0> - $with_workspace_admin->under('/organization/:organization_id_or_name') - ->to('organization#find_organization') - ->delete('/')->to('workspace_organization#remove_workspace_organization'); } } diff --git a/lib/Conch/ValidationSystem.pm b/lib/Conch/ValidationSystem.pm index 16df54bed..81ea9bd55 100644 --- a/lib/Conch/ValidationSystem.pm +++ b/lib/Conch/ValidationSystem.pm @@ -258,7 +258,7 @@ sub update_validation_plans ($self) { } } - # now we have a list of active validations we want. add any that aren't already present. + # now we have a list of active validations we want. add any that aren't already present. foreach my $want_validation (@add_active_validations) { next if $existing_active_validations{$want_validation->module}; $self->log->info('adding '.$want_validation->name.' version ' diff --git a/lib/Test/Conch.pm b/lib/Test/Conch.pm index f8afc4f5e..7803347e9 100644 --- a/lib/Test/Conch.pm +++ b/lib/Test/Conch.pm @@ -500,7 +500,7 @@ sub generate_fixtures ($self, @specification) { =head2 authenticate Authenticates a user in the current test instance. Uses default (superuser) credentials if not -provided. Optionally will bail out of B tests on failure. +provided. Optionally will bail out of B tests on failure. This will set 'user' in the session (C<< $t->app->session('user') >>), so a token is not needed on subsequent requests. diff --git a/lib/Test/Conch/Fixtures.pm b/lib/Test/Conch/Fixtures.pm index a44cea5ba..45b322ba3 100644 --- a/lib/Test/Conch/Fixtures.pm +++ b/lib/Test/Conch/Fixtures.pm @@ -586,12 +586,13 @@ sub _generate_definition ($self, $fixture_type, $num, $specification) { }, requires => { "hardware_vendor_$num" => { our => 'hardware_vendor_id', their => 'id' }, - "validation_plan_$num" => { our => 'validation_plan_id', their => 'id' }, + $specification->{validation_plan_id} ? () : + ("validation_plan_$num" => { our => 'validation_plan_id', their => 'id' }), }, }, }, [ 'hardware_vendor', $num, $vendor_spec ], - [ 'validation_plan', $num, {} ]; + $specification->{validation_plan_id} ? () : [ 'validation_plan', $num, {} ]; } elsif ($fixture_type eq 'datacenter_room') { return +{ diff --git a/sql/migrations/0141-user_relay_connection.sql b/sql/migrations/0141-user_relay_connection.sql new file mode 100644 index 000000000..afcd9838a --- /dev/null +++ b/sql/migrations/0141-user_relay_connection.sql @@ -0,0 +1,16 @@ +SELECT run_migration(141, $$ + + alter table relay add column user_id uuid references user_account (id); + + update relay + set user_id = user_relay_connection.user_id, + last_seen = greatest(relay.last_seen, user_relay_connection.last_seen) + from user_relay_connection + where user_relay_connection.relay_id = relay.id; + + create index relay_user_id on relay (user_id); + + alter table relay alter column user_id set not null; + drop table user_relay_connection; + +$$); diff --git a/sql/migrations/0143-drop-organization_workspace_role.sql b/sql/migrations/0143-drop-organization_workspace_role.sql new file mode 100644 index 000000000..d313f0e70 --- /dev/null +++ b/sql/migrations/0143-drop-organization_workspace_role.sql @@ -0,0 +1,5 @@ +SELECT run_migration(143, $$ + + drop table organization_workspace_role; + +$$); diff --git a/sql/schema.sql b/sql/schema.sql index e1b2dd685..7c30d4c66 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -467,19 +467,6 @@ CREATE TABLE public.organization_build_role ( ALTER TABLE public.organization_build_role OWNER TO conch; --- --- Name: organization_workspace_role; Type: TABLE; Schema: public; Owner: conch --- - -CREATE TABLE public.organization_workspace_role ( - organization_id uuid NOT NULL, - workspace_id uuid NOT NULL, - role public.role_enum DEFAULT 'ro'::public.role_enum NOT NULL -); - - -ALTER TABLE public.organization_workspace_role OWNER TO conch; - -- -- Name: rack; Type: TABLE; Schema: public; Owner: conch -- @@ -548,6 +535,7 @@ CREATE TABLE public.relay ( updated timestamp with time zone DEFAULT now() NOT NULL, id uuid DEFAULT public.gen_random_uuid() NOT NULL, last_seen timestamp with time zone DEFAULT now() NOT NULL, + user_id uuid NOT NULL, CONSTRAINT relay_ssh_port_check CHECK ((ssh_port >= 0)) ); @@ -601,20 +589,6 @@ CREATE TABLE public.user_organization_role ( ALTER TABLE public.user_organization_role OWNER TO conch; --- --- Name: user_relay_connection; Type: TABLE; Schema: public; Owner: conch --- - -CREATE TABLE public.user_relay_connection ( - user_id uuid NOT NULL, - first_seen timestamp with time zone DEFAULT now() NOT NULL, - last_seen timestamp with time zone DEFAULT now() NOT NULL, - relay_id uuid NOT NULL -); - - -ALTER TABLE public.user_relay_connection OWNER TO conch; - -- -- Name: user_session_token; Type: TABLE; Schema: public; Owner: conch -- @@ -967,14 +941,6 @@ ALTER TABLE ONLY public.organization ADD CONSTRAINT organization_pkey PRIMARY KEY (id); --- --- Name: organization_workspace_role organization_workspace_role_pkey; Type: CONSTRAINT; Schema: public; Owner: conch --- - -ALTER TABLE ONLY public.organization_workspace_role - ADD CONSTRAINT organization_workspace_role_pkey PRIMARY KEY (organization_id, workspace_id); - - -- -- Name: rack rack_datacenter_room_id_name_key; Type: CONSTRAINT; Schema: public; Owner: conch -- @@ -1071,14 +1037,6 @@ ALTER TABLE ONLY public.user_organization_role ADD CONSTRAINT user_organization_role_pkey PRIMARY KEY (user_id, organization_id); --- --- Name: user_relay_connection user_relay_connection_pkey; Type: CONSTRAINT; Schema: public; Owner: conch --- - -ALTER TABLE ONLY public.user_relay_connection - ADD CONSTRAINT user_relay_connection_pkey PRIMARY KEY (user_id, relay_id); - - -- -- Name: user_session_token user_session_token_pkey; Type: CONSTRAINT; Schema: public; Owner: conch -- @@ -1351,13 +1309,6 @@ CREATE INDEX organization_build_role_build_id_idx ON public.organization_build_r CREATE UNIQUE INDEX organization_name_key ON public.organization USING btree (name) WHERE (deactivated IS NULL); --- --- Name: organization_workspace_role_workspace_id_idx; Type: INDEX; Schema: public; Owner: conch --- - -CREATE INDEX organization_workspace_role_workspace_id_idx ON public.organization_workspace_role USING btree (workspace_id); - - -- -- Name: rack_build_id_idx; Type: INDEX; Schema: public; Owner: conch -- @@ -1393,6 +1344,13 @@ CREATE INDEX rack_layout_rack_id_idx ON public.rack_layout USING btree (rack_id) CREATE INDEX rack_rack_role_id_idx ON public.rack USING btree (rack_role_id); +-- +-- Name: relay_user_id; Type: INDEX; Schema: public; Owner: conch +-- + +CREATE INDEX relay_user_id ON public.relay USING btree (user_id); + + -- -- Name: user_account_email_key; Type: INDEX; Schema: public; Owner: conch -- @@ -1421,13 +1379,6 @@ CREATE INDEX user_build_role_build_id_idx ON public.user_build_role USING btree CREATE INDEX user_organization_role_organization_id_idx ON public.user_organization_role USING btree (organization_id); --- --- Name: user_relay_connection_relay_id_idx; Type: INDEX; Schema: public; Owner: conch --- - -CREATE INDEX user_relay_connection_relay_id_idx ON public.user_relay_connection USING btree (relay_id); - - -- -- Name: user_session_token_expires_idx; Type: INDEX; Schema: public; Owner: conch -- @@ -1725,22 +1676,6 @@ ALTER TABLE ONLY public.organization_build_role ADD CONSTRAINT organization_build_role_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organization(id); --- --- Name: organization_workspace_role organization_workspace_role_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: conch --- - -ALTER TABLE ONLY public.organization_workspace_role - ADD CONSTRAINT organization_workspace_role_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organization(id); - - --- --- Name: organization_workspace_role organization_workspace_role_workspace_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: conch --- - -ALTER TABLE ONLY public.organization_workspace_role - ADD CONSTRAINT organization_workspace_role_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES public.workspace(id); - - -- -- Name: rack rack_build_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: conch -- @@ -1789,6 +1724,14 @@ ALTER TABLE ONLY public.rack ADD CONSTRAINT rack_role_fkey FOREIGN KEY (rack_role_id) REFERENCES public.rack_role(id); +-- +-- Name: relay relay_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: conch +-- + +ALTER TABLE ONLY public.relay + ADD CONSTRAINT relay_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.user_account(id); + + -- -- Name: user_build_role user_build_role_build_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: conch -- @@ -1821,22 +1764,6 @@ ALTER TABLE ONLY public.user_organization_role ADD CONSTRAINT user_organization_role_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.user_account(id); --- --- Name: user_relay_connection user_relay_connection_relay_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: conch --- - -ALTER TABLE ONLY public.user_relay_connection - ADD CONSTRAINT user_relay_connection_relay_id_fkey FOREIGN KEY (relay_id) REFERENCES public.relay(id); - - --- --- Name: user_relay_connection user_relay_connection_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: conch --- - -ALTER TABLE ONLY public.user_relay_connection - ADD CONSTRAINT user_relay_connection_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.user_account(id); - - -- -- Name: user_session_token user_session_token_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: conch -- @@ -2085,13 +2012,6 @@ GRANT SELECT ON TABLE public.organization TO conch_read_only; GRANT SELECT ON TABLE public.organization_build_role TO conch_read_only; --- --- Name: TABLE organization_workspace_role; Type: ACL; Schema: public; Owner: conch --- - -GRANT SELECT ON TABLE public.organization_workspace_role TO conch_read_only; - - -- -- Name: TABLE rack; Type: ACL; Schema: public; Owner: conch -- @@ -2141,13 +2061,6 @@ GRANT SELECT ON TABLE public.user_build_role TO conch_read_only; GRANT SELECT ON TABLE public.user_organization_role TO conch_read_only; --- --- Name: TABLE user_relay_connection; Type: ACL; Schema: public; Owner: conch --- - -GRANT SELECT ON TABLE public.user_relay_connection TO conch_read_only; - - -- -- Name: TABLE user_session_token; Type: ACL; Schema: public; Owner: conch -- diff --git a/t/conch-rollbar.t b/t/conch-rollbar.t index 753924ef0..964a4df89 100644 --- a/t/conch-rollbar.t +++ b/t/conch-rollbar.t @@ -338,7 +338,7 @@ $t->do_and_wait_for_event( sub ($t) { $t->get_ok('/rack/foo/bar/i_do_not_exist') ->status_is(404) - ->json_is({ error => 'Not Found' }); + ->json_is({ error => 'Route Not Found' }); }, sub ($payload) { cmp_deeply( @@ -375,7 +375,7 @@ $t->do_and_wait_for_event( sub ($t) { $t->get_ok('/i_do_not_exist') ->status_is(404) - ->json_is({ error => 'Not Found' }); + ->json_is({ error => 'Route Not Found' }); }, sub ($payload) { fail('rollbar message was incorrectly sent') }, sub { pass('no rollbar message was sent for unrecognized path prefix') }, @@ -386,7 +386,7 @@ $t->do_and_wait_for_event( sub ($t) { $t->get_ok('/devicefoo') ->status_is(404) - ->json_is({ error => 'Not Found' }); + ->json_is({ error => 'Route Not Found' }); }, sub ($payload) { fail('rollbar message was incorrectly sent') }, sub { pass('no rollbar message was sent for unrecognized path prefix') }, diff --git a/t/conch-status.t b/t/conch-status.t index dadcbe346..ba184f376 100644 --- a/t/conch-status.t +++ b/t/conch-status.t @@ -38,7 +38,7 @@ use Test::Conch; $t->get_ok('/401')->status_is(401)->json_is({ error => 'Unauthorized' }); $t->get_ok('/403')->status_is(403)->json_is({ error => 'Forbidden' }); - $t->get_ok('/404')->status_is(404)->json_is({ error => 'Not Found' }); + $t->get_ok('/404')->status_is(404)->json_is({ error => 'Entity Not Found' }); $t->get_ok('/501')->status_is(501)->json_is({ error => 'Unimplemented' }); $t->get_ok('/409')->status_is(409)->json_is({ error => 'Conflict'}); diff --git a/t/database.t b/t/database.t index 87dcc0ff6..1c99e84ee 100644 --- a/t/database.t +++ b/t/database.t @@ -319,8 +319,9 @@ subtest 'results exist' => sub { ->schema ->storage ->_select($query, \'*', {}, {}); - my (@results) = $sth->fetchrow_array; - is(@results, 1, 'only one value is fetched'); + + my $results = $sth->fetchall_arrayref; + is($results->@*, 1, 'only one value is fetched'); $rs->populate([ map +{ name => $_ }, qw(build1 build2 build3) ]); @@ -331,8 +332,8 @@ subtest 'results exist' => sub { ->storage ->_select($query, \'*', {}, {}); - (@results) = $sth->fetchrow_array; - is(@results, 1, 'only one value is fetched even when multiple rows exist'); + $results = $sth->fetchall_arrayref; + is($results->@*, 1, 'only one value is fetched even when multiple rows exist'); }; done_testing; diff --git a/t/integration/crud/build.t b/t/integration/crud/build.t index f42d8c0d2..a57cff9b8 100644 --- a/t/integration/crud/build.t +++ b/t/integration/crud/build.t @@ -554,7 +554,6 @@ $t->get_ok('/organization/my first organization') { (map +($_ => $org_admin->$_), qw(id name email)), role => 'admin' }, { (map +($_ => $org_member->$_), qw(id name email)), role => 'ro' }, ], - workspaces => [], builds => [ +{ $build->%{qw(id name description)}, role => 'ro' } ], }); diff --git a/t/integration/crud/devices.t b/t/integration/crud/devices.t index 4cb0adb75..9d3f24024 100644 --- a/t/integration/crud/devices.t +++ b/t/integration/crud/devices.t @@ -396,9 +396,7 @@ subtest 'located device' => sub { ->status_is(403) ->log_debug_is('User cannot access requested device(s)'); - my $org = $t->generate_fixtures('organization'); - $org->create_related('user_organization_roles', { user_id => $null_user->id, role => 'ro' }); - $global_ws->create_related('organization_workspace_roles', { organization_id => $org->id, role => 'ro' }); + $global_ws->create_related('user_workspace_roles', { user_id => $null_user->id, role => 'ro' }); $t->get_ok('/device/'.$located_device_id) ->status_is(200) @@ -885,11 +883,6 @@ subtest 'Device settings' => sub { ->json_schema_is('RequestValidationError') ->json_cmp_deeply('/details', [ { path => '/', message => re(qr/expected object/i) } ]); - $t->post_ok('/device/LOCATED_DEVICE/settings', json => { foo => 1 }) - ->status_is(400) - ->json_schema_is('RequestValidationError') - ->json_cmp_deeply('/details', [ { path => '/foo', message => re(qr/expected string/i) } ]); - $t->post_ok('/device/LOCATED_DEVICE/settings/foo', json => { foo => 'bar', baz => 'quux' }) ->status_is(400) ->json_schema_is('RequestValidationError') @@ -902,6 +895,22 @@ subtest 'Device settings' => sub { $t->post_ok('/device/LOCATED_DEVICE/settings', json => { foo => 'baz' }) ->status_is(204); + $t->post_ok('/device/LOCATED_DEVICE/settings', json => { foo => 2 }) + ->status_is(204); + + $t->get_ok('/device/LOCATED_DEVICE/settings') + ->status_is(200) + ->json_schema_is('DeviceSettings') + ->json_is({ foo => 2 }); + + $t->post_ok('/device/LOCATED_DEVICE/settings', json => { foo => JSON::PP::true }) + ->status_is(204); + + $t->get_ok('/device/LOCATED_DEVICE/settings') + ->status_is(200) + ->json_schema_is('DeviceSettings') + ->json_is({ foo => 1 }); + $t->post_ok('/device/LOCATED_DEVICE/settings', json => { foo => 'bar' }) ->status_is(204); @@ -1023,16 +1032,14 @@ subtest 'Device PXE' => sub { $t_super->authenticate(email => $super_user->email); my $layout = $t_super->load_fixture('rack_0a_layout_3_6'); - my $relay = $t_super->app->db_relays->create({ serial_number => 'my_relay' }); - my $device_pxe = $t_super->app->db_devices->create({ serial_number => 'PXE_TEST', hardware_product_id => $layout->hardware_product_id, health => 'unknown', device_relay_connections => [{ relay => { - id => $relay->id, - user_relay_connections => [ { user_id => $super_user->id } ], + serial_number => 'my_relay', + user_id => $super_user->id, } }], device_nics => [ diff --git a/t/integration/crud/organization.t b/t/integration/crud/organization.t index 3d361bd21..439688dc4 100644 --- a/t/integration/crud/organization.t +++ b/t/integration/crud/organization.t @@ -74,7 +74,6 @@ $t->get_ok($t->tx->res->headers->location) users => [ { (map +($_ => $admin_user->$_), qw(id name email)), role => 'admin' }, ], - workspaces => [], builds => [], }); my $organization = $t->tx->res->json; @@ -115,7 +114,6 @@ $t->get_ok('/organization') users => [ { (map +($_ => $admin_user->$_), qw(id name email)), role => 'admin' }, ], - workspaces => [], builds => [], }, ]); @@ -369,72 +367,6 @@ $t->delete_ok('/user/'.$admin_user->id) user => { map +($_ => $admin_user->$_), qw(id email name created deactivated) }, }); - -my $global_ws = $t->load_fixture('global_workspace'); -my $sub_ws = $t->generate_fixtures('workspace', { parent_workspace_id => $global_ws->id, name => 'sub ws' }); - -$t->get_ok('/workspace/'.$sub_ws->id.'/organization') - ->status_is(200) - ->json_schema_is('WorkspaceOrganizations') - ->json_is([]); - -$t->post_ok('/workspace/'.$sub_ws->id.'/organization', json => { role => 'ro' }) - ->status_is(400) - ->json_schema_is('RequestValidationError') - ->json_cmp_deeply('/details', [ { path => '/organization_id', message => re(qr/missing property/i) } ]); - -$t->post_ok('/workspace/'.$sub_ws->id.'/organization', json => { organization_id => $organization->{id} }) - ->status_is(400) - ->json_schema_is('RequestValidationError') - ->json_cmp_deeply('/details', [ { path => '/role', message => re(qr/missing property/i) } ]); - -$t2->get_ok('/workspace/'.$sub_ws->id.'/organization') - ->status_is(403) - ->log_debug_is('User lacks the required role (admin) for workspace '.$sub_ws->id); - -$t2->post_ok('/workspace/'.$sub_ws->id.'/organization', json => { - organization_id => $organization->{id}, - role => 'ro', - }) - ->status_is(403) - ->log_debug_is('User lacks the required role (admin) for workspace '.$sub_ws->id); - -$t->post_ok('/workspace/'.$sub_ws->id.'/organization', json => { - organization_id => $organization->{id}, - role => 'ro', - }) - ->status_is(204) - ->email_cmp_deeply([ - { - To => '"'.$admin_user->name.'" <'.$admin_user->email.'>, "'.$new_user->name.'" <'.$new_user->email.'>', - From => 'noreply@127.0.0.1', - Subject => 'Your Conch access has changed', - body => re(qr/^Your "my first organization" organization has been added to the\R"${\$sub_ws->name}" workspace at \Q$JOYENT\E with the "ro" role\./m), - }, - { - To => '"'.$super_user->name.'" <'.$super_user->email.'>', - From => 'noreply@127.0.0.1', - Subject => 'We added an organization to your workspace', - body => re(qr/^${\$super_user->name} \(${\$super_user->email}\) added the "my first organization" organization to the\R"${\$sub_ws->name}" workspace at \Q$JOYENT\E with the "ro" role\./m), - }, - ]); - -$t->get_ok('/workspace/'.$sub_ws->id.'/organization') - ->status_is(200) - ->json_schema_is('WorkspaceOrganizations') - ->json_is([ - { - $organization->%{qw(id name description)}, - role => 'ro', - admins => [ - { map +($_ => $admin_user->$_), qw(id name email) }, - { map +($_ => $new_user->$_), qw(id name email) }, - ], - }, - ]); - -push $organization->{workspaces}->@*, +{ (map +($_ => $sub_ws->$_), qw(id parent_workspace_id name description)), role => 'ro' }; - $t->get_ok('/organization/my first organization') ->status_is(200) ->json_schema_is('Organization') @@ -450,13 +382,6 @@ $t2->get_ok('/organization/my first organization') ->json_schema_is('Organization') ->json_is({ $organization->%*, - workspaces => [ - { - (map +($_ => $sub_ws->$_), qw(id name description)), - parent_workspace_id => undef, # user does not have the role to see GLOBAL - role => 'ro', - }, - ], builds => [], }); @@ -466,13 +391,6 @@ $t2->get_ok('/organization') ->json_is([ { $organization->%*, - workspaces => [ - { - (map +($_ => $sub_ws->$_), qw(id name description)), - parent_workspace_id => undef, # user does not have the role to see GLOBAL - role => 'ro', - }, - ], builds => [], }, # user is not a member of organization2 @@ -487,13 +405,6 @@ $t->get_ok('/user/'.$admin_user->email) { $organization->%{qw(id name description)}, role => 'admin' }, { $organization2->%{qw(id name description)}, role => 'admin' }, ], - workspaces => [ - { - (map +($_ => $sub_ws->$_), qw(id name description parent_workspace_id)), - role => 'ro', - role_via_organization_id => $organization->{id}, - }, - ], builds => [], })); @@ -505,13 +416,6 @@ $t->get_ok('/user/'.$new_user->email) organizations => [ { $organization->%{qw(id name description)}, role => 'admin' }, ], - workspaces => [ - { - (map +($_ => $sub_ws->$_), qw(id name description parent_workspace_id)), - role => 'ro', - role_via_organization_id => $organization->{id}, - }, - ], builds => [], })); @@ -523,374 +427,13 @@ $t2->get_ok('/user/me') organizations => [ { $organization->%{qw(id name description)}, role => 'admin' }, ], - workspaces => [ - { - (map +($_ => $sub_ws->$_), qw(id name description)), - parent_workspace_id => undef, # user does not have the role to see GLOBAL - role => 'ro', - role_via_organization_id => $organization->{id}, - }, - ], builds => [], })); -my $grandchild_ws = $t->generate_fixtures('workspace', { parent_workspace_id => $sub_ws->id, name => 'grandchild ws' }); - -push $organization->{workspaces}->@*, +{ (map +($_ => $grandchild_ws->$_), qw(id parent_workspace_id name description)), role => 'ro', role_via_workspace_id => $sub_ws->id }; - -$t->get_ok('/workspace/'.$grandchild_ws->id.'/organization') - ->status_is(200) - ->json_schema_is('WorkspaceOrganizations') - ->json_is([ - { - $organization->%{qw(id name description)}, - role => 'ro', - role_via_workspace_id => $sub_ws->id, - admins => [ - { map +($_ => $admin_user->$_), qw(id name email) }, - { map +($_ => $new_user->$_), qw(id name email) }, - ], - }, - ]); - -$t->get_ok('/user/'.$admin_user->email) - ->status_is(200) - ->json_schema_is('UserDetailed') - ->json_cmp_deeply(superhashof({ - id => $admin_user->id, - organizations => [ - { $organization->%{qw(id name description)}, role => 'admin' }, - { $organization2->%{qw(id name description)}, role => 'admin' }, - ], - workspaces => [ - { - (map +($_ => $sub_ws->$_), qw(id name description parent_workspace_id)), - role => 'ro', - role_via_organization_id => $organization->{id}, - }, - { - (map +($_ => $grandchild_ws->$_), qw(id name description parent_workspace_id)), - role => 'ro', - role_via_organization_id => $organization->{id}, - role_via_workspace_id => $sub_ws->id, - }, - ], - })); - -$t->get_ok('/user/'.$new_user->email) - ->status_is(200) - ->json_schema_is('UserDetailed') - ->json_cmp_deeply(superhashof({ - id => $new_user->id, - organizations => [ - { $organization->%{qw(id name description)}, role => 'admin' }, - ], - workspaces => [ - { - (map +($_ => $sub_ws->$_), qw(id name description parent_workspace_id)), - role => 'ro', - role_via_organization_id => $organization->{id}, - }, - { - (map +($_ => $grandchild_ws->$_), qw(id name description parent_workspace_id)), - role => 'ro', - role_via_organization_id => $organization->{id}, - role_via_workspace_id => $sub_ws->id, - }, - ], - builds => [], - })); - -$t2->get_ok('/user/me') - ->status_is(200) - ->json_schema_is('UserDetailed') - ->json_cmp_deeply(superhashof({ - id => $new_user->id, - organizations => [ - { $organization->%{qw(id name description)}, role => 'admin' }, - ], - workspaces => [ - { - (map +($_ => $sub_ws->$_), qw(id name description)), - parent_workspace_id => undef, # user does not have the role to see GLOBAL - role => 'ro', - role_via_organization_id => $organization->{id}, - }, - { - (map +($_ => $grandchild_ws->$_), qw(id name description parent_workspace_id)), - role => 'ro', - role_via_organization_id => $organization->{id}, - role_via_workspace_id => $sub_ws->id, - }, - ], - builds => [], - })); - -$t->get_ok('/workspace/'.$sub_ws->id.'/user') - ->status_is(200) - ->json_schema_is('WorkspaceUsers') - ->json_is([ - { - (map +($_ => $admin_user->$_), qw(id name email)), - role => 'ro', - role_via_organization_id => $organization->{id}, - }, - { - (map +($_ => $new_user->$_), qw(id name email)), - role => 'ro', - role_via_organization_id => $organization->{id}, - }, - ]); - -$t->get_ok('/workspace/'.$grandchild_ws->id.'/user') - ->status_is(200) - ->json_schema_is('WorkspaceUsers') - ->json_is([ - { - (map +($_ => $admin_user->$_), qw(id name email)), - role => 'ro', - role_via_workspace_id => $sub_ws->id, - role_via_organization_id => $organization->{id}, - }, - { - (map +($_ => $new_user->$_), qw(id name email)), - role => 'ro', - role_via_workspace_id => $sub_ws->id, - role_via_organization_id => $organization->{id}, - }, - ]); - -$t2->get_ok('/workspace') - ->status_is(200) - ->json_schema_is('WorkspacesAndRoles') - ->json_is([ - # $new_user cannot see GLOBAL - { - (map +($_ => $sub_ws->$_), qw(id name description)), - parent_workspace_id => undef, - role => 'ro', - role_via_organization_id => $organization->{id}, - }, - { - (map +($_ => $grandchild_ws->$_), qw(id name description parent_workspace_id)), - role => 'ro', - role_via_workspace_id => $sub_ws->id, - role_via_organization_id => $organization->{id}, - }, - ]); - -$t->post_ok('/workspace/'.$sub_ws->id.'/organization', json => { - organization_id => $organization->{id}, - role => 'rw', - }) - ->status_is(204) - ->email_cmp_deeply([ - { - To => '"'.$admin_user->name.'" <'.$admin_user->email.'>, "'.$new_user->name.'" <'.$new_user->email.'>', - From => 'noreply@127.0.0.1', - Subject => 'Your Conch access has changed', - body => re(qr/^Your access to the "${\$sub_ws->name}" workspace at \Q$JOYENT\E\Rvia the "my first organization" organization has been adjusted to the "rw" role\./m), - }, - { - To => '"'.$super_user->name.'" <'.$super_user->email.'>', - From => 'noreply@127.0.0.1', - Subject => 'We modified an organization\'s access to your workspace', - body => re(qr/^${\$super_user->name} \(${\$super_user->email}\) modified the "my first organization" organization's\Raccess to the "${\$sub_ws->name}" workspace at \Q$JOYENT\E to the "rw" role\./m), - }, - ]); - -$t->get_ok('/workspace/'.$sub_ws->id.'/organization') - ->status_is(200) - ->json_schema_is('WorkspaceOrganizations') - ->json_is([ - { - $organization->%{qw(id name description)}, - role => 'rw', - admins => [ - { map +($_ => $admin_user->$_), qw(id name email) }, - { map +($_ => $new_user->$_), qw(id name email) }, - ], - }, - ]); - -$_->{role} = 'rw' foreach $organization->{workspaces}->@*; - -$t->get_ok('/organization/my first organization') - ->status_is(200) - ->json_schema_is('Organization') - ->json_is($organization); - -$t->get_ok('/organization') - ->status_is(200) - ->json_schema_is('Organizations') - ->json_is([ $organization, $organization2 ]); - -$t->post_ok('/workspace/'.$sub_ws->id.'/organization', json => { - organization_id => $organization->{id}, - role => 'rw', - }) - ->status_is(204) - ->log_debug_is('organization "my first organization" already has rw access to workspace '.$sub_ws->id.': nothing to do') - ->email_not_sent; - -$t->post_ok('/workspace/'.$grandchild_ws->id.'/organization', json => { - organization_id => $organization->{id}, - role => 'rw', - }) - ->status_is(204) - ->log_debug_is('organization "my first organization" already has rw access to workspace '.$grandchild_ws->id.' via workspace '.$sub_ws->id.': nothing to do') - ->email_not_sent; - -$t->post_ok('/workspace/'.$sub_ws->id.'/organization', json => { - organization_id => $organization->{id}, - role => 'ro', - }) - ->status_is(409) - ->json_is({ error => 'organization "my first organization" already has rw access to workspace '.$sub_ws->id.': cannot downgrade role to ro' }) - ->email_not_sent; - -$t->post_ok('/workspace/'.$grandchild_ws->id.'/organization', json => { - organization_id => $organization->{id}, - role => 'ro', - }) - ->status_is(409) - ->json_is({ error => 'organization "my first organization" already has rw access to workspace '.$grandchild_ws->id.' via workspace '.$sub_ws->id.': cannot downgrade role to ro' }) - ->email_not_sent; - -$t->post_ok('/workspace/'.$sub_ws->id.'/user', json => { - user_id => $new_user->id, - role => 'rw', - }) - ->log_debug_is('user '.$new_user->name.' already has rw access to workspace '.$sub_ws->id.' via organization '.$organization->{id}.': nothing to do') - ->status_is(204) - ->email_not_sent; - -$t->post_ok('/workspace/'.$grandchild_ws->id.'/user', json => { - user_id => $new_user->id, - role => 'rw', - }) - ->log_debug_is('user '.$new_user->name.' already has rw access to workspace '.$grandchild_ws->id.' via workspace '.$sub_ws->id.' and organization '.$organization->{id}.': nothing to do') - ->status_is(204) - ->email_not_sent; - -$t->post_ok('/workspace/'.$sub_ws->id.'/user', json => { - user_id => $new_user->id, - role => 'ro', - }) - ->status_is(409) - ->json_is({ error => 'user '.$new_user->name.' already has rw access to workspace '.$sub_ws->id.' via organization '.$organization->{id}.': cannot downgrade role to ro' }) - ->email_not_sent; - -$t->post_ok('/workspace/'.$grandchild_ws->id.'/user', json => { - user_id => $new_user->id, - role => 'ro', - }) - ->status_is(409) - ->json_is({ error => 'user '.$new_user->name.' already has rw access to workspace '.$grandchild_ws->id.' via workspace '.$sub_ws->id.' and organization '.$organization->{id}.': cannot downgrade role to ro' }) - ->email_not_sent; - -$t2->delete_ok('/workspace/grandchild ws/organization/my first organization') - ->status_is(403) - ->log_debug_is('User lacks the required role (admin) for workspace grandchild ws'); - my $t_admin_user = Test::Conch->new(pg => $t->pg); $t_admin_user->authenticate(email => $admin_user->email); -$t_admin_user->delete_ok('/workspace/'.$grandchild_ws->id.'/organization/'.$organization->{id}) - ->status_is(403) - ->log_debug_is('User lacks the required role (admin) for workspace '.$grandchild_ws->id); - -$t_admin_user->delete_ok('/workspace/'.$sub_ws->id.'/organization/'.$organization->{id}) - ->status_is(403) - ->log_debug_is('User lacks the required role (admin) for workspace '.$sub_ws->id); - -$t->delete_ok('/workspace/'.$grandchild_ws->id.'/organization/'.$organization->{id}) - ->status_is(204) - ->email_not_sent; - - -$t->get_ok('/workspace/'.$grandchild_ws->id.'/organization') - ->status_is(200) - ->json_schema_is('WorkspaceOrganizations') - ->json_is([ - { - $organization->%{qw(id name description)}, - role => 'rw', - role_via_workspace_id => $sub_ws->id, - admins => [ - { map +($_ => $admin_user->$_), qw(id name email) }, - { map +($_ => $new_user->$_), qw(id name email) }, - ], - }, - ]); - -$t->post_ok('/workspace/'.$grandchild_ws->id.'/organization', json => { - organization_id => $organization->{id}, - role => 'admin', - }) - ->status_is(204) - ->email_cmp_deeply([ - { - To => '"'.$admin_user->name.'" <'.$admin_user->email.'>, "'.$new_user->name.'" <'.$new_user->email.'>', - From => 'noreply@127.0.0.1', - Subject => 'Your Conch access has changed', - body => re(qr/^Your access to the "${\$grandchild_ws->name}" workspace at \Q$JOYENT\E\Rvia the "my first organization" organization has been adjusted to the "admin" role\./m), - }, - { - To => '"'.$super_user->name.'" <'.$super_user->email.'>', - From => 'noreply@127.0.0.1', - Subject => 'We modified an organization\'s access to your workspace', - body => re(qr/^${\$super_user->name} \(${\$super_user->email}\) modified the "my first organization" organization's\Raccess to the "${\$grandchild_ws->name}" workspace at \Q$JOYENT\E to the "admin" role\./m), - }, - ]); - -$t->get_ok('/workspace/'.$grandchild_ws->id.'/organization') - ->status_is(200) - ->json_schema_is('WorkspaceOrganizations') - ->json_is([ - { - $organization->%{qw(id name description)}, - role => 'admin', - admins => [ - { map +($_ => $admin_user->$_), qw(id name email) }, - { map +($_ => $new_user->$_), qw(id name email) }, - ], - }, - ]); - -$t->delete_ok('/workspace/'.$grandchild_ws->id.'/organization/'.$organization->{id}) - ->status_is(204) - ->email_cmp_deeply([ - { - To => '"'.$admin_user->name.'" <'.$admin_user->email.'>, "'.$new_user->name.'" <'.$new_user->email.'>', - From => 'noreply@127.0.0.1', - Subject => 'Your Conch workspaces have been updated', - body => re(qr/^Your "my first organization" organization has been removed from the\R"grandchild ws" workspace at \Q$JOYENT\E\./m), - }, - { - To => '"'.$super_user->name.'" <'.$super_user->email.'>', - From => 'noreply@127.0.0.1', - Subject => 'We removed an organization from your workspace', - body => re(qr/^${\$super_user->name} \(${\$super_user->email}\) removed the "my first organization"\Rorganization from the "grandchild ws" workspace at \Q$JOYENT\E\./m), - }, - ]); - -$t->get_ok('/workspace/'.$grandchild_ws->id.'/organization') - ->status_is(200) - ->json_schema_is('WorkspaceOrganizations') - ->json_is([ - { - $organization->%{qw(id name description)}, - role => 'rw', - role_via_workspace_id => $sub_ws->id, - admins => [ - { map +($_ => $admin_user->$_), qw(id name email) }, - { map +($_ => $new_user->$_), qw(id name email) }, - ], - }, - ]); - $t->get_ok('/organization') ->status_is(200) ->json_schema_is('Organizations') @@ -911,7 +454,7 @@ $t->delete_ok('/organization/foo') $t->delete_ok('/organization/our second organization') ->status_is(204) - ->log_debug_is('Deactivated organization our second organization, removing 1 user memberships and removing from 0 workspaces and 0 builds'); + ->log_debug_is('Deactivated organization our second organization, removing 1 user memberships and removing from 0 builds'); $t->delete_ok('/organization/our second organization') ->status_is(410); @@ -926,7 +469,7 @@ $t->get_ok('/organization') $t->delete_ok('/organization/my first organization') ->status_is(204) - ->log_debug_is('Deactivated organization my first organization, removing 2 user memberships and removing from 2 workspaces and 0 builds'); + ->log_debug_is('Deactivated organization my first organization, removing 2 user memberships and removing from 0 builds'); $t->get_ok('/organization') ->status_is(200) diff --git a/t/integration/crud/relay.t b/t/integration/crud/relay.t index 43af3b95d..9d6137c73 100644 --- a/t/integration/crud/relay.t +++ b/t/integration/crud/relay.t @@ -71,18 +71,16 @@ my $relay0 = $t->app->db_relays->find({ serial_number => 'relay0' }); my $relay1 = $t->app->db_relays->find({ serial_number => 'relay1' }); cmp_deeply( - [ $relay0->user_relay_connections ], - [ - methods( - user_id => $null_user->id, - first_seen => re(qr/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3,9}Z$/), - last_seen => re(qr/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3,9}Z$/), - ), - ], - 'user_relay_connection timestamps are set', + $relay0, + methods( + serial_number => 'relay0', + user_id => $null_user->id, + last_seen => re(qr/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3,9}Z$/), + ), + 'relay registration updates fields', ); -my $rs = $t->app->db_relays->search({ serial_number => 'relay0' })->rows(1)->prefetch('user_relay_connections'); +my $rs = $t->app->db_relays->search({ serial_number => 'relay0' })->rows(1); my ($relay_data) = $rs->hri->all; @@ -92,11 +90,6 @@ $t->post_ok('/relay/relay0/register', json => { serial => 'relay0' }) my ($new_relay_data) = $rs->hri->all; -isnt( - $new_relay_data->{user_relay_connections}[0]{last_seen}, - $relay_data->{user_relay_connections}[0]{last_seen}, - 'user_relay_connection.last_seen has been updated', -); isnt($new_relay_data->{last_seen}, $relay_data->{last_seen}, 'relay.last_seen has been updated'); is($new_relay_data->{updated}, $relay_data->{updated}, 'relay update timestamp did not change'); @@ -119,6 +112,7 @@ $t->get_ok('/relay/'.$relay0->id) created => $relay0->created, updated => $relay0->updated, last_seen => $relay0->last_seen, + user_id => $null_user->id, }); $t->get_ok('/relay/relay0') @@ -134,6 +128,7 @@ $t->get_ok('/relay/relay0') created => $relay0->created, updated => $relay0->updated, last_seen => $relay0->last_seen, + user_id => $null_user->id, }); $t->get_ok('/relay') @@ -154,6 +149,7 @@ $t_super->get_ok('/relay') created => str(($relay0, $relay1)[$_]->created), updated => str(($relay0, $relay1)[$_]->updated), last_seen => str(($relay0, $relay1)[$_]->last_seen), + user_id => $null_user->id, }, (0..1) ]); @@ -177,11 +173,16 @@ $t_super->get_ok('/relay') $t2->get_ok('/relay/relay0') ->status_is(200) ->json_schema_is('Relay') - ->json_cmp_deeply({ (map +($_ => str($relay0->$_)), qw(id serial_number name version ipaddr ssh_port created updated)), last_seen => ignore }); + ->json_cmp_deeply({ + (map +($_ => str($relay0->$_)), qw(id serial_number name version ipaddr ssh_port created)), + last_seen => ignore, + updated => ignore, + user_id => $other_user->id, + }); + isnt($t2->tx->res->json->{updated}, $relay0->updated, 'updated timestamp has changed'); } -$relay0->user_relay_connections->update({ - first_seen => '1999-01-01', +$relay0->update({ last_seen => '1999-01-01', }); @@ -204,8 +205,7 @@ $t_super->get_ok('/relay') })), 'version was updated'); my $y2000 = Conch::Time->new(year => 2000); -cmp_ok(($relay0->user_relay_connections)[0]->first_seen, '<', $y2000, 'urc first_seen was not updated'); -cmp_ok(($relay0->user_relay_connections)[0]->last_seen, '>', $y2000, 'urc last_seen was updated'); +cmp_ok($relay0->last_seen, '>', $y2000, 'relay last_seen was updated'); # now register the relays on various devices in both workspace racks... @@ -272,6 +272,7 @@ subtest list => sub { az => 'room-1a', }, last_seen => '2018-01-02T00:00:00.000Z', + user_id => $null_user->id, num_devices => 2, }, { @@ -290,6 +291,7 @@ subtest list => sub { az => 'room-0a', }, last_seen => '2018-01-04T00:00:00.000Z', + user_id => $null_user->id, num_devices => 3, }, ]); @@ -376,7 +378,7 @@ subtest delete => sub { $t_super->delete_ok('/relay/'.$relay0->id) ->status_is(204) - ->log_debug_is('Deactivated relay '.$relay0->id.', removing 2 associated device connections and 2 associated user connections'); + ->log_debug_is('Deactivated relay '.$relay0->id.', removing 2 associated device connections'); $t_super->get_ok('/relay/'.$relay0->id) ->status_is(410); diff --git a/t/integration/device-reports.t b/t/integration/device-reports.t index 9cf5ecd86..4885ca56f 100644 --- a/t/integration/device-reports.t +++ b/t/integration/device-reports.t @@ -46,38 +46,28 @@ subtest preliminaries => sub { ->status_is(409) ->json_is({ error => 'relay serial deadbeef is not registered' }); - $t->post_ok('/relay/deadbeef/register', json => { serial => 'deadbeef' }) + my $null_user = $t->generate_fixtures('user_account'); + Test::Conch->new(pg => $t->pg) + ->authenticate(email => $null_user->email) + ->post_ok('/relay/deadbeef/register', json => { serial => 'deadbeef' }) ->status_is(201); $t->post_ok('/device/TEST', json => $report_data) - ->status_is(404) - ->log_error_is('Could not find device TEST'); - - my $device = $t->generate_fixtures('device', { - serial_number => 'TEST', - hardware_product_id => $hardware_product->id, - }); - - # deactivate product, create a new product with the same sku - $hardware_product->update({ deactivated => \'now()' }); - my $new_compute = $t->app->db_hardware_products->create(do { my %cols = $hardware_product->get_columns; delete @cols{qw(id deactivated)}; \%cols }); - - $t->post_ok('/device/TEST', json => $report_data) - ->status_is(409) - ->json_is({ error => 'Report sku does not match expected hardware_product for device TEST' }); - - $t->post_ok('/device_report', json => $report_data) ->status_is(409) - ->json_is({ error => 'Report sku does not match expected hardware_product for device TEST' }); + ->json_is({ error => 'relay serial deadbeef is not registered by user '.$ro_user->name }); - $device->discard_changes; - is($device->health, 'error', 'bad reports flip device health to error'); + $t->post_ok('/relay/deadbeef/register', json => { serial => 'deadbeef' }) + ->status_is(204); - # go back to the original hardware_product - $new_compute->update({ deactivated => \'now()' }); - $hardware_product->update({ deactivated => undef }); + $t->post_ok('/device/TEST', json => $report_data) + ->status_is(404) + ->log_error_is('Could not find device TEST'); }; +my $device = $t->generate_fixtures('device', { + serial_number => 'TEST', + hardware_product_id => $hardware_product->id, +}); # create a validation plan with all current validations in it Conch::ValidationSystem->new(log => $t->app->log, schema => $t->app->schema)->load_validations; @@ -502,5 +492,45 @@ subtest 'submit report for production device' => sub { ); }; +subtest 'hardware_product is different' => sub { + my $new_product = first { $_->isa('Conch::DB::Result::HardwareProduct') } + $t->generate_fixtures('hardware_product', { + sku => 'my_new_sku', + generation_name => 'something', + validation_plan_id => $full_validation_plan->id, + }); + + my $altered_report = from_json($report); + $altered_report->{sku} = 'my_new_sku'; + $altered_report->{product_name} = 'something else'; + + $t->post_ok('/device/TEST', json => $altered_report) + ->status_is(200) + ->location_is('/device/'.$device->id) + ->json_schema_is('ValidationStateWithResults') + ->json_cmp_deeply(superhashof({ + device_id => $device->id, + status => ignore, + completed => re(qr/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3,9}Z$/), + results => [{ + id => ignore, + validation_id => ignore, + category => Conch::Validation::DeviceProductName->category, + component => ignore, + hardware_product_id => $new_product->id, + hint => ignore, + message => ignore, + status => 'fail', + }], + })); + + $device->discard_changes; + is( + $device->hardware_product_id, + $new_product->id, + 'device was updated to reflect the hardware in the report', + ); +}; + done_testing; # vim: set ts=4 sts=4 sw=4 et : diff --git a/t/integration/unsecured-endpoints.t b/t/integration/unsecured-endpoints.t index b005ef68c..d7bdbca59 100644 --- a/t/integration/unsecured-endpoints.t +++ b/t/integration/unsecured-endpoints.t @@ -27,12 +27,12 @@ $t->get_ok('/version') $t->get_ok('/foo/bar/baz') ->status_is(404) - ->json_is({ error => 'Not Found' }) + ->json_is({ error => 'Route Not Found' }) ->log_error_is('no endpoint found for: GET /foo/bar/baz'); $t->post_ok('/boop?some_arg=1') ->status_is(404) - ->json_is({ error => 'Not Found' }) + ->json_is({ error => 'Route Not Found' }) ->log_error_is('no endpoint found for: POST /boop'); $t->get_ok('/workspace')->status_is(401); diff --git a/t/integration/users.t b/t/integration/users.t index 2ce732bc3..d0fae726c 100644 --- a/t/integration/users.t +++ b/t/integration/users.t @@ -355,7 +355,11 @@ subtest 'Log out' => sub { subtest 'JWT authentication' => sub { $t->authenticate(email => $ro_user->email, bailout => 0) - ->json_has('/jwt_token'); + ->status_is(200) + ->header_exists('Last-Modified') + ->header_exists('Expires') + ->json_schema_is('Login') + ->json_cmp_deeply({ jwt_token => re(qr/\..*\./) }); my $jwt_token = $t->tx->res->json->{jwt_token}; @@ -617,11 +621,15 @@ subtest 'modify another user' => sub { $t2->get_ok('/me')->status_is(204); $t2->post_ok('/user/me/token', json => { name => 'my api token' }) + ->header_exists('Last-Modified') + ->header_exists('Expires') ->status_is(201) ->location_is('/user/me/token/my api token'); my $api_token = $t2->tx->res->json->{token}; $t2->post_ok('/user/me/token', json => { name => 'my second api token' }) + ->header_exists('Last-Modified') + ->header_exists('Expires') ->status_is(201) ->location_is('/user/me/token/my second api token'); diff --git a/t/schema.t b/t/schema.t index 086716274..11e73210c 100644 --- a/t/schema.t +++ b/t/schema.t @@ -287,7 +287,7 @@ my $json_spec_schema = $_validator->schema->data; $t->get_ok('/schema/REQUEST/hello') ->status_is(404) - ->json_is({ error => 'Not Found' }) + ->json_is({ error => 'Route Not Found' }) ->log_error_is('no endpoint found for: GET /schema/REQUEST/hello'); $t->get_ok('/schema/request/hello') diff --git a/t/workspace-role.t b/t/workspace-role.t index 69320234f..4e123ee1e 100644 --- a/t/workspace-role.t +++ b/t/workspace-role.t @@ -11,8 +11,6 @@ subtest 'user-role access' => sub { my $null_user = $t->generate_fixtures('user_account', { name => 'user with no access' }); my $ws_user = $t->generate_fixtures('user_account', { name => 'user with direct workspace access' }); - my $org_user = $t->generate_fixtures('user_account', { name => 'user with access via organization' }); - my $both_user = $t->generate_fixtures('user_account', { name => 'user with access both ways' }); my $org = $t->generate_fixtures('organization'); my $global_ws = $t->load_fixture('global_workspace'); @@ -20,12 +18,6 @@ subtest 'user-role access' => sub { $ws_user->create_related('user_workspace_roles', { workspace_id => $global_ws->id, role => 'ro' }); $ws_user->create_related('user_workspace_roles', { workspace_id => $child1_ws->id, role => 'rw' }); - $org_user->create_related('user_organization_roles', { organization_id => $org->id, role => 'admin' }); - $org->create_related('organization_workspace_roles', { workspace_id => $global_ws->id, role => 'rw' }); - $org->create_related('organization_workspace_roles', { workspace_id => $child1_ws->id, role => 'admin' }); - - $both_user->create_related('user_workspace_roles', { workspace_id => $global_ws->id, role => 'ro' }); - $both_user->create_related('user_organization_roles', { organization_id => $org->id, role => 'admin' }); my @racks = map { first { $_->isa('Conch::DB::Result::Rack') } $t->generate_fixtures('rack') } 0..1; $racks[1]->create_related('workspace_racks', { workspace_id => $child1_ws->id }); @@ -44,39 +36,23 @@ subtest 'user-role access' => sub { my @tests = ( [ 'GLOBAL', $null_user, 'none', 1 ], [ 'GLOBAL', $ws_user, 'none', 1 ], - [ 'GLOBAL', $org_user, 'none', 1 ], - [ 'GLOBAL', $both_user, 'none', 1 ], [ 'child1', $null_user, 'none', 1 ], [ 'child1', $ws_user, 'none', 1 ], - [ 'child1', $org_user, 'none', 1 ], - [ 'child1', $both_user, 'none', 1 ], [ 'GLOBAL', $null_user, 'ro', 0 ], [ 'GLOBAL', $ws_user, 'ro', 1 ], # direct user access on GLOBAL - [ 'GLOBAL', $org_user, 'ro', 1 ], # via org on GLOBAL - [ 'GLOBAL', $both_user, 'ro', 1 ], [ 'child1', $null_user, 'ro', 0 ], [ 'child1', $ws_user, 'ro', 1 ], # indirect user access via GLOBAL, and direct user access on child 1 - [ 'child1', $org_user, 'ro', 1 ], - [ 'child1', $both_user, 'ro', 1 ], [ 'GLOBAL', $null_user, 'rw', 0 ], [ 'GLOBAL', $ws_user, 'rw', 0 ], - [ 'GLOBAL', $org_user, 'rw', 1 ], - [ 'GLOBAL', $both_user, 'rw', 1 ], [ 'child1', $null_user, 'rw', 0 ], [ 'child1', $ws_user, 'rw', 1 ], # direct access on child 1 - [ 'child1', $org_user, 'rw', 1 ], - [ 'child1', $both_user, 'rw', 1 ], [ 'GLOBAL', $null_user, 'admin', 0 ], [ 'GLOBAL', $ws_user, 'admin', 0 ], - [ 'GLOBAL', $org_user, 'admin', 0 ], - [ 'GLOBAL', $both_user, 'admin', 0 ], [ 'child1', $null_user, 'admin', 0 ], [ 'child1', $ws_user, 'admin', 0 ], - [ 'child1', $org_user, 'admin', 1 ], # via org on child 1 - [ 'child1', $both_user, 'admin', 1 ], # "" ); # remember, you can set DBIC_TRACE=1 while you run the tests to see @@ -109,7 +85,7 @@ subtest 'user-role access' => sub { } }; -subtest 'user_workspace_role and organization_workspace_role role_via_for_user' => sub { +subtest 'user_workspace_role role_via_for_user' => sub { my $t = Test::Conch->new; my $user = $t->generate_fixtures('user_account'); my $org1 = $t->generate_fixtures('organization'); @@ -157,88 +133,6 @@ subtest 'user_workspace_role and organization_workspace_role role_via_for_user' ), 'direct user access via grandparent workspace, no organization', ], - [ - { - organization_workspace_role => [ { organization_id => $org1->id, workspace_id => $grandchild_ws->id, role => 'admin' } ], - }, - methods( - organization_id => $org1->id, - workspace_id => $grandchild_ws->id, - role => 'admin', - ), - 'direct organization access to the workspace, at admin', - ], - [ - { - organization_workspace_role => [ { organization_id => $org2->id, workspace_id => $child_ws->id, role => 'rw' } ], - }, - methods( - organization_id => $org2->id, - workspace_id => $child_ws->id, - role => 'rw', - ), - 'organization access via the parent workspace', - ], - [ - { - user_workspace_role => [ { user_id => $user->id, workspace_id => $child_ws->id, role => 'ro' } ], - organization_workspace_role => [ { organization_id => $org1->id, workspace_id => $global_ws->id, role => 'rw' } ], - }, - methods( - organization_id => $org1->id, - workspace_id => $global_ws->id, - role => 'rw', - ), - 'organization access via grandparent workspace prevails over a lower role granted directly to the user on the workspace', - ], - [ - { - user_workspace_role => [ { user_id => $user->id, workspace_id => $global_ws->id, role => 'rw' } ], - organization_workspace_role => [ { organization_id => $org1->id, workspace_id => $grandchild_ws->id, role => 'ro' } ], - }, - methods( - user_id => $user->id, - workspace_id => $global_ws->id, - role => 'rw', - ), - 'user access via GLOBAL prevails over a lower role granted to the organization right on the workspace', - ], - [ - { - user_workspace_role => [ { user_id => $user->id, workspace_id => $grandchild_ws->id, role => 'rw' } ], - organization_workspace_role => [ { organization_id => $org1->id, workspace_id => $grandchild_ws->id, role => 'rw' } ], - }, - methods( - user_id => $user->id, - workspace_id => $grandchild_ws->id, - role => 'rw', - ), - 'tied roles: user access to the workspace prevails over organization access to the workspace', - ], - [ - { - user_workspace_role => [ { user_id => $user->id, workspace_id => $grandchild_ws->id, role => 'rw' } ], - organization_workspace_role => [ { organization_id => $org1->id, workspace_id => $child_ws->id, role => 'rw' } ], - }, - methods( - user_id => $user->id, - workspace_id => $grandchild_ws->id, - role => 'rw', - ), - 'tied roles: user access to the workspace prevails over organization access to the parent workspace', - ], - [ - { - user_workspace_role => [ { user_id => $user->id, workspace_id => $child_ws->id, role => 'rw' } ], - organization_workspace_role => [ { organization_id => $org1->id, workspace_id => $grandchild_ws->id, role => 'rw' } ], - }, - methods( - organization_id => $org1->id, - workspace_id => $grandchild_ws->id, - role => 'rw', - ), - 'tied roles: organization access directly to the workspace prevails over user access to the parent workspace', - ], ); foreach my $test_data (@permutations) { diff --git a/templates/email/workspace_organization_add_admins.txt.ep b/templates/email/workspace_organization_add_admins.txt.ep deleted file mode 100644 index e6593ef85..000000000 --- a/templates/email/workspace_organization_add_admins.txt.ep +++ /dev/null @@ -1,8 +0,0 @@ -Hello, - -<%= $user->name %> (<%= $user->email %>) added the "<%= $organization %>" organization to the -"<%= $workspace %>" workspace at Joyent Conch (https://<%= host %>) with the "<%= $role %>" role. - -Thank you, -Joyent Build Ops Team - diff --git a/templates/email/workspace_organization_add_members.txt.ep b/templates/email/workspace_organization_add_members.txt.ep deleted file mode 100644 index eb6bd3f46..000000000 --- a/templates/email/workspace_organization_add_members.txt.ep +++ /dev/null @@ -1,7 +0,0 @@ -Hello, - -Your "<%= $organization %>" organization has been added to the -"<%= $workspace %>" workspace at Joyent Conch (https://<%= host %>) with the "<%= $role %>" role. - -Thank you, -Joyent Build Ops Team diff --git a/templates/email/workspace_organization_remove_admins.txt.ep b/templates/email/workspace_organization_remove_admins.txt.ep deleted file mode 100644 index 20f26e89d..000000000 --- a/templates/email/workspace_organization_remove_admins.txt.ep +++ /dev/null @@ -1,7 +0,0 @@ -Hello, - -<%= $user->name %> (<%= $user->email %>) removed the "<%= $organization %>" -organization from the "<%= $workspace %>" workspace at Joyent Conch (https://<%= host %>). - -Thank you, -Joyent Build Ops Team diff --git a/templates/email/workspace_organization_remove_members.txt.ep b/templates/email/workspace_organization_remove_members.txt.ep deleted file mode 100644 index 01e9f9561..000000000 --- a/templates/email/workspace_organization_remove_members.txt.ep +++ /dev/null @@ -1,7 +0,0 @@ -Hello, - -Your "<%= $organization %>" organization has been removed from the -"<%= $workspace %>" workspace at Joyent Conch (https://<%= host %>). - -Thank you, -Joyent Build Ops Team diff --git a/templates/email/workspace_organization_update_admins.txt.ep b/templates/email/workspace_organization_update_admins.txt.ep deleted file mode 100644 index c1c8cd4f6..000000000 --- a/templates/email/workspace_organization_update_admins.txt.ep +++ /dev/null @@ -1,8 +0,0 @@ -Hello, - -<%= $user->name %> (<%= $user->email %>) modified the "<%= $organization %>" organization's -access to the "<%= $workspace %>" workspace at Joyent Conch (https://<%= host %>) to the "<%= $role %>" role. - -Thank you, -Joyent Build Ops Team - diff --git a/templates/email/workspace_organization_update_members.txt.ep b/templates/email/workspace_organization_update_members.txt.ep deleted file mode 100644 index 6fec4e6b6..000000000 --- a/templates/email/workspace_organization_update_members.txt.ep +++ /dev/null @@ -1,7 +0,0 @@ -Hello, - -Your access to the "<%= $workspace %>" workspace at Joyent Conch (https://<%= host %>) -via the "<%= $organization %>" organization has been adjusted to the "<%= $role %>" role. - -Thank you, -Joyent Build Ops Team