Skip to content

Commit d5eaa73

Browse files
Merge pull request #1355 from Giskard-AI/GSK-1623-secure-readonly-demo-space
[GSK-1623] Secure Giskard readonly demo space at Hugging Face Spaces
2 parents eea8171 + 9cdf5b1 commit d5eaa73

32 files changed

Lines changed: 433 additions & 24 deletions

backend/src/main/java/ai/giskard/config/ApplicationProperties.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public class ApplicationProperties {
3636
private long tokenValidityInSecondsForRememberMe = 2592000; // 30 days;
3737
private CorsConfiguration cors = new CorsConfiguration();
3838

39+
private String defaultApiKey;
40+
private String hfDemoSpaceUnlockToken;
3941
private String mixpanelProjectKey;
4042
}
4143

backend/src/main/java/ai/giskard/config/SecurityConfiguration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public class SecurityConfiguration {
3737
private final ApiKeyService apiKeyService;
3838

3939

40+
public static final String GISKARD_API_ENDPOINTS = "/api/**";
41+
4042
@Bean
4143
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
4244
http
@@ -75,7 +77,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
7577
antMatcher("/management/**")
7678
).hasAuthority(AuthoritiesConstants.ADMIN)
7779
.requestMatchers(antMatcher("/public-api/**")).hasAuthority(AuthoritiesConstants.API)
78-
.requestMatchers(antMatcher("/api/**")).authenticated()
80+
.requestMatchers(antMatcher(GISKARD_API_ENDPOINTS)).authenticated()
7981
)
8082
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
8183
.apply(securityConfigurerAdapter());

backend/src/main/java/ai/giskard/domain/ApiKey.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
package ai.giskard.domain;
22

3+
import ai.giskard.security.GalleryDatabaseOperationListener;
34
import jakarta.persistence.*;
45
import lombok.Getter;
56
import lombok.NoArgsConstructor;
7+
import lombok.Setter;
68
import org.apache.commons.lang3.RandomStringUtils;
79

810
import java.util.UUID;
911

1012
@Entity(name = "api_keys")
1113
@Getter
14+
@Setter
1215
@NoArgsConstructor
16+
@EntityListeners(GalleryDatabaseOperationListener.class)
1317
public class ApiKey extends AbstractAuditingEntity {
1418

1519
public static final String PREFIX = "gsk-";

backend/src/main/java/ai/giskard/domain/Project.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import ai.giskard.domain.ml.Dataset;
44
import ai.giskard.domain.ml.ProjectModel;
55
import ai.giskard.domain.ml.TestSuite;
6+
import ai.giskard.security.GalleryDatabaseOperationListener;
67
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
78
import com.fasterxml.jackson.annotation.JsonIgnore;
89
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -22,7 +23,7 @@
2223

2324
@Entity(name = "projects")
2425
@NoArgsConstructor
25-
@EntityListeners(AuditingEntityListener.class)
26+
@EntityListeners({AuditingEntityListener.class, GalleryDatabaseOperationListener.class})
2627
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "key")
2728
public class Project extends AbstractAuditingEntity {
2829
@Serial

backend/src/main/java/ai/giskard/domain/Role.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package ai.giskard.domain;
22

3+
import ai.giskard.security.GalleryDatabaseOperationListener;
34
import jakarta.persistence.Column;
45
import jakarta.persistence.Entity;
6+
import jakarta.persistence.EntityListeners;
57
import jakarta.persistence.Table;
68
import jakarta.validation.constraints.NotNull;
79
import jakarta.validation.constraints.Size;
@@ -12,6 +14,7 @@
1214
*/
1315
@Entity
1416
@Table(name = "role")
17+
@EntityListeners(GalleryDatabaseOperationListener.class)
1518
public class Role extends BaseEntity {
1619
@lombok.Setter
1720
@lombok.Getter

backend/src/main/java/ai/giskard/domain/User.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ai.giskard.domain;
22

33
import ai.giskard.config.Constants;
4+
import ai.giskard.security.GalleryDatabaseOperationListener;
45
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
56
import com.fasterxml.jackson.annotation.JsonIgnore;
67
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@@ -29,6 +30,7 @@
2930
@Setter
3031
@NotNull
3132
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "login")
33+
@EntityListeners(GalleryDatabaseOperationListener.class)
3234
public class User extends AbstractAuditingEntity {
3335
@Serial
3436
private static final long serialVersionUID = 0L;

backend/src/main/java/ai/giskard/domain/ml/ProjectModel.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import ai.giskard.domain.AbstractAuditingEntity;
44
import ai.giskard.domain.Project;
5+
import ai.giskard.security.GalleryDatabaseOperationListener;
56
import ai.giskard.utils.SimpleJSONStringAttributeConverter;
67
import com.fasterxml.jackson.annotation.JsonBackReference;
78
import lombok.AllArgsConstructor;
@@ -20,6 +21,7 @@
2021
@Setter
2122
@NoArgsConstructor
2223
@AllArgsConstructor
24+
@EntityListeners(GalleryDatabaseOperationListener.class)
2325
public class ProjectModel extends AbstractAuditingEntity {
2426
@Serial
2527
private static final long serialVersionUID = 0L;

backend/src/main/java/ai/giskard/security/AuthoritiesConstants.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ public final class AuthoritiesConstants {
2323
ADMIN, "Admin"
2424
);
2525

26-
public static final String[] AUTHORITIES = AUTHORITY_NAMES.keySet().toArray(new String[0]);
27-
2826
private AuthoritiesConstants() {
2927
}
3028
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package ai.giskard.security;
2+
3+
import ai.giskard.service.GeneralSettingsService;
4+
import ai.giskard.service.InitService;
5+
import ai.giskard.web.rest.errors.GalleryDemoSpaceException;
6+
import jakarta.persistence.PrePersist;
7+
import jakarta.persistence.PreRemove;
8+
import jakarta.persistence.PreUpdate;
9+
import org.springframework.beans.factory.annotation.Configurable;
10+
11+
@Configurable
12+
public class GalleryDatabaseOperationListener {
13+
@PrePersist
14+
@PreUpdate
15+
@PreRemove
16+
void beforeEntityModification(Object entity) {
17+
if (GeneralSettingsService.IS_RUNNING_IN_DEMO_HF_SPACES && !InitService.isUnlocked()) {
18+
throw new GalleryDemoSpaceException("This is a read-only Giskard Gallery instance. You cannot modify entities " + entity.getClass().getName());
19+
}
20+
}
21+
}

backend/src/main/java/ai/giskard/service/AnalyticsCollectorService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ public void doTrack(String eventName, JSONObject props) {
5656
serverProps.put("Giskard Version", buildVersion);
5757
serverProps.put("Giskard Plan", licenseService.getCurrentLicense().getPlanCode());
5858
serverProps.put("Giskard LicenseID", licenseService.getCurrentLicense().getId());
59-
serverProps.put("Is HuggingFace", GeneralSettingsService.isRunningInHFSpaces);
60-
serverProps.put("HuggingFace Space ID", GeneralSettingsService.hfSpaceId);
59+
serverProps.put("Is HuggingFace", GeneralSettingsService.IS_RUNNING_IN_HFSPACES);
60+
serverProps.put("HuggingFace Space ID", GeneralSettingsService.HF_SPACE_ID);
6161
messageBuilder.set(settingsService.getSettings().getInstanceId(), serverProps);
6262
} catch (NoSuchElementException e) {
6363
// Do not track when we failed to initialize the server properties

0 commit comments

Comments
 (0)