Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5450 create a sample project #5492

Merged
merged 44 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
3671274
add a boolean flag
YohannParis Nov 13, 2024
7915eda
Make the project as false
YohannParis Nov 13, 2024
6ce6e3b
first pass at a new controller
YohannParis Nov 13, 2024
5a79e42
Make sure that all the permissions relationship are properly set.
YohannParis Nov 13, 2024
fb0657e
chore: lint types
github-actions[bot] Nov 13, 2024
fee0f56
add utilities to ReBac permissions
YohannParis Nov 13, 2024
e6d17a7
When removing a relationship we do not care if it already exists
YohannParis Nov 13, 2024
e947109
Add a remove all to simplify
YohannParis Nov 13, 2024
8dde837
Update ProjectController.java
YohannParis Nov 13, 2024
6cc3992
chore: lint types
github-actions[bot] Nov 13, 2024
d630742
display sample project
YohannParis Nov 13, 2024
35e694d
Update Home.vue
YohannParis Nov 13, 2024
72d0500
Merge branch '5450-create-a-sample-project' of github.com:DARPA-ASKEM…
YohannParis Nov 13, 2024
09cd63c
add menu for admin only to make a sample
YohannParis Nov 13, 2024
7792a96
add controller endpoint to service
YohannParis Nov 13, 2024
5a7ef53
make button functional
YohannParis Nov 13, 2024
0840db7
chore: lint types
github-actions[bot] Nov 13, 2024
5bf3ca1
remove await
YohannParis Nov 13, 2024
c40fa12
Merge branch '5450-create-a-sample-project' of github.com:DARPA-ASKEM…
YohannParis Nov 13, 2024
5afbedc
chore: lint types
github-actions[bot] Nov 13, 2024
532aa98
Merge branch 'main' into 5450-create-a-sample-project
YohannParis Nov 14, 2024
46b8df5
Update ProjectController.java
YohannParis Nov 14, 2024
8534209
Merge branch 'main' into 5450-create-a-sample-project
YohannParis Nov 14, 2024
c44edf0
Make admin group a writer of the sample project
YohannParis Nov 14, 2024
ee7088b
make sure the value for the boolean are set
YohannParis Nov 14, 2024
47d7526
chore: lint types
github-actions[bot] Nov 14, 2024
b64f610
add admin creator and remove all for groups
YohannParis Nov 14, 2024
ca1d1d4
Make the admin group and administrater
YohannParis Nov 14, 2024
1e6c952
test if a contributor is a group or user
YohannParis Nov 14, 2024
0aec560
Merge branch '5450-create-a-sample-project' of github.com:DARPA-ASKEM…
YohannParis Nov 14, 2024
2173f96
Update RebacGroup.java
YohannParis Nov 14, 2024
6be394f
Update ProjectController.java
YohannParis Nov 14, 2024
c0347d0
improve how we remove relationships on groups
YohannParis Nov 14, 2024
05ab6ef
Merge branch 'main' into 5450-create-a-sample-project
YohannParis Nov 14, 2024
855070a
chore: lint types
github-actions[bot] Nov 14, 2024
ff8aed5
make an explicit removal list
YohannParis Nov 14, 2024
5571a8c
chore: lint types
github-actions[bot] Nov 14, 2024
f73a528
merge main
YohannParis Nov 15, 2024
a7f9a17
chore: lint types
github-actions[bot] Nov 15, 2024
105bd91
needed to commit change to project to database
bigglesandginger Nov 15, 2024
928eac4
Merge branch 'main' into 5450-create-a-sample-project
YohannParis Nov 15, 2024
166446d
Fucking hibernate
YohannParis Nov 15, 2024
d76a2d3
Merge branch 'main' into 5450-create-a-sample-project
YohannParis Nov 15, 2024
b37a4d7
Update tera-project-menu.vue
YohannParis Nov 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import { isEmpty } from 'lodash';
import Button from 'primevue/button';
import Menu from 'primevue/menu';
import { computed, ref } from 'vue';
import { exportProjectAsFile } from '@/services/project';
import { exportProjectAsFile, setSample } from '@/services/project';
import { AcceptedExtensions } from '@/types/common';
import { MenuItem } from 'primevue/menuitem';
import useAuthStore from '@/stores/auth';
import { useToastService } from '@/services/toast';

const props = defineProps<{ project: Project | null }>();

Expand Down Expand Up @@ -81,6 +82,20 @@ const downloadMenuItem = {
}
};

const makeSampleMenuItem = {
label: 'Make a sample',
icon: 'pi pi-star',
command: () => {
setSample(props.project?.id).then((response) => {
if (response) {
useToastService().success(undefined, 'Project set as sample');
} else {
useToastService().error(undefined, 'Error setting project as sample');
}
});
}
};

const projectMenuItems = computed(() => {
// Basic access to a public and reader project
const items: MenuItem[] = [copyMenuItem, downloadMenuItem];
Expand All @@ -95,6 +110,11 @@ const projectMenuItems = computed(() => {
items.push(removeMenuItem);
}

// Admin only
if (useAuthStore().isAdmin) {
items.push(makeSampleMenuItem);
}

return items;
});

Expand Down
20 changes: 15 additions & 5 deletions packages/client/hmi-client/src/page/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,10 @@ const myFilteredSortedProjects = computed(() => {
const projects = useProjects().allProjects.value;
if (!projects) return [];
const myProjects = projects.filter(
({ userPermission, publicProject }) =>
// I can edit the project, or I can view the project and it's not public
['creator', 'writer'].includes(userPermission ?? '') || (userPermission === 'reader' && !publicProject)
({ userPermission, publicProject, sampleProject }) =>
// I can edit the project, or I can view the project, and it's not public
['creator', 'writer'].includes(userPermission ?? '') ||
(userPermission === 'reader' && publicProject === false && sampleProject === false)
);
return filterAndSortProjects(myProjects);
});
Expand All @@ -248,10 +249,19 @@ function tabChange(event) {
const publicFilteredSortedProjects = computed(() => {
const projects = useProjects().allProjects.value;
if (!projects) return [];
const publicProjects = projects.filter(({ publicProject }) => publicProject === true);
const publicProjects = projects.filter(
({ publicProject, sampleProject }) => publicProject === true && sampleProject === false
);
return filterAndSortProjects(publicProjects);
});

const sampleFilteredSortedProjects = computed(() => {
const projects = useProjects().allProjects.value;
if (!projects) return [];
const sampleProjects = projects.filter(({ sampleProject }) => sampleProject === true);
return filterAndSortProjects(sampleProjects);
});

function openCreateProjectModal() {
isProjectConfigDialogVisible.value = true;
menuProject.value = null;
Expand Down Expand Up @@ -305,7 +315,7 @@ function filterAndSortProjects(projects: Project[]) {
const projectsTabs = computed<{ title: string; projects: Project[] }[]>(() => [
{ title: TabTitles.MyProjects, projects: myFilteredSortedProjects.value },
{ title: TabTitles.PublicProjects, projects: publicFilteredSortedProjects.value },
{ title: TabTitles.SampleProjects, projects: [] }
{ title: TabTitles.SampleProjects, projects: sampleFilteredSortedProjects.value }
]);

// Table view
Expand Down
11 changes: 11 additions & 0 deletions packages/client/hmi-client/src/services/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ async function setAccessibility(projectId: Project['id'], isPublic: boolean): Pr
}
}

async function setSample(projectId: Project['id']): Promise<boolean> {
try {
const response = await API.post(`projects/set-sample/${projectId}`);
return response?.status === 200;
} catch (error) {
console.error(`The project was not made a sample project, ${error}`);
return false;
}
}

async function getPermissions(projectId: Project['id']): Promise<PermissionRelationships | null> {
try {
const { status, data } = await API.get(`projects/${projectId}/permissions`);
Expand Down Expand Up @@ -288,6 +298,7 @@ export {
removePermissions,
setAccessibility,
setPermissions,
setSample,
update,
updatePermissions,
exportProjectAsFile,
Expand Down
3 changes: 2 additions & 1 deletion packages/client/hmi-client/src/types/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ export interface Project extends TerariumAsset {
overviewContent?: any;
projectAssets: ProjectAsset[];
metadata?: { [index: string]: string };
sampleProject?: boolean;
publicProject?: boolean;
userPermission?: string;
}
Expand Down Expand Up @@ -795,9 +796,9 @@ export interface ModelUnit {
}

export interface GroundedSemantic {
grounding?: ModelGrounding;
id: string;
name?: string;
grounding?: ModelGrounding;
description?: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,75 @@ public ResponseEntity<JsonNode> makeProjectPublic(
}
}

@Operation(summary = "Set a project as a sample project by ID")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "Project has been made a sample project",
content = {
@Content(
mediaType = "application/json",
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = UUID.class)
)
}
),
@ApiResponse(
responseCode = "403",
description = "The current user does not have privileges to modify this project.",
content = @Content
),
@ApiResponse(responseCode = "500", description = "An error occurred verifying permissions", content = @Content)
}
)
@PostMapping("/set-sample/{id}")
@Secured(Roles.USER)
public ResponseEntity<JsonNode> makeProjectSample(@PathVariable("id") final UUID id) {
try {
// Only an admin can set a project as a sample project
projectService.checkPermissionCanAdministrate(currentUserService.get().getId(), id);

final Optional<Project> project = projectService.getProject(id);
if (project.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, messages.get("projects.not-found"));
}

// Update the project and make it public as well as a sample project
project.get().setSampleProject(true).setPublicAsset(true);
projectAssetService.togglePublicForAssets(terariumAssetServices, id, true, Schema.Permission.WRITE);
projectService.updateProject(project.get());

/* Permissions */

// Getting the project
final RebacProject rebacProject = new RebacProject(id, reBACService);

// Delete all previous user relationships attached to the project,
final List<Contributor> contributors = projectPermissionsService.getContributors(rebacProject);
for (final Contributor contributor : contributors) {
if (contributor.isUser()) {
final RebacUser rebacUser = new RebacUser(contributor.getUserId(), reBACService);
rebacUser.removeAllRelationships(rebacProject);
}
}

// Add the Admin group to administrate the project
final RebacGroup adminGroup = new RebacGroup(ReBACService.ASKEM_ADMIN_GROUP_ID, reBACService);
adminGroup.removeAllRelationsExceptOne(rebacProject, Schema.Relationship.ADMIN);

// Add the public group to read the project
final RebacGroup publicGroup = new RebacGroup(ReBACService.PUBLIC_GROUP_ID, reBACService);
publicGroup.removeAllRelationsExceptOne(rebacProject, Schema.Relationship.READER);

return ResponseEntity.ok().build();
} catch (final ResponseStatusException rethrow) {
throw rethrow;
} catch (final Exception e) {
log.error("Unexpected error, failed to set as a sample project ", e);
throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, messages.get("rebac.service-unavailable"));
}
}

@PostMapping("/{id}/permissions/user/{user-id}/{relationship}")
@Secured(Roles.USER)
@Operation(summary = "Sets a user's permissions for a project")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,14 @@ public Schema.Relationship getPermission() {
public String getUserId() {
return userId;
}

/** Is the Contributor a Group? */
public Boolean isGroup() {
return userId == null;
}

/** Is the Contributor a User? */
public Boolean isUser() {
return userId != null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ public class Project extends TerariumAsset {
@Schema(accessMode = Schema.AccessMode.READ_ONLY, defaultValue = "{}")
private Map<String, String> metadata;

@TSOptional
@Schema(accessMode = Schema.AccessMode.READ_ONLY, defaultValue = "false")
private Boolean sampleProject;

/** Information for the front-end to display/filter the project accordingly. */
@TSOptional
@Transient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,11 +491,15 @@ public void removeRelationship(
final SchemaObject who,
final SchemaObject what,
final Schema.Relationship relationship
) throws Exception, RelationshipAlreadyExistsException {
) throws Exception {
userCache.invalidate(who.id);
invalidatePermissionCache(who, what);
final ReBACFunctions rebac = new ReBACFunctions(channel, spiceDbBearerToken);
CURRENT_ZED_TOKEN = rebac.removeRelationship(who, relationship, what);
try {
CURRENT_ZED_TOKEN = rebac.removeRelationship(who, relationship, what);
} catch (RelationshipAlreadyExistsException ignore) {
bigglesandginger marked this conversation as resolved.
Show resolved Hide resolved
// NB: This is a no-op as the relationship is already removed
}
}

private Consistency getCurrentConsistency() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public boolean canAdministrate(RebacObject rebacObject) throws Exception {
return reBACService.can(getSchemaObject(), Schema.Permission.ADMINISTRATE, rebacObject.getSchemaObject());
}

public void createReaderRelationship(RebacObject rebacObject) throws Exception, RelationshipAlreadyExistsException {
reBACService.createRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.READER);
}

public void createWriterRelationship(RebacObject rebacObject) throws Exception, RelationshipAlreadyExistsException {
reBACService.createRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.WRITER);
}
Expand All @@ -34,6 +38,38 @@ public void createCreatorRelationship(RebacObject rebacObject) throws Exception,
reBACService.createRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.CREATOR);
}

public void createAdminRelationship(RebacObject rebacObject) throws Exception, RelationshipAlreadyExistsException {
reBACService.createRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.ADMIN);
}

/** Remove all relationships between this group and the given object. */
public void removeAllRelationships(final RebacObject rebacObject) throws Exception {
final Schema.Relationship[] relationships = Schema.Relationship.values();
for (Schema.Relationship relationship : relationships) {
reBACService.removeRelationship(getSchemaObject(), rebacObject.getSchemaObject(), relationship);
}
}

/**
* Remove all relationships between this group and the given object.
* Except the provided relationship, if it doesn't exist, it will be added
*/
public void removeAllRelationsExceptOne(
YohannParis marked this conversation as resolved.
Show resolved Hide resolved
final RebacObject rebacObject,
final Schema.Relationship relationshipToIgnore
) throws Exception {
final Schema.Relationship[] relationships = Schema.Relationship.values();
for (Schema.Relationship relationship : relationships) {
if (relationship == relationshipToIgnore) {
try {
reBACService.createRelationship(getSchemaObject(), rebacObject.getSchemaObject(), relationship);
} catch (RelationshipAlreadyExistsException ignore) {}
} else {
reBACService.removeRelationship(getSchemaObject(), rebacObject.getSchemaObject(), relationship);
}
}
}

public void setPermissionRelationships(RebacObject who, String relationship)
throws Exception, RelationshipAlreadyExistsException {
Schema.Relationship relationshipEnum = Schema.Relationship.valueOf(relationship.toUpperCase());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,34 @@ public void createCreatorRelationship(final RebacObject rebacObject)
reBACService.createRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.CREATOR);
}

public void createWriterRelationship(final RebacObject rebacObject)
throws Exception, RelationshipAlreadyExistsException {
reBACService.createRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.WRITER);
}

public void createReaderRelationship(final RebacObject rebacObject)
throws Exception, RelationshipAlreadyExistsException {
reBACService.createRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.READER);
}

public void removeCreatorRelationship(final RebacObject rebacObject) throws Exception {
reBACService.removeRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.CREATOR);
}

public void removeWriterRelationship(final RebacObject rebacObject) throws Exception {
reBACService.removeRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.WRITER);
}

public void removeReaderRelationship(final RebacObject rebacObject) throws Exception {
reBACService.removeRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.READER);
}

public void removeAllRelationships(final RebacObject rebacObject) throws Exception {
reBACService.removeRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.CREATOR);
reBACService.removeRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.WRITER);
reBACService.removeRelationship(getSchemaObject(), rebacObject.getSchemaObject(), Schema.Relationship.READER);
}

public PermissionGroup createGroup(final String name) throws Exception, RelationshipAlreadyExistsException {
final PermissionGroup group = reBACService.createGroup(name);
reBACService.createRelationship(
Expand Down