Skip to content

Commit 30a8269

Browse files
committed
Allow curators to create public connectors / document sets
1 parent 264878a commit 30a8269

File tree

13 files changed

+91
-25
lines changed

13 files changed

+91
-25
lines changed

backend/ee/onyx/db/user_group.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,14 @@ def validate_object_creation_for_user(
128128
target_group_ids: list[int] | None = None,
129129
object_is_public: bool | None = None,
130130
object_is_perm_sync: bool | None = None,
131+
object_is_owned_by_user: bool | None = None,
132+
object_is_new: bool | None = None,
131133
) -> None:
132134
"""
133135
All users can create/edit permission synced objects if they don't specify a group
134136
All admin actions are allowed.
135-
Prevents non-admins from creating/editing:
137+
Curators and global curators can create public objects.
138+
Prevents other non-admins from creating/editing:
136139
- public objects
137140
- objects with no groups
138141
- objects that belong to a group they don't curate
@@ -143,13 +146,23 @@ def validate_object_creation_for_user(
143146
if not user or user.role == UserRole.ADMIN:
144147
return
145148

146-
if object_is_public:
147-
detail = "User does not have permission to create public credentials"
149+
# Allow curators and global curators to create public objects
150+
# w/o associated groups IF the object is new/owned by them
151+
if (
152+
object_is_public
153+
and user.role in [UserRole.CURATOR, UserRole.GLOBAL_CURATOR]
154+
and (object_is_new or object_is_owned_by_user)
155+
):
156+
return
157+
158+
if object_is_public and user.role == UserRole.BASIC:
159+
detail = "User does not have permission to create public objects"
148160
logger.error(detail)
149161
raise HTTPException(
150162
status_code=400,
151163
detail=detail,
152164
)
165+
153166
if not target_group_ids:
154167
detail = "Curators must specify 1+ groups"
155168
logger.error(detail)

backend/onyx/db/connector_credential_pair.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,16 @@ def fetch_connector_credential_pairs(
629629
return list(db_session.scalars(stmt).unique().all())
630630

631631

632+
def fetch_connector_credential_pair_for_connector(
633+
db_session: Session,
634+
connector_id: int,
635+
) -> ConnectorCredentialPair | None:
636+
stmt = select(ConnectorCredentialPair).where(
637+
ConnectorCredentialPair.connector_id == connector_id,
638+
)
639+
return db_session.scalar(stmt)
640+
641+
632642
def resync_cc_pair(
633643
cc_pair: ConnectorCredentialPair,
634644
search_settings_id: int,

backend/onyx/db/document_set.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def _add_user_filters(
8181
.where(~DocumentSet__UG.user_group_id.in_(user_groups))
8282
.correlate(DocumentSetDBModel)
8383
)
84+
where_clause |= DocumentSetDBModel.user_id == user.id
8485
else:
8586
where_clause |= DocumentSetDBModel.is_public == True # noqa: E712
8687

backend/onyx/server/documents/cc_pair.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ def associate_credential_to_connector(
463463
target_group_ids=metadata.groups,
464464
object_is_public=metadata.access_type == AccessType.PUBLIC,
465465
object_is_perm_sync=metadata.access_type == AccessType.SYNC,
466+
object_is_new=True,
466467
)
467468

468469
try:

backend/onyx/server/documents/connector.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373
from onyx.db.connector import mark_ccpair_with_indexing_trigger
7474
from onyx.db.connector import update_connector
7575
from onyx.db.connector_credential_pair import add_credential_to_connector
76+
from onyx.db.connector_credential_pair import (
77+
fetch_connector_credential_pair_for_connector,
78+
)
7679
from onyx.db.connector_credential_pair import get_cc_pair_groups_for_ids
7780
from onyx.db.connector_credential_pair import get_cc_pair_groups_for_ids_parallel
7881
from onyx.db.connector_credential_pair import get_connector_credential_pair
@@ -890,6 +893,7 @@ def create_connector_from_model(
890893
target_group_ids=connector_data.groups,
891894
object_is_public=connector_data.access_type == AccessType.PUBLIC,
892895
object_is_perm_sync=connector_data.access_type == AccessType.SYNC,
896+
object_is_new=True,
893897
)
894898
connector_base = connector_data.to_connector_base()
895899
connector_response = create_connector(
@@ -1002,6 +1006,7 @@ def update_connector_from_model(
10021006
user: User = Depends(current_curator_or_admin_user),
10031007
db_session: Session = Depends(get_session),
10041008
) -> ConnectorSnapshot | StatusResponse[int]:
1009+
cc_pair = fetch_connector_credential_pair_for_connector(db_session, connector_id)
10051010
try:
10061011
_validate_connector_allowed(connector_data.source)
10071012
fetch_ee_implementation_or_noop(
@@ -1012,6 +1017,7 @@ def update_connector_from_model(
10121017
target_group_ids=connector_data.groups,
10131018
object_is_public=connector_data.access_type == AccessType.PUBLIC,
10141019
object_is_perm_sync=connector_data.access_type == AccessType.SYNC,
1020+
object_is_owned_by_user=cc_pair and user and cc_pair.creator_id == user.id,
10151021
)
10161022
connector_base = connector_data.to_connector_base()
10171023
except ValueError as e:

backend/onyx/server/features/document_set/api.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from onyx.configs.constants import OnyxCeleryTask
1212
from onyx.db.document_set import check_document_sets_are_public
1313
from onyx.db.document_set import fetch_all_document_sets_for_user
14+
from onyx.db.document_set import get_document_set_by_id
1415
from onyx.db.document_set import insert_document_set
1516
from onyx.db.document_set import mark_document_set_as_to_be_deleted
1617
from onyx.db.document_set import update_document_set
@@ -42,6 +43,7 @@ def create_document_set(
4243
user=user,
4344
target_group_ids=document_set_creation_request.groups,
4445
object_is_public=document_set_creation_request.is_public,
46+
object_is_new=True,
4547
)
4648
try:
4749
document_set_db_model, _ = insert_document_set(
@@ -64,17 +66,25 @@ def create_document_set(
6466
@router.patch("/admin/document-set")
6567
def patch_document_set(
6668
document_set_update_request: DocumentSetUpdateRequest,
67-
user: User = Depends(current_curator_or_admin_user),
69+
user: User | None = Depends(current_curator_or_admin_user),
6870
db_session: Session = Depends(get_session),
6971
tenant_id: str = Depends(get_current_tenant_id),
7072
) -> None:
73+
document_set = get_document_set_by_id(db_session, document_set_update_request.id)
74+
if document_set is None:
75+
raise HTTPException(
76+
status_code=404,
77+
detail=f"Document set {document_set_update_request.id} does not exist",
78+
)
79+
7180
fetch_ee_implementation_or_noop(
7281
"onyx.db.user_group", "validate_object_creation_for_user", None
7382
)(
7483
db_session=db_session,
7584
user=user,
7685
target_group_ids=document_set_update_request.groups,
7786
object_is_public=document_set_update_request.is_public,
87+
object_is_owned_by_user=user and document_set.user_id == user.id,
7888
)
7989
try:
8090
update_document_set(
@@ -99,6 +109,22 @@ def delete_document_set(
99109
db_session: Session = Depends(get_session),
100110
tenant_id: str = Depends(get_current_tenant_id),
101111
) -> None:
112+
document_set = get_document_set_by_id(db_session, document_set_id)
113+
if document_set is None:
114+
raise HTTPException(
115+
status_code=404,
116+
detail=f"Document set {document_set_id} does not exist",
117+
)
118+
119+
fetch_ee_implementation_or_noop(
120+
"onyx.db.user_group", "validate_object_creation_for_user", None
121+
)(
122+
db_session=db_session,
123+
user=user,
124+
object_is_public=document_set.is_public,
125+
object_is_owned_by_user=user and document_set.user_id == user.id,
126+
)
127+
102128
try:
103129
mark_document_set_as_to_be_deleted(
104130
db_session=db_session,

web/src/app/admin/documents/sets/[documentSetId]/page.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ function Main({ documentSetId }: { documentSetId: number }) {
3333
const { data: userGroups, isLoading: userGroupsIsLoading } = useUserGroups();
3434

3535
if (isDocumentSetsLoading || isCCPairsLoading || userGroupsIsLoading) {
36-
return <ThreeDotsLoader />;
36+
return (
37+
<div className="flex justify-center items-center min-h-[400px]">
38+
<ThreeDotsLoader />
39+
</div>
40+
);
3741
}
3842

3943
if (documentSetsError || !documentSets) {
@@ -98,7 +102,7 @@ export default function Page(props: {
98102
const documentSetId = parseInt(params.documentSetId);
99103

100104
return (
101-
<div>
105+
<div className="container mx-auto">
102106
<BackButton />
103107

104108
<Main documentSetId={documentSetId} />

web/src/app/admin/documents/sets/new/page.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ function Main() {
2626
const { data: userGroups, isLoading: userGroupsIsLoading } = useUserGroups();
2727

2828
if (isCCPairsLoading || userGroupsIsLoading) {
29-
return <ThreeDotsLoader />;
29+
return (
30+
<div className="flex justify-center items-center min-h-[400px]">
31+
<ThreeDotsLoader />
32+
</div>
33+
);
3034
}
3135

3236
if (ccPairsError || !ccPairs) {

web/src/app/admin/documents/sets/page.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,11 @@ const Main = () => {
281281
} = useDocumentSets(true);
282282

283283
if (isDocumentSetsLoading || isEditableDocumentSetsLoading) {
284-
return <ThreeDotsLoader />;
284+
return (
285+
<div className="flex justify-center items-center min-h-[400px]">
286+
<ThreeDotsLoader />
287+
</div>
288+
);
285289
}
286290

287291
if (documentSetsError || !documentSets) {
@@ -308,9 +312,6 @@ const Main = () => {
308312
href="/admin/documents/sets/new"
309313
text="New Document Set"
310314
/>
311-
{/* <Link href="/admin/documents/sets/new">
312-
<Button variant="navigate">New Document Set</Button>
313-
</Link> */}
314315
</div>
315316

316317
{documentSets.length > 0 && (

web/src/components/IsPublicGroupSelector.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const IsPublicGroupSelector = <T extends IsPublicGroupSelectorFormType>({
3636
useEffect(() => {
3737
if (user && userGroups && isPaidEnterpriseFeaturesEnabled) {
3838
const isUserAdmin = user.role === UserRole.ADMIN;
39-
if (!isUserAdmin) {
39+
if (!isUserAdmin && userGroups.length > 0) {
4040
formikProps.setFieldValue("is_public", false);
4141
}
4242
if (

0 commit comments

Comments
 (0)