Skip to content

Commit

Permalink
WIP for #1070
Browse files Browse the repository at this point in the history
  • Loading branch information
oharsta committed Nov 20, 2023
1 parent ee2bc50 commit d0fa649
Show file tree
Hide file tree
Showing 19 changed files with 245 additions and 37 deletions.
8 changes: 5 additions & 3 deletions client/src/components/redesign/ServiceConnectionRequests.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {ReactComponent as ChevronLeft} from "../../icons/chevron-left.svg";

import "./ServiceConnectionRequests.scss";
import "./UserColumn.scss";
import {stopEvent} from "../../utils/Utils";
import {isEmpty, stopEvent} from "../../utils/Utils";
import I18n from "../../locale/I18n";
import Button from "../Button";
import {setFlash} from "../../utils/Flash";
Expand Down Expand Up @@ -58,10 +58,11 @@ class ServiceConnectionRequests extends React.Component {
approveServiceConnectionRequest = () => {
const {service} = this.props;
const serviceConnectionRequest = this.getSelectedServiceConnectionRequest();
const name = isEmpty(service) ? serviceConnectionRequest.service.name : service.name;
this.confirm(() => {
this.refreshAndFlash(approveServiceConnectionRequest(serviceConnectionRequest),
I18n.t("serviceConnectionRequest.flash.accepted", {
name: service.name
name: name
}), () => this.componentDidMount())
}, I18n.t("serviceConnectionRequest.approveConfirmation"), false);

Expand All @@ -70,10 +71,11 @@ class ServiceConnectionRequests extends React.Component {
denyServiceConnectionRequest = () => {
const {service} = this.props;
const serviceConnectionRequest = this.getSelectedServiceConnectionRequest();
const name = isEmpty(service) ? serviceConnectionRequest.service.name : service.name;
this.confirm(() => {
this.refreshAndFlash(denyServiceConnectionRequest(serviceConnectionRequest),
I18n.t("serviceConnectionRequest.flash.declined", {
name: service.name
name: name
}), () => this.componentDidMount())
}, I18n.t("serviceConnectionRequest.declineConfirmation"), true);
};
Expand Down
2 changes: 2 additions & 0 deletions client/src/locale/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,8 @@ const en = {
schacHomeOrganisationTooltip: "The domain names users of this organisation log in with. These users can request or create collaborations with your organisation.",
collaborationCreationAllowed: "Users can create collaborations straightaway",
collaborationCreationAllowedTooltip: "Allows users from your organisation to create a collaboration without requiring approval from an organisation admin or manager",
serviceConnectionRequiresApproval: "Service connection request must be approved by the Organisation admin",
serviceConnectionRequiresApprovalTooltip: "If checked then service connection requests must first be approved by an Organisation admin",
collaborationCreationLabel: "Users from these domains",
collaborationCreationIsAllowed: "Can create collaborations",
collaborationCreationNotAllowed: "Can request collaborations",
Expand Down
2 changes: 2 additions & 0 deletions client/src/locale/nl.js
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,8 @@ const nl = {
schacHomeOrganisationTooltip: "De domeinnamen waarmee gebruikers van deze organisatie inloggen. Deze gebruikers kunnen samenwerkingen aanmaken of aanvragen bij je organisatie.",
collaborationCreationAllowed: "Gebruikers kunnen zonder goedkeuring een samenwerking aanmaken",
collaborationCreationAllowedTooltip: "Sta toe dat gebruikers van de organisatie samenwerkingen aanmaken zonder goedkeuring van de organisatiebeheerder of -manager",
serviceConnectionRequiresApproval: "Verzoek voor dienst koppeling moet worden goedgekeurd door de organisatie beheerder",
serviceConnectionRequiresApprovalTooltip: "Indien geselecteerd, dan moet een verzoek voor een dienst koppeling eerst worden goedgekeurd door de organisatie beheerder",
collaborationCreationLabel: "Gebruikers van deze domeinen",
collaborationCreationIsAllowed: "Kunnen samenwerkingen aanmaken",
collaborationCreationNotAllowed: "Kunnen samenwerkingen aanvragen",
Expand Down
35 changes: 35 additions & 0 deletions client/src/pages/OrganisationDetail.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import "./OrganisationDetail.scss";
import I18n from "../locale/I18n";
import {isEmpty, stopEvent} from "../utils/Utils";
import Tabs from "../components/Tabs";
import {ReactComponent as ServiceConnectionRequestsIcon} from "../icons/connections.svg";
import {ReactComponent as PlatformAdminIcon} from "../icons/users.svg";
import {ReactComponent as ServicesIcon} from "../icons/services.svg";
import {ReactComponent as ApiKeysIcon} from "../icons/security.svg";
Expand All @@ -33,6 +34,7 @@ import {ReactComponent as MembersIcon} from "../icons/single-neutral.svg";
import Users from "../components/redesign/Users";
import {ButtonType, MetaDataList} from "@surfnet/sds";
import {isInvitationExpired} from "../utils/Date";
import ServiceConnectionRequests from "../components/redesign/ServiceConnectionRequests";

class OrganisationDetail extends React.Component {

Expand Down Expand Up @@ -155,6 +157,7 @@ class OrganisationDetail extends React.Component {
this.getCollaborationsTab(organisation),
this.getCollaborationRequestsTab(organisation),
this.getOrganisationAdminsTab(organisation, user),
this.getServiceConnectionRequestsTab(organisation),
user.admin ? this.getServicesTab(organisation) : null,
config.api_keys_enabled ? this.getAPIKeysTab(organisation, user) : null,
this.getUsersTab(organisation)
Expand Down Expand Up @@ -212,6 +215,12 @@ class OrganisationDetail extends React.Component {
</div>)
}

refresh = () => {
const params = this.props.match.params;
const organisation_id = parseInt(params.id, 10);
organisationById(organisation_id).then(json => this.setState({organisation: json}));
}

getCollaborationRequestsTab = organisation => {
const crl = (organisation.collaboration_requests || []).filter(cr => cr.status === "open").length;
const tabLabel = I18n.t("home.tabs.collaborationRequests", {count: (organisation.collaboration_requests || []).length});
Expand All @@ -224,6 +233,32 @@ class OrganisationDetail extends React.Component {

}

getServiceConnectionRequestsTab = organisation => {
const serviceConnectionRequests = organisation.collaborations
.map(coll => {
coll.service_connection_requests.forEach(scr => scr.collaboration = coll)
return coll.service_connection_requests
})
.flat()
.filter(scr => scr.pending_organisation_approval);
if (serviceConnectionRequests.length === 0) {
return null;
}
const nbr = serviceConnectionRequests.length;

return (
<div key="serviceConnectionRequests" name="serviceConnectionRequests"
label={I18n.t("home.tabs.serviceConnectionRequests")}
icon={<ServiceConnectionRequestsIcon/>}
notifier={serviceConnectionRequests.length > 0 ? nbr : null}>
<ServiceConnectionRequests
refresh={this.refresh}
serviceConnectionRequests={serviceConnectionRequests}
modelName={"serviceConnectionRequests"}
{...this.props} />
</div>);
}

tabChanged = (name, id) => {
const orgId = id || this.state.organisation.id;
this.setState({tab: name}, () =>
Expand Down
31 changes: 28 additions & 3 deletions client/src/pages/OrganisationForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class OrganisationForm extends React.Component {
services_restricted: false,
on_boarding_msg: "",
on_boarding_changed: false,
service_connection_requires_approval: false,
categoryOptions: categoryOptions,
category: category,
administrators: [],
Expand Down Expand Up @@ -218,7 +219,7 @@ class OrganisationForm extends React.Component {
if (this.isValid()) {
const {
name, short_name, administrators, message, schac_home_organisations, description, logo,
on_boarding_msg, category, services_restricted, units
on_boarding_msg, category, services_restricted, units, service_connection_requires_approval
} = this.state;
this.setState({loading: true});
createOrganisation({
Expand All @@ -231,6 +232,7 @@ class OrganisationForm extends React.Component {
message,
description,
services_restricted,
service_connection_requires_approval,
logo,
on_boarding_msg
})
Expand All @@ -257,8 +259,19 @@ class OrganisationForm extends React.Component {
doUpdate = () => {
if (this.isValid()) {
const {
name, description, organisation, schac_home_organisations, collaboration_creation_allowed,
short_name, identifier, logo, on_boarding_msg, category, services_restricted, units
name,
description,
organisation,
schac_home_organisations,
collaboration_creation_allowed,
short_name,
identifier,
logo,
on_boarding_msg,
category,
services_restricted,
units,
service_connection_requires_approval
} = this.state;
this.setState({loading: true});
updateOrganisation({
Expand All @@ -268,6 +281,7 @@ class OrganisationForm extends React.Component {
units: units.filter(unit => !isEmpty(unit.name)),
schac_home_organisations,
collaboration_creation_allowed,
service_connection_requires_approval,
services_restricted,
short_name,
identifier,
Expand Down Expand Up @@ -313,6 +327,7 @@ class OrganisationForm extends React.Component {
short_name,
schac_home_organisations,
collaboration_creation_allowed,
service_connection_requires_approval,
services_restricted,
logo,
on_boarding_msg,
Expand Down Expand Up @@ -446,6 +461,16 @@ class OrganisationForm extends React.Component {
readOnly={isEmpty(schac_home_organisations)}
/>

{(user.admin || service_connection_requires_approval) &&
<CheckBox name={"service_connection_requires_approval"}
value={service_connection_requires_approval}
tooltip={I18n.t("organisation.serviceConnectionRequiresApprovalTooltip")}
onChange={() => this.setState({service_connection_requires_approval: !service_connection_requires_approval})}
info={I18n.t("organisation.serviceConnectionRequiresApproval")}
readOnly={!user.admin}
/>}


<OrganisationOnBoarding
on_boarding_msg={(isEmpty(on_boarding_msg) && isNew && !on_boarding_changed) ? I18n.t("organisation.onBoarding.template") : on_boarding_msg}
saveOnBoarding={val => this.setState({on_boarding_msg: val, on_boarding_changed: true})}/>
Expand Down
5 changes: 3 additions & 2 deletions client/src/pages/ServiceDetail.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,11 @@ class ServiceDetail extends React.Component {
}

getServiceConnectionRequestTab = (service, serviceConnectionRequests) => {
const nbr = (serviceConnectionRequests || []).length;
serviceConnectionRequests = (serviceConnectionRequests || []).filter(scr => !scr.pending_organisation_approval);
const nbr = serviceConnectionRequests.length;
return (
<div key="serviceConnectionRequests" name="serviceConnectionRequests"
label={I18n.t("home.tabs.serviceConnectionRequests", {count: serviceConnectionRequests.length})}
label={I18n.t("home.tabs.serviceConnectionRequests")}
icon={<ServiceConnectionRequestsIcon/>}
notifier={nbr > 0 ? nbr : null}>
<ServiceConnectionRequests
Expand Down
13 changes: 12 additions & 1 deletion server/api/organisation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
from server.db.defaults import default_expiry_date, cleanse_short_name
from server.db.defaults import full_text_search_autocomplete_limit
from server.db.domain import Organisation, OrganisationMembership, OrganisationInvitation, User, \
CollaborationRequest, SchacHomeOrganisation, Collaboration, CollaborationMembership, Invitation
CollaborationRequest, SchacHomeOrganisation, Collaboration, CollaborationMembership, Invitation, \
ServiceConnectionRequest
from server.db.models import update, save, delete
from server.mail import mail_organisation_invitation, mail_platform_admins
from server.scim.events import broadcast_organisation_deleted
Expand Down Expand Up @@ -197,6 +198,12 @@ def organisation_by_id(organisation_id):
.selectinload(CollaborationRequest.requester)) \
.options(selectinload(Organisation.collaborations)
.selectinload(Collaboration.tags)) \
.options(selectinload(Organisation.collaborations)
.selectinload(Collaboration.service_connection_requests)
.selectinload(ServiceConnectionRequest.requester)) \
.options(selectinload(Organisation.collaborations)
.selectinload(Collaboration.service_connection_requests)
.selectinload(ServiceConnectionRequest.service)) \
.filter(Organisation.id == organisation_id)

api_call = request_context.is_authorized_api_call
Expand Down Expand Up @@ -415,6 +422,10 @@ def override_func():
if not is_application_admin() and organisation.services_restricted:
data["services_restricted"] = True

approval_changed = organisation.service_connection_requires_approval != data["service_connection_requires_approval"]
if not is_application_admin() and approval_changed:
data["service_connection_requires_approval"] = organisation.service_connection_requires_approval

# Corner case: user removed name and added the exact same name again, prevent duplicate entry
if "schac_home_organisations" in data:
existing_names = [sho.name for sho in organisation.schac_home_organisations]
Expand Down
Loading

0 comments on commit d0fa649

Please sign in to comment.