Skip to content

Commit

Permalink
Merge pull request #44 from stuartcampbell/synchweb-permissions
Browse files Browse the repository at this point in the history
Add SynchWeb user permission for proposal directories
  • Loading branch information
stuartcampbell committed Feb 12, 2024
2 parents 06fd621 + 0316e4a commit a1642c4
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 9 deletions.
40 changes: 39 additions & 1 deletion src/nsls2api/api/v1/beamline_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
ProposalDirectoriesList,
)

from nsls2api.infrastructure.security import get_api_key
from nsls2api.infrastructure.logging import logger
from nsls2api.infrastructure.security import get_api_key, validate_admin_role
from nsls2api.models.beamlines import Beamline, BeamlineService, DetectorList
from nsls2api.services import beamline_service

Expand Down Expand Up @@ -168,3 +169,40 @@ async def get_beamline_operator_username(name: str):
detail=f"No operator user has been defined for the {name} beamline",
)
return operator_user


@router.get("/beamline/{name}/services/", response_model=list[BeamlineService])
async def get_beamline_services(name: str):
beamline_services = await beamline_service.all_services(name)
if beamline_services is None:
raise HTTPException(
status_code=404, detail=f"Beamline named {name} could not be found"
)
return beamline_services


@router.put(
"/beamline/{name}/services/",
include_in_schema=True,
response_model=BeamlineService,
dependencies=[Depends(validate_admin_role)],
)
async def add_beamline_service(name: str, service: BeamlineService):
logger.info(f"Adding service {service.name} to beamline {name}")

new_service = await beamline_service.add_service(
beamline_name=name,
service_name=service.name,
used_in_production=service.used_in_production,
host=service.host,
port=service.port,
uri=service.uri,
)

if new_service is None:
raise HTTPException(
status_code=404,
detail=f"Service {service.name} already exists in beamline {name}",
)

return new_service
18 changes: 13 additions & 5 deletions src/nsls2api/models/beamlines.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ class Detector(pydantic.BaseModel):
name: str
directory_name: str | None = None


class DetectorView(pydantic.BaseModel):
detectors: list[Detector] | None = None

class Settings:
projection = {
"detectors": "$detectors",
Expand All @@ -23,12 +24,12 @@ class DetectorList(pydantic.BaseModel):
count: int | None = None



class BeamlineService(pydantic.BaseModel):
name: str
host: Optional[str]
port: Optional[int]
uri: Optional[str]
used_in_production: Optional[bool] = None
host: Optional[str] = None
port: Optional[int] = None
uri: Optional[str] = None


class ServicesOnly(pydantic.BaseModel):
Expand Down Expand Up @@ -98,6 +99,13 @@ class Settings:
projection = {"username": "$service_accounts.lsdc"}


class BeamlineServicesSynchwebView(pydantic.BaseModel):
synchweb: BeamlineService

class Settings:
projection = {"synchweb": "$services.name"}


class DataRootDirectoryView(pydantic.BaseModel):
data_root: Optional[str] = None

Expand Down
100 changes: 100 additions & 0 deletions src/nsls2api/services/beamline_service.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import datetime
from pathlib import Path
from typing import Optional

from beanie.odm.operators.find.comparison import In
from beanie.odm.operators.find.array import ElemMatch

from nsls2api.api.models.beamline_model import AssetDirectoryGranularity
from nsls2api.infrastructure.logging import logger
from nsls2api.models.beamlines import (
Beamline,
BeamlineService,
Detector,
DetectorView,
ServicesOnly,
Expand Down Expand Up @@ -234,3 +238,99 @@ async def proposal_directory_skeleton(name: str):
directory_list.append(default_directory)

return directory_list


async def check_service_exists(beamline_name: str, service_name: str) -> bool:
"""
Check if a service exists in a given beamline.
Args:
beamline_name (str): The name of the beamline.
service_name (str): The name of the service.
Returns:
bool: True if the service exists in the beamline, False otherwise.
"""

# Get the beamline from the database
beamline = await Beamline.find_one(Beamline.name == beamline_name.upper())

# If the beamline does not exist, then by definition it can't have the service
if beamline is None:
return False

current_services = await beamline.find(
ElemMatch(Beamline.services, {"name": service_name})
).to_list()

print(f"current_services: {current_services}")

if current_services is None or len(current_services) == 0:
logger.info(f"Service {service_name} not found in beamline {beamline_name}")
return False
else:
logger.info(f"Service {service_name} found in beamline {beamline_name}")
return True

return False


async def add_service(
beamline_name: str,
service_name: str,
used_in_production: bool = False,
host: str = None,
port: int = None,
uri: str = None,
) -> Optional[BeamlineService]:
"""
Add a new service to a beamline.
Args:
beamline_name (str): The name of the beamline.
service_name (str): The name of the service.
used_in_production (bool, optional): Whether the service is used in production. Defaults to False.
host (str, optional): The host of the service. Defaults to None.
port (int, optional): The port of the service. Defaults to None.
uri (str, optional): The URI of the service. Defaults to None.
Returns:
Optional[BeamlineService]: The newly created BeamlineService object if successful, None otherwise.
"""
beamline = await Beamline.find_one(Beamline.name == beamline_name.upper())

service = BeamlineService(
name=service_name,
used_in_production=used_in_production,
host=host,
port=port,
uri=uri,
)

if await check_service_exists(beamline_name, service_name):
logger.info(
f"Service {service_name} already exists in beamline {beamline_name}"
)
return None
else:
beamline.services.append(service)
beamline.last_updated = datetime.datetime.now()
await beamline.save()

return service


async def uses_synchweb(name: str) -> bool:
"""
Check if the specified beamline uses the SynchWeb service.
Args:
name (str): The name of the beamline.
Returns:
bool: True if the beamline uses SynchWeb, False otherwise.
"""
if await check_service_exists(name, "synchweb"):
return True
else:
return False
20 changes: 17 additions & 3 deletions src/nsls2api/services/proposal_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ async def proposal_by_id(proposal_id: int) -> Optional[Proposal]:

return proposal


# Get a list of proposals that match the search criteria
async def search_proposals(search_text: str) -> Optional[list[Proposal]]:
query = Text(search=search_text, case_sensitive=False)
Expand All @@ -105,14 +106,18 @@ async def search_proposals(search_text: str) -> Optional[list[Proposal]]:
await Proposal.find(query).sort([("score", {"$meta": "textScore"})]).to_list()
)

logger.info(f"Found {len(found_proposals)} proposals by search for the text '{search_text}'")
logger.info(
f"Found {len(found_proposals)} proposals by search for the text '{search_text}'"
)

# Now do a special search just for the proposal id
found_proposals += await Proposal.find(
RegEx(Proposal.proposal_id, pattern=f"{search_text}")
).to_list()

logger.info(f"Found {len(found_proposals)} proposals after searching for '{search_text}' in just the proposal_id field")
logger.info(
f"Found {len(found_proposals)} proposals after searching for '{search_text}' in just the proposal_id field"
)

return found_proposals

Expand Down Expand Up @@ -316,14 +321,23 @@ async def directories(proposal_id: int):
users_acl.append({"nsls2data": "rw"})
users_acl.append({f"{service_accounts.workflow}": "rw"})
users_acl.append({f"{service_accounts.ioc}": "rw"})

# If beamline uses SynchWeb then add access for synchweb user
if beamline_service.uses_synchweb(beamline_tla):
users_acl.append({"synchweb": "r"})

groups_acl.append({str(proposal.data_session): "rw"})

# Add LSDC beamline users for the appropriate beamlines (i.e. if the account is defined)
if service_accounts.lsdc:
users_acl.append({f"{service_accounts.lsdc}": "rw"})

groups_acl.append({"n2sn-right-dataadmin": "rw"})
groups_acl.append({f"{await beamline_service.custom_data_admin_group(beamline_tla)}": "rw"})
groups_acl.append(
{
f"{await beamline_service.custom_data_admin_group(beamline_tla)}": "rw"
}
)

directory = {
"path": str(
Expand Down

0 comments on commit a1642c4

Please sign in to comment.