Skip to content

Commit

Permalink
Merge branch 'master' into rfc79-inc-data-upload
Browse files Browse the repository at this point in the history
  • Loading branch information
forus authored Jul 16, 2024
2 parents 02e8f07 + 9fbfa19 commit c19eb10
Show file tree
Hide file tree
Showing 14 changed files with 630 additions and 128 deletions.
61 changes: 61 additions & 0 deletions docs/Create-And-Publish-Virtual-Study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Create and Publish Virtual Study

A [Virtual Study](./user-guide/faq.md#what-is-a-virtual-study) defines a subset or a combination of samples from one or more studies in the system.

*Note*: To publish or un-publish a virtual study, your cBioPortal instance must be configured with `session.endpoint.publisher-api-key` in the `application.properties`.

## Create Virtual Study

To create a virtual study in cBioPortal, follow these steps:

1. Define the desired filters on the study or multiple studies summary page.
2. Click the button with the bookmark icon () in the top right corner of the screen.
3. Provide a title and description, then click the Save button. You will see a link that looks like:

```
https://<cbioportal_host>/study?id=<virtual_study_id>
```

4. Save the virtual study link or ID if you want to publish it.

If you are logged in, this virtual study will appear in the `My Virtual Studies` section on the landing page.
You can always find the ID of the virtual study from the URL of the page that opens after clicking on it.

## Publish Virtual Study

To publish a virtual study, you need to supply the publisher API key in the `X-PUBLISHER-API-KEY` header.

Here is a curl command to publish a virtual study:
```shell
curl \
-X POST \
-H 'X-PUBLISHER-API-KEY: <session.endpoint.publisher-api-key>' \
-v 'http://<cbioportal_host>/api/public_virtual_studies/<virtual_study_id>'
```
The published virtual study will appear under the `Public Virtual Studies` section (next to the `My Virtual Studies` section) on the landing page for all users of cBioPortal.

While publishing, you can specify the PubMed ID (`pmid`) and `typeOfCancerId` of the virtual study using the following command:
```shell
curl \
-X POST \
-H 'X-PUBLISHER-API-KEY: <session.endpoint.publisher-api-key>' \
-v 'http://<cbioportal_host>/api/public_virtual_studies/<virtual_study_id>?pmid=<pmid>&typeOfCancerId=<code>'
```

The type of cancer code should match the known types of cancers in the cBioPortal database.
If the type of cancer is specified, the published virtual study will appear under the respective cancer section on the landing page.
Specifying the `pmid` enables a link to the PubMed page of the study.

## Un-publish Virtual Study

To un-publish a virtual study, you need to supply the publisher API key in the `X-PUBLISHER-API-KEY` header.
After un-publishing, virtual study will no longer be displayed in the `Public Virtual Studies` section on the landing page.
However, it reappears in the `My Virtual Studies` section for the owner.

Here is the command to un-publish a virtual study:
```shell
curl \
-X DELETE \
-H 'X-PUBLISHER-API-KEY: <session.endpoint.publisher-api-key>' \
-v 'http://<cbioportal_host>/api/public_virtual_studies/<virtual_study_id>'
```
3 changes: 3 additions & 0 deletions docs/Data-Loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@ To remove a study, the [cbioportalImporter script](/Data-Loading-Maintaining-Stu

## Example studies
Examples for the different types of data are available on the [File Formats](/File-Formats.md) page. The Provisional TCGA studies, downloadable from the [Data Sets section](https://www.cbioportal.org/datasets) are complete studies that can be used as reference when creating data files.

## Public Virtual Studies
If your new study data is a subset or a combination of existing studies in the system, consider using [Public Virtual Studies](./Create-And-Publish-Virtual-Study.md) instead of duplicating data.
14 changes: 11 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<groupId>org.cbioportal</groupId>
<artifactId>cbioportal</artifactId>
<version>6.0.10-SNAPSHOT</version>
<version>6.0.13-SNAPSHOT</version>

<description>cBioPortal for Cancer Genomics</description>

Expand All @@ -27,7 +27,7 @@
<!-- TODO replace with version of cbioportal frontend with compatible
login url-->
<frontend.groupId>com.github.cbioportal</frontend.groupId>
<frontend.version>v6.0.9</frontend.version>
<frontend.version>v6.0.12</frontend.version>

<!-- THIS SHOULD BE KEPT IN SYNC TO VERSION IN CGDS.SQL -->
<db.version>2.13.1</db.version>
Expand Down Expand Up @@ -98,6 +98,7 @@
<selenium_chrome_driver.version>3.14.0</selenium_chrome_driver.version>
<selenium.version>4.17.0</selenium.version>
<sentry.version>7.1.0</sentry.version>
<apache_httpclient.version>5.2.1</apache_httpclient.version>


<!-- No sure what these are for -->
Expand Down Expand Up @@ -346,7 +347,14 @@
<artifactId>sentry-spring-boot-starter-jakarta</artifactId>
<version>${sentry.version}</version>
</dependency>
</dependencies>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>${apache_httpclient.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

<dependencyManagement>
<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,22 @@ public List<Integer> getVisibleSampleInternalIdsForClinicalTable(List<String> st
Integer offset = PaginationCalculator.offset(pageSize, pageNumber);
Boolean sortAttrIsNumber = null;
Boolean sortIsPatientAttr = null;
if (sortAttrId != null && ! sortAttrId.isEmpty()) {
ClinicalAttribute clinicalAttributeMeta = getClinicalAttributeMeta(studyIds, sortAttrId);
sortAttrIsNumber = clinicalAttributeMeta.getDatatype().equals("NUMBER");
sortIsPatientAttr = clinicalAttributeMeta.getPatientAttribute();

if (sortAttrId != null) {
if (sortAttrId.equals("patientId") || sortAttrId.equals("sampleId")) {
//these are both false because patientId and sampleId are actually not
//clinical attributes and are never numbers
sortAttrIsNumber = false;
sortIsPatientAttr = false;
} else {
ClinicalAttribute clinicalAttributeMeta = getClinicalAttributeMeta(studyIds, sortAttrId);
sortAttrIsNumber = clinicalAttributeMeta.getDatatype().equals("NUMBER");
sortIsPatientAttr = clinicalAttributeMeta.getPatientAttribute();
}
}



return clinicalDataMapper.getVisibleSampleInternalIdsForClinicalTable(studyIds, sampleIds,"SUMMARY", pageSize,
offset, searchTerm, sortAttrId, sortAttrIsNumber, sortIsPatientAttr, direction);
}
Expand Down
106 changes: 10 additions & 96 deletions src/main/java/org/cbioportal/proxy/ProxyController.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,17 @@
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.security.core.Authentication;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
import java.util.regex.Pattern;

// TODO Consider creating separate DispatcherServlets as in the original web.xml
Expand Down Expand Up @@ -64,27 +57,13 @@ public class ProxyController {

@Value("${darwin.regex:Test}")
private String darwinRegex;


@RequestMapping("/**")
public String proxy(@RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request)
throws URISyntaxException {
HttpHeaders httpHeaders = initHeaders(request);

// TODO when reimplemeting different dispatcherservlets with different context roots
// reset this to 'String requestPathInfo = request.getPathInfo();'
String requestPathInfo = request.getPathInfo() == null? request.getServletPath() : request.getPathInfo();
requestPathInfo = requestPathInfo.replace("proxy/", "");
return exchangeData(body,
buildUri(requestPathInfo, request.getQueryString(), false),
method,
httpHeaders,
String.class
).getBody();
}
//TODO: Hey figure out if we need this
@RequestMapping("/legacy/proxy/oncokb/**")
public String legacyProxyOncokb(
/**
* This dev endpoint can be used (with a personal access token) instead of the production endpoint.
* This is useful when debugging the frontend proxy API calls.
*/
@RequestMapping("/dev/oncokb/**")
public String devProxyOncokb(
@RequestBody(required = false) String body,
HttpMethod method,
HttpServletRequest request
Expand All @@ -94,7 +73,7 @@ public String legacyProxyOncokb(

return exchangeOncokbData(
body,
request.getPathInfo().replaceFirst("/oncokb", ""),
request.getPathInfo().replaceFirst("/dev/oncokb", ""),
request.getQueryString(),
method,
getOncokbHeaders(request, token)
Expand Down Expand Up @@ -168,32 +147,6 @@ private HttpHeaders getOncokbHeaders(HttpServletRequest request, String token) {
return httpHeaders;
}

//TODO: Figure out what is different (Rebased from Spring Boot Branch)
@RequestMapping("/proxy/oncokb/**")
public String proxyOncokb(@RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request)
throws URISyntaxException {

if (!this.showOncokb) {
throw new OncoKBServiceIsDisabledException();
}

HttpHeaders httpHeaders = initHeaders(request);

if (!ObjectUtils.isEmpty(this.oncokbToken)) {
httpHeaders.add("Authorization", "Bearer " + this.oncokbToken);
}

// TODO when reimplemeting different dispatcherservlets with different context roots
// reset this to 'String requestPathInfo = request.getPathInfo();'
String requestPathInfo = request.getPathInfo() == null? request.getServletPath() : request.getPathInfo();
String replaceString = request.getPathInfo() == null? "/proxy/oncokb" : "/oncokb";
return exchangeData(body,
buildUri(this.oncokbApiUrl + requestPathInfo.replaceFirst(replaceString, ""), request.getQueryString()),
method,
httpHeaders,
String.class).getBody();
}

@GetMapping("/checkDarwinAccess")
public ResponseEntity<String> checkDarwinAccess(HttpServletRequest request, Authentication authentication) {
String user = authentication != null ? authentication.getName(): "anonymousUser";
Expand Down Expand Up @@ -226,54 +179,15 @@ private <T> ResponseEntity<T> exchangeData(String body, URI uri, HttpMethod meth
return restTemplate.exchange(uri, method, new HttpEntity<>(body, httpHeaders), responseType);
}


private InputStream getResourceStream(String propertiesFileName)
{
String resourceFilename = null;
InputStream resourceFIS = null;

try {
String home = System.getenv("PORTAL_HOME");
if (home != null) {
resourceFilename =
home + File.separator + propertiesFileName;
resourceFIS = new FileInputStream(resourceFilename);
}
} catch (FileNotFoundException e) {
}

if (resourceFIS == null) {
resourceFIS = this.getClass().getClassLoader().
getResourceAsStream(propertiesFileName);
}

return resourceFIS;
}
private Properties loadProperties(InputStream resourceInputStream)
{
Properties properties = new Properties();

try {
properties.load(resourceInputStream);
resourceInputStream.close();
}
catch (IOException e) {
System.out.println("Error loading properties file: " + e.getMessage());
}

return properties;
}

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "OncoKB service is disabled")
public class OncoKBServiceIsDisabledException extends RuntimeException {
}

@ResponseStatus(code = HttpStatus.FORBIDDEN, reason = "No OncoKB access token is provided")
public class NoOncoKBTokenProvidedException extends RuntimeException {
}

@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "Fair Usage Agreement is missing")
public class OncoKBProxyUserAgreementException extends RuntimeException {
}

@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "Unknown/Invalid hostname")
public class UnknownHostException extends RuntimeException {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

import org.cbioportal.persistence.cachemaputil.CacheMapUtil;
import org.cbioportal.security.CancerStudyPermissionEvaluator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
Expand All @@ -17,28 +15,22 @@
// We are allowing users to enable method_authorization if optional_oauth2 is selected
@ConditionalOnExpression("{'oauth2','saml', 'saml_plus_basic'}.contains('${authenticate}') or ('optional_oauth2' eq '${authenticate}' and 'true' eq '${security.method_authorization_enabled}')")
public class MethodSecurityConfig {
@Value("${app.name:}")
private String appName;

@Value("${filter_groups_by_appname:true}")
private String doFilterGroupsByAppName;

@Value("${always_show_study_group:}")
private String alwaysShowCancerStudyGroup;

@Autowired
private CacheMapUtil cacheMapUtil;

@Bean
public MethodSecurityExpressionHandler createExpressionHandler() {
public CancerStudyPermissionEvaluator cancerStudyPermissionEvaluator(
@Value("${app.name:}") String appName,
@Value("${filter_groups_by_appname:true}") String doFilterGroupsByAppName,
@Value("${always_show_study_group:}") String alwaysShowCancerStudyGroup,
CacheMapUtil cacheMapUtil
) {
return new CancerStudyPermissionEvaluator(appName, doFilterGroupsByAppName, alwaysShowCancerStudyGroup, cacheMapUtil);
}

@Bean
public MethodSecurityExpressionHandler createExpressionHandler(CancerStudyPermissionEvaluator cancerStudyPermissionEvaluator) {
DefaultMethodSecurityExpressionHandler expressionHandler =
new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(cancerStudyPermissionEvaluator());
expressionHandler.setPermissionEvaluator(cancerStudyPermissionEvaluator);
return expressionHandler;
}

@Bean
public CancerStudyPermissionEvaluator cancerStudyPermissionEvaluator() {
return new CancerStudyPermissionEvaluator(appName, doFilterGroupsByAppName, alwaysShowCancerStudyGroup, cacheMapUtil);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.cbioportal.service.exception;

public class AccessForbiddenException extends RuntimeException {
public AccessForbiddenException(String message) {
super(message);
}
}
Loading

0 comments on commit c19eb10

Please sign in to comment.