dara-core 1.23.0a1__py3-none-any.whl → 1.23.2a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dara/core/_assets/auto_js/dara.core.umd.cjs +77 -33
- dara/core/auth/definitions.py +4 -0
- dara/core/auth/oidc/config.py +80 -17
- dara/core/auth/oidc/routes.py +6 -1
- dara/core/auth/oidc/settings.py +2 -0
- {dara_core-1.23.0a1.dist-info → dara_core-1.23.2a1.dist-info}/METADATA +10 -10
- {dara_core-1.23.0a1.dist-info → dara_core-1.23.2a1.dist-info}/RECORD +10 -10
- {dara_core-1.23.0a1.dist-info → dara_core-1.23.2a1.dist-info}/LICENSE +0 -0
- {dara_core-1.23.0a1.dist-info → dara_core-1.23.2a1.dist-info}/WHEEL +0 -0
- {dara_core-1.23.0a1.dist-info → dara_core-1.23.2a1.dist-info}/entry_points.txt +0 -0
|
@@ -27789,8 +27789,10 @@
|
|
|
27789
27789
|
`;
|
|
27790
27790
|
styled.div`
|
|
27791
27791
|
display: flex;
|
|
27792
|
+
align-items: center;
|
|
27792
27793
|
gap: 0.5rem;
|
|
27793
27794
|
width: 100%;
|
|
27795
|
+
line-height: 1;
|
|
27794
27796
|
`;
|
|
27795
27797
|
styled.div`
|
|
27796
27798
|
color: ${(props) => props.theme.colors.grey3};
|
|
@@ -39077,6 +39079,8 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39077
39079
|
style: Object.assign(Object.assign(Object.assign({}, floatingStyles), { maxHeight: 800, minWidth: 150, zIndex: 9999 }), style)
|
|
39078
39080
|
}), { isOpen, children: jsxRuntimeExports.jsx(SectionedList, { items: allowColumnHiding ? [resetFunctions, columnToggles] : [resetFunctions], onSelect: onOptionSelect }, isOpen ? "open" : "closed") })), document.body)] });
|
|
39079
39081
|
};
|
|
39082
|
+
const { fontSize } = window.getComputedStyle(document.documentElement);
|
|
39083
|
+
const DEFAULT_ROW_HEIGHT = parseFloat(fontSize) * 2.5;
|
|
39080
39084
|
var __rest$1 = function(s, e2) {
|
|
39081
39085
|
var t2 = {};
|
|
39082
39086
|
for (var p2 in s) if (Object.prototype.hasOwnProperty.call(s, p2) && e2.indexOf(p2) < 0)
|
|
@@ -39088,8 +39092,6 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39088
39092
|
}
|
|
39089
39093
|
return t2;
|
|
39090
39094
|
};
|
|
39091
|
-
const { fontSize } = window.getComputedStyle(document.documentElement);
|
|
39092
|
-
const ROW_HEIGHT = parseFloat(fontSize) * 2.5;
|
|
39093
39095
|
const shouldForwardProp$2 = (prop) => !["isSorted", "onClickRow"].includes(prop);
|
|
39094
39096
|
const Row = styled.div.withConfig({ shouldForwardProp: shouldForwardProp$2 })`
|
|
39095
39097
|
cursor: ${(props) => props.onClickRow ? "pointer" : "default"};
|
|
@@ -39145,7 +39147,7 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39145
39147
|
align-items: center;
|
|
39146
39148
|
|
|
39147
39149
|
min-width: 80px;
|
|
39148
|
-
height: ${() => `${
|
|
39150
|
+
height: ${({ rowHeight }) => `${rowHeight}px`};
|
|
39149
39151
|
|
|
39150
39152
|
color: ${(props) => props.theme.colors.grey6};
|
|
39151
39153
|
|
|
@@ -39169,7 +39171,7 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39169
39171
|
var _a;
|
|
39170
39172
|
return areEqual(prevProps, nextProps) && !(((_a = nextProps.data) === null || _a === void 0 ? void 0 : _a.headerGroups) || []).some((headerGroup) => ((headerGroup === null || headerGroup === void 0 ? void 0 : headerGroup.headers) || []).some((header) => header.isResizing));
|
|
39171
39173
|
};
|
|
39172
|
-
const RenderRow = React__namespace.memo(({ data: { width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns }, index: index2, style: renderRowStyle }) => {
|
|
39174
|
+
const RenderRow = React__namespace.memo(({ data: { width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns, rowHeight }, index: index2, style: renderRowStyle }) => {
|
|
39173
39175
|
let row = rows[index2];
|
|
39174
39176
|
if (getItem) {
|
|
39175
39177
|
const value = getItem(index2);
|
|
@@ -39182,8 +39184,8 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39182
39184
|
}
|
|
39183
39185
|
if (!row) {
|
|
39184
39186
|
return jsxRuntimeExports.jsx("div", { children: headerGroups.map((headerGroup, gidx) => jsxRuntimeExports.jsx(RowPlaceholder, { style: {
|
|
39185
|
-
height:
|
|
39186
|
-
top: (index2 + 1) *
|
|
39187
|
+
height: rowHeight,
|
|
39188
|
+
top: (index2 + 1) * rowHeight,
|
|
39187
39189
|
width: totalColumnsWidth > width ? totalColumnsWidth : "100%"
|
|
39188
39190
|
}, children: headerGroup === null || headerGroup === void 0 ? void 0 : headerGroup.headers.map((col, cidx) => {
|
|
39189
39191
|
const headerProps = col.getHeaderProps();
|
|
@@ -39201,15 +39203,23 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39201
39203
|
}
|
|
39202
39204
|
};
|
|
39203
39205
|
const _a = row.getRowProps({ style: renderRowStyle }), { style: rowStyle } = _a, restRow = __rest$1(_a, ["style"]);
|
|
39204
|
-
return React$1.createElement(Row, Object.assign({}, restRow, { key: `row-${index2}`, onClick, onClickRow, style: Object.assign(Object.assign({}, rowStyle), {
|
|
39205
|
-
|
|
39206
|
+
return React$1.createElement(Row, Object.assign({}, restRow, { key: `row-${index2}`, onClick, onClickRow, style: Object.assign(Object.assign({}, rowStyle), {
|
|
39207
|
+
// The first row is the header row which is not controlled by this rowHeight prop so it needs to be part of the calculation.
|
|
39208
|
+
top: index2 === 0 ? DEFAULT_ROW_HEIGHT : index2 * rowHeight + DEFAULT_ROW_HEIGHT,
|
|
39209
|
+
width: totalColumnsWidth > width ? totalColumnsWidth : "100%"
|
|
39210
|
+
}) }), row.cells.map((cell, colIdx) => {
|
|
39211
|
+
var _a2, _b, _c, _d, _e2;
|
|
39206
39212
|
const cellProps = cell.getCellProps();
|
|
39207
39213
|
return React$1.createElement(
|
|
39208
39214
|
Cell,
|
|
39209
|
-
Object.assign({}, cellProps, { key: `cell-${index2}-${colIdx}`, style: Object.assign(Object.assign({}, cellProps.style), { backgroundColor, justifyContent: mappedColumns[colIdx].align, maxWidth: (_a2 = cell.column) === null || _a2 === void 0 ? void 0 : _a2.maxWidth, width: (
|
|
39215
|
+
Object.assign({}, cellProps, { rowHeight, key: `cell-${index2}-${colIdx}`, style: Object.assign(Object.assign(Object.assign(Object.assign({}, cellProps.style), { backgroundColor, justifyContent: mappedColumns[colIdx].align, maxWidth: (_a2 = cell.column) === null || _a2 === void 0 ? void 0 : _a2.maxWidth, width: (
|
|
39210
39216
|
// If width calc has messed up then use the raw width from the column
|
|
39211
39217
|
cellProps.style.width === "NaNpx" ? mappedColumns[colIdx].width : cellProps.style.width
|
|
39212
|
-
) })
|
|
39218
|
+
) }), ((_b = mappedColumns[colIdx]) === null || _b === void 0 ? void 0 : _b.sticky) === "left" && typeof ((_c = mappedColumns[colIdx]) === null || _c === void 0 ? void 0 : _c.stickyOffset) === "number" ? {
|
|
39219
|
+
left: `${mappedColumns[colIdx].stickyOffset}px`
|
|
39220
|
+
} : {}), ((_d = mappedColumns[colIdx]) === null || _d === void 0 ? void 0 : _d.sticky) === "right" && typeof ((_e2 = mappedColumns[colIdx]) === null || _e2 === void 0 ? void 0 : _e2.stickyOffset) === "number" ? {
|
|
39221
|
+
right: `${mappedColumns[colIdx].stickyOffset}px`
|
|
39222
|
+
} : {}) }),
|
|
39213
39223
|
jsxRuntimeExports.jsx(CellContent, { children: cell.render("Cell", {
|
|
39214
39224
|
colIdx,
|
|
39215
39225
|
currentEditCell,
|
|
@@ -39290,7 +39300,7 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39290
39300
|
justify-content: space-between;
|
|
39291
39301
|
|
|
39292
39302
|
min-width: 80px;
|
|
39293
|
-
height: ${
|
|
39303
|
+
height: ${DEFAULT_ROW_HEIGHT}px;
|
|
39294
39304
|
|
|
39295
39305
|
color: ${(props) => props.theme.colors.text};
|
|
39296
39306
|
|
|
@@ -39375,6 +39385,26 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39375
39385
|
});
|
|
39376
39386
|
return [...leftStickyCols, ...nonStickyCols, ...rightStickyCols];
|
|
39377
39387
|
};
|
|
39388
|
+
const appendStickyOffsets = (columns) => {
|
|
39389
|
+
let leftOffset = 0;
|
|
39390
|
+
let rightOffset = 0;
|
|
39391
|
+
const rightStickyColumnWidths = columns.filter((col) => col.sticky === "right").slice(1).map((col) => parseInt(col.width) || 150);
|
|
39392
|
+
return columns.map((col) => {
|
|
39393
|
+
if (col.sticky === "left") {
|
|
39394
|
+
const nextCol = Object.assign(Object.assign({}, col), { stickyOffset: leftOffset });
|
|
39395
|
+
const width = parseInt(col.width) || 150;
|
|
39396
|
+
leftOffset += width;
|
|
39397
|
+
return nextCol;
|
|
39398
|
+
}
|
|
39399
|
+
if (col.sticky === "right") {
|
|
39400
|
+
rightOffset = rightStickyColumnWidths.reduce((acc, width) => acc + width, 0);
|
|
39401
|
+
const nextCol = Object.assign(Object.assign({}, col), { stickyOffset: rightOffset });
|
|
39402
|
+
rightStickyColumnWidths.shift();
|
|
39403
|
+
return nextCol;
|
|
39404
|
+
}
|
|
39405
|
+
return col;
|
|
39406
|
+
});
|
|
39407
|
+
};
|
|
39378
39408
|
const filterComponentMap = {
|
|
39379
39409
|
categorical: CategoricalFilter,
|
|
39380
39410
|
datetime: DatetimeFilter,
|
|
@@ -39392,12 +39422,26 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39392
39422
|
return Object.assign(Object.assign({}, col), { Filter: filterComponentMap[col.filter] });
|
|
39393
39423
|
});
|
|
39394
39424
|
};
|
|
39425
|
+
const createActionColumn = (actions, accessor, sticky, disableSelectAll = false) => {
|
|
39426
|
+
const width = actions.includes(Actions.SELECT) ? 52 : actions.length * 24 + 24;
|
|
39427
|
+
return {
|
|
39428
|
+
Cell: ActionCell,
|
|
39429
|
+
Header: actions.includes(Actions.SELECT) && !disableSelectAll ? SelectHeader : "",
|
|
39430
|
+
accessor: accessor || "actions",
|
|
39431
|
+
actions,
|
|
39432
|
+
disableSortBy: true,
|
|
39433
|
+
maxWidth: width,
|
|
39434
|
+
minWidth: actions.includes(Actions.SELECT) ? 52 : 48,
|
|
39435
|
+
sticky: sticky || null,
|
|
39436
|
+
width
|
|
39437
|
+
};
|
|
39438
|
+
};
|
|
39395
39439
|
const cells = {
|
|
39396
39440
|
DATETIME: DatetimeCell,
|
|
39397
39441
|
EDIT_INPUT: EditInputCell,
|
|
39398
39442
|
EDIT_SELECT: EditSelectCell
|
|
39399
39443
|
};
|
|
39400
|
-
const createItemData$1 = memoizeOne$1((width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns) => ({
|
|
39444
|
+
const createItemData$1 = memoizeOne$1((width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns, rowHeight) => ({
|
|
39401
39445
|
backgroundColor,
|
|
39402
39446
|
currentEditCell,
|
|
39403
39447
|
getItem,
|
|
@@ -39406,12 +39450,14 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39406
39450
|
onClickRow,
|
|
39407
39451
|
prepareRow,
|
|
39408
39452
|
rows,
|
|
39453
|
+
rowHeight,
|
|
39409
39454
|
throttledClickRow,
|
|
39410
39455
|
totalColumnsWidth,
|
|
39411
39456
|
width
|
|
39412
39457
|
}));
|
|
39413
|
-
const Table = React$1.forwardRef(({ allowHiding, backgroundColor, className, columns, data: data2, getItem, initialSort = [], itemCount, maxRows, onAction, onChange: onChange2, onClickRow, onItemsRendered, onFilter, onSort, showTableOptions, style, tableOptionsStyle }, ref) => {
|
|
39458
|
+
const Table = React$1.forwardRef(({ allowHiding, actions, backgroundColor, className, columns, data: data2, getItem, initialSort = [], itemCount, maxRows, onAction, onChange: onChange2, onClickRow, onItemsRendered, onFilter, onSort, rowHeight, showTableOptions, style, tableOptionsStyle }, ref) => {
|
|
39414
39459
|
const [currentSortBy, setCurrentSortBy] = React$1.useState(initialSort);
|
|
39460
|
+
const tableRowHeight = rowHeight || DEFAULT_ROW_HEIGHT;
|
|
39415
39461
|
React$1.useEffect(
|
|
39416
39462
|
() => {
|
|
39417
39463
|
setCurrentSortBy(initialSort);
|
|
@@ -39441,7 +39487,14 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39441
39487
|
throttledSetEditCell([Number(cell[0]), cell[1]]);
|
|
39442
39488
|
};
|
|
39443
39489
|
const infiniteData = React$1.useMemo(() => Array(itemCount).fill(0), [itemCount]);
|
|
39444
|
-
const mappedColumns = React$1.useMemo(() =>
|
|
39490
|
+
const mappedColumns = React$1.useMemo(() => {
|
|
39491
|
+
let processedColumns = columns;
|
|
39492
|
+
if (actions && actions.length > 0) {
|
|
39493
|
+
const actionColumn = createActionColumn(actions);
|
|
39494
|
+
processedColumns = [...columns, actionColumn];
|
|
39495
|
+
}
|
|
39496
|
+
return appendStickyOffsets(appendFilterComponents(orderStickyCols(processedColumns)));
|
|
39497
|
+
}, [columns, actions]);
|
|
39445
39498
|
const hasFixedColumns = React$1.useMemo(() => mappedColumns.some((column) => "sticky" in column), [mappedColumns]);
|
|
39446
39499
|
const totalColumnsWidth = React$1.useMemo(() => mappedColumns.reduce((acc, column) => acc + (parseInt(column.width) || 150), 0), [mappedColumns]);
|
|
39447
39500
|
const filterTypes = React$1.useMemo(() => ({
|
|
@@ -39497,11 +39550,15 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39497
39550
|
const showHeaderCellButtonContainer = showSort || showFilter || showOptions;
|
|
39498
39551
|
return React$1.createElement(
|
|
39499
39552
|
HeaderCell,
|
|
39500
|
-
Object.assign({}, headerProps, { key: `col-${gidx}-${cidx}`, style: Object.assign(Object.assign({}, headerProps.style), {
|
|
39553
|
+
Object.assign({}, headerProps, { key: `col-${gidx}-${cidx}`, style: Object.assign(Object.assign(Object.assign(Object.assign({}, headerProps.style), {
|
|
39501
39554
|
maxWidth: col.maxWidth,
|
|
39502
39555
|
// If width calc has messed up then use the raw width from the column
|
|
39503
39556
|
width: headerProps.style.width === "NaNpx" ? mappedColumns[cidx].width : headerProps.style.width
|
|
39504
|
-
})
|
|
39557
|
+
}), col.sticky === "left" && typeof col.stickyOffset === "number" ? {
|
|
39558
|
+
left: `${col.stickyOffset}px`
|
|
39559
|
+
} : {}), col.sticky === "right" && typeof col.stickyOffset === "number" ? {
|
|
39560
|
+
right: `${col.stickyOffset}px`
|
|
39561
|
+
} : {}) }),
|
|
39505
39562
|
jsxRuntimeExports.jsxs(HeaderTooltipContainer, { isPrimitiveHeader: typeof headerContent === "string", children: [jsxRuntimeExports.jsx(HeaderContentWrapper, Object.assign({}, sortProps, { isPrimitiveHeader: typeof headerContent === "string", title: typeof headerContent === "string" ? headerContent : "", children: headerContent })), col.tooltip && jsxRuntimeExports.jsx(Tooltip, { content: col.tooltip, children: jsxRuntimeExports.jsx(TooltipIcon, { icon: faCircleQuestion }) })] }),
|
|
39506
39563
|
showHeaderCellButtonContainer && jsxRuntimeExports.jsxs(HeaderCellButtonContainer, { children: [jsxRuntimeExports.jsxs(HeaderIconsWrapper, { children: [showSort && jsxRuntimeExports.jsx(HeaderIconWrapper, { children: jsxRuntimeExports.jsx(SortIcon, Object.assign({}, sortProps, { className: "tableSortArrow", icon: getSortIcon(col.isSorted, col.isSortedDesc), isSorted: col.isSorted })) }), showFilter ? jsxRuntimeExports.jsx(FilterContainer, { col }) : null, showOptions && jsxRuntimeExports.jsx(OptionsMenu, { allColumns, allowColumnHiding: allowHiding, numVisibleColumns, resetResizing, setAllFilters, style: tableOptionsStyle })] }), jsxRuntimeExports.jsx(ResizeBorder, Object.assign({}, resizerProps))] })
|
|
39507
39564
|
);
|
|
@@ -39510,28 +39567,15 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
39510
39567
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
39511
39568
|
useDeepCompare([tableProps, totalColumnsWidth, headerGroups])
|
|
39512
39569
|
);
|
|
39513
|
-
return jsxRuntimeExports.jsx(Wrapper$2, Object.assign({}, getTableProps(), { "$hasMaxRows": !!maxRows, className: `${className} ${hasFixedColumns ? "sticky" : ""}`, style: Object.assign({ height: maxRows ? (Math.min(rows.length, maxRows) + 1) *
|
|
39514
|
-
return jsxRuntimeExports.jsx(StyledFixedSizeList, { height, innerElementType: renderTable, itemCount: itemCount || rows.length, itemData: createItemData$1(width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns), itemSize:
|
|
39570
|
+
return jsxRuntimeExports.jsx(Wrapper$2, Object.assign({}, getTableProps(), { "$hasMaxRows": !!maxRows, className: `${className} ${hasFixedColumns ? "sticky" : ""}`, style: Object.assign({ height: maxRows ? (Math.min(rows.length, maxRows) + 1) * tableRowHeight : "100%" }, style), children: jsxRuntimeExports.jsx(AutoSizer, { children: ({ height, width }) => {
|
|
39571
|
+
return jsxRuntimeExports.jsx(StyledFixedSizeList, { height, innerElementType: renderTable, itemCount: itemCount || rows.length, itemData: createItemData$1(width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns, tableRowHeight), itemSize: tableRowHeight, onItemsRendered, style: {
|
|
39515
39572
|
overflowX: width < totalColumnsWidth ? "auto" : "hidden",
|
|
39516
|
-
overflowY: height < (rows.length + 1) *
|
|
39573
|
+
overflowY: height < (rows.length + 1) * tableRowHeight ? "auto" : "hidden"
|
|
39517
39574
|
}, width, children: RenderRow }, "table-list");
|
|
39518
39575
|
} }) }));
|
|
39519
39576
|
});
|
|
39520
39577
|
Table.displayName = "Table";
|
|
39521
|
-
Table.ActionColumn =
|
|
39522
|
-
const width = actions.includes(Actions.SELECT) ? 52 : actions.length * 24 + 24;
|
|
39523
|
-
return {
|
|
39524
|
-
Cell: ActionCell,
|
|
39525
|
-
Header: actions.includes(Actions.SELECT) && !disableSelectAll ? SelectHeader : "",
|
|
39526
|
-
accessor: accessor || "actions",
|
|
39527
|
-
actions,
|
|
39528
|
-
disableSortBy: true,
|
|
39529
|
-
maxWidth: width,
|
|
39530
|
-
minWidth: actions.includes(Actions.SELECT) ? 52 : 48,
|
|
39531
|
-
sticky: sticky || null,
|
|
39532
|
-
width
|
|
39533
|
-
};
|
|
39534
|
-
};
|
|
39578
|
+
Table.ActionColumn = createActionColumn;
|
|
39535
39579
|
Table.Actions = Actions;
|
|
39536
39580
|
Table.cells = cells;
|
|
39537
39581
|
styled.div`
|
dara/core/auth/definitions.py
CHANGED
|
@@ -18,6 +18,7 @@ limitations under the License.
|
|
|
18
18
|
from contextvars import ContextVar
|
|
19
19
|
from datetime import datetime
|
|
20
20
|
|
|
21
|
+
from pydantic import ConfigDict
|
|
21
22
|
from typing_extensions import TypedDict
|
|
22
23
|
|
|
23
24
|
from dara.core.base_definitions import DaraBaseModel as BaseModel
|
|
@@ -60,6 +61,9 @@ class UserData(BaseModel):
|
|
|
60
61
|
identity_email: str | None = None
|
|
61
62
|
groups: list[str] | None = []
|
|
62
63
|
|
|
64
|
+
# allow extra for more flexibility in custom oidc configs
|
|
65
|
+
model_config = ConfigDict(extra='allow')
|
|
66
|
+
|
|
63
67
|
@classmethod
|
|
64
68
|
def from_token_data(cls, token_data: TokenData):
|
|
65
69
|
return cls(
|
dara/core/auth/oidc/config.py
CHANGED
|
@@ -65,6 +65,7 @@ class OIDCAuthConfig(BaseAuthConfig):
|
|
|
65
65
|
- SSO_EXTRA_AUDIENCE - if set, extra audiences to verify against the ID token in addition to `sso_client_id`
|
|
66
66
|
- SSO_SCOPES - space-separated list of scopes to request from the identity provider, defaults to `openid`
|
|
67
67
|
- SSO_JWT_ALGO - algorithm to use for verifying IDP-provided JWTs, defaults to `ES256`
|
|
68
|
+
- SSO_USE_USERINFO - if set to `true`, fetch additional claims from the userinfo endpoint when an access token is available
|
|
68
69
|
"""
|
|
69
70
|
|
|
70
71
|
# NOTE: the config follows OIDC specification, but makes a few concessions
|
|
@@ -209,29 +210,84 @@ class OIDCAuthConfig(BaseAuthConfig):
|
|
|
209
210
|
state = self.generate_state(redirect_to=body.redirect_to)
|
|
210
211
|
return RedirectResponse(redirect_uri=self.get_authorization_url(state))
|
|
211
212
|
|
|
212
|
-
def
|
|
213
|
+
async def fetch_userinfo(self, access_token: str) -> dict | None:
|
|
213
214
|
"""
|
|
214
|
-
|
|
215
|
+
Fetch user information from the OIDC userinfo endpoint.
|
|
216
|
+
|
|
217
|
+
Per OpenID Connect Core 1.0 Section 5.3, the userinfo endpoint returns claims
|
|
218
|
+
about the authenticated user. This is useful when the ID token doesn't contain
|
|
219
|
+
all required claims.
|
|
220
|
+
|
|
221
|
+
:param access_token: The access token to authenticate the request
|
|
222
|
+
:return: Dictionary of userinfo claims, or None if the request fails
|
|
223
|
+
"""
|
|
224
|
+
userinfo_endpoint = self.discovery.userinfo_endpoint
|
|
225
|
+
if not userinfo_endpoint:
|
|
226
|
+
dev_logger.warning('Userinfo endpoint not available in OIDC discovery')
|
|
227
|
+
return None
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
response = await self.client.get(
|
|
231
|
+
userinfo_endpoint,
|
|
232
|
+
headers={'Authorization': f'Bearer {access_token}'},
|
|
233
|
+
timeout=10,
|
|
234
|
+
)
|
|
235
|
+
response.raise_for_status()
|
|
236
|
+
return response.json()
|
|
237
|
+
except httpx.HTTPStatusError as e:
|
|
238
|
+
dev_logger.warning(
|
|
239
|
+
f'Failed to fetch userinfo: HTTP {e.response.status_code}',
|
|
240
|
+
)
|
|
241
|
+
return None
|
|
242
|
+
except httpx.RequestError as e:
|
|
243
|
+
dev_logger.warning(f'Failed to fetch userinfo: {e}')
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
def extract_user_data(self, claims: IdTokenClaims, userinfo: dict | None = None) -> UserData:
|
|
247
|
+
"""
|
|
248
|
+
Extract user data from ID token claims and optional userinfo response.
|
|
215
249
|
|
|
216
250
|
Override this method in subclasses to handle provider-specific claim structures.
|
|
217
251
|
The default implementation uses standard OIDC claims, with support for the
|
|
218
|
-
non-standard 'identity' claim.
|
|
252
|
+
non-standard 'identity' claim. When userinfo is provided and SSO_USE_USERINFO
|
|
253
|
+
is enabled, userinfo claims take precedence over ID token claims.
|
|
219
254
|
|
|
220
255
|
:param claims: Decoded ID token claims
|
|
256
|
+
:param userinfo: Optional userinfo response from the userinfo endpoint
|
|
221
257
|
:return: UserData extracted from the claims
|
|
222
258
|
"""
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
identity_email =
|
|
259
|
+
oidc_settings = get_oidc_settings()
|
|
260
|
+
|
|
261
|
+
# When userinfo is provided and use_userinfo is enabled, prefer userinfo claims
|
|
262
|
+
if userinfo and oidc_settings.use_userinfo:
|
|
263
|
+
# userinfo 'sub' must match id_token 'sub' per OIDC spec
|
|
264
|
+
identity_id = userinfo.get('sub') or claims.sub
|
|
265
|
+
identity_email = userinfo.get('email') or claims.email
|
|
266
|
+
identity_name = (
|
|
267
|
+
userinfo.get('name')
|
|
268
|
+
or userinfo.get('preferred_username')
|
|
269
|
+
or userinfo.get('nickname')
|
|
270
|
+
or (
|
|
271
|
+
f'{userinfo.get("given_name", "")} {userinfo.get("family_name", "")}'.strip()
|
|
272
|
+
if userinfo.get('given_name') or userinfo.get('family_name')
|
|
273
|
+
else None
|
|
274
|
+
)
|
|
275
|
+
)
|
|
276
|
+
groups = userinfo.get('groups') or claims.groups
|
|
230
277
|
else:
|
|
231
|
-
#
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
278
|
+
# Check for non-standard 'identity' claim (Causalens IDP)
|
|
279
|
+
# This is a nested object with id, name, email fields
|
|
280
|
+
identity_claim = getattr(claims, 'identity', None)
|
|
281
|
+
if isinstance(identity_claim, dict):
|
|
282
|
+
identity_id = identity_claim.get('id') or claims.sub
|
|
283
|
+
identity_name = identity_claim.get('name')
|
|
284
|
+
identity_email = identity_claim.get('email') or claims.email
|
|
285
|
+
else:
|
|
286
|
+
# Standard OIDC: use 'sub' as the identity ID
|
|
287
|
+
identity_id = claims.sub
|
|
288
|
+
identity_email = claims.email
|
|
289
|
+
identity_name = None
|
|
290
|
+
groups = claims.groups
|
|
235
291
|
|
|
236
292
|
# Fall back to standard claims for name if not set
|
|
237
293
|
if not identity_name:
|
|
@@ -252,7 +308,7 @@ class OIDCAuthConfig(BaseAuthConfig):
|
|
|
252
308
|
identity_id=identity_id,
|
|
253
309
|
identity_name=identity_name,
|
|
254
310
|
identity_email=identity_email,
|
|
255
|
-
groups=
|
|
311
|
+
groups=groups,
|
|
256
312
|
)
|
|
257
313
|
|
|
258
314
|
def verify_token(self, token: str) -> TokenData:
|
|
@@ -291,7 +347,7 @@ class OIDCAuthConfig(BaseAuthConfig):
|
|
|
291
347
|
claims = decode_id_token(token)
|
|
292
348
|
|
|
293
349
|
# Extract user data (can be overridden for provider-specific claim structures)
|
|
294
|
-
user_data = self.
|
|
350
|
+
user_data = self.extract_user_data(claims)
|
|
295
351
|
|
|
296
352
|
# Verify user has access based on groups
|
|
297
353
|
self.verify_user_access(user_data)
|
|
@@ -390,6 +446,8 @@ class OIDCAuthConfig(BaseAuthConfig):
|
|
|
390
446
|
:return: Tuple of (new_session_token, new_refresh_token)
|
|
391
447
|
:raises HTTPException: If the refresh fails
|
|
392
448
|
"""
|
|
449
|
+
oidc_settings = get_oidc_settings()
|
|
450
|
+
|
|
393
451
|
# Request new tokens from the IDP
|
|
394
452
|
oidc_tokens = await get_token_from_idp(
|
|
395
453
|
self,
|
|
@@ -406,8 +464,13 @@ class OIDCAuthConfig(BaseAuthConfig):
|
|
|
406
464
|
# Decode and verify the new ID token
|
|
407
465
|
claims = decode_id_token(oidc_tokens.id_token)
|
|
408
466
|
|
|
467
|
+
# Fetch userinfo if enabled and we have an access token
|
|
468
|
+
userinfo = None
|
|
469
|
+
if oidc_settings.use_userinfo and oidc_tokens.access_token:
|
|
470
|
+
userinfo = await self.fetch_userinfo(oidc_tokens.access_token)
|
|
471
|
+
|
|
409
472
|
# Extract user data from claims
|
|
410
|
-
user_data = self.
|
|
473
|
+
user_data = self.extract_user_data(claims, userinfo=userinfo)
|
|
411
474
|
|
|
412
475
|
# Verify user still has access
|
|
413
476
|
self.verify_user_access(user_data)
|
dara/core/auth/oidc/routes.py
CHANGED
|
@@ -100,8 +100,13 @@ async def sso_callback(
|
|
|
100
100
|
# Decode and verify the ID token
|
|
101
101
|
claims = decode_id_token(oidc_tokens.id_token)
|
|
102
102
|
|
|
103
|
+
# Fetch userinfo if enabled and we have an access token
|
|
104
|
+
userinfo = None
|
|
105
|
+
if oidc_settings.use_userinfo and oidc_tokens.access_token:
|
|
106
|
+
userinfo = await auth_config.fetch_userinfo(oidc_tokens.access_token)
|
|
107
|
+
|
|
103
108
|
# Extract user data from claims (handles both standard OIDC and Causalens identity claim)
|
|
104
|
-
user_data = auth_config.
|
|
109
|
+
user_data = auth_config.extract_user_data(claims, userinfo=userinfo)
|
|
105
110
|
|
|
106
111
|
# Verify user has access based on groups
|
|
107
112
|
auth_config.verify_user_access(user_data)
|
dara/core/auth/oidc/settings.py
CHANGED
|
@@ -24,6 +24,8 @@ class OIDCSettings(BaseSettings):
|
|
|
24
24
|
verify_audience: bool = False
|
|
25
25
|
extra_audience: list[str] | None = None
|
|
26
26
|
allowed_identity_id: str | None = None
|
|
27
|
+
use_userinfo: bool = False
|
|
28
|
+
"""If True, fetch additional claims from the userinfo endpoint when an access token is available."""
|
|
27
29
|
|
|
28
30
|
model_config = SettingsConfigDict(env_file='.env', extra='allow', env_prefix='sso_')
|
|
29
31
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dara-core
|
|
3
|
-
Version: 1.23.
|
|
3
|
+
Version: 1.23.2a1
|
|
4
4
|
Summary: Dara Framework Core
|
|
5
5
|
Home-page: https://dara.causalens.com/
|
|
6
6
|
License: Apache-2.0
|
|
@@ -21,10 +21,10 @@ Requires-Dist: cachetools (>=5.0.0)
|
|
|
21
21
|
Requires-Dist: certifi (>=2024.7.4)
|
|
22
22
|
Requires-Dist: click (>=8.1.3,<9.0.0)
|
|
23
23
|
Requires-Dist: colorama (>=0.4.6,<0.5.0)
|
|
24
|
-
Requires-Dist: create-dara-app (==1.23.
|
|
24
|
+
Requires-Dist: create-dara-app (==1.23.2-alpha.1)
|
|
25
25
|
Requires-Dist: croniter (>=6.0.0,<7.0.0)
|
|
26
26
|
Requires-Dist: cryptography (>=42.0.4)
|
|
27
|
-
Requires-Dist: dara-components (==1.23.
|
|
27
|
+
Requires-Dist: dara-components (==1.23.2-alpha.1) ; extra == "all"
|
|
28
28
|
Requires-Dist: exceptiongroup (>=1.1.3,<2.0.0)
|
|
29
29
|
Requires-Dist: fastapi (>=0.115.0,<0.121.0)
|
|
30
30
|
Requires-Dist: fastapi_vite_dara (==0.4.0)
|
|
@@ -55,7 +55,7 @@ Description-Content-Type: text/markdown
|
|
|
55
55
|
|
|
56
56
|
# Dara Application Framework
|
|
57
57
|
|
|
58
|
-
<img src="https://github.com/causalens/dara/blob/v1.23.
|
|
58
|
+
<img src="https://github.com/causalens/dara/blob/v1.23.2-alpha.1/img/dara_light.svg?raw=true">
|
|
59
59
|
|
|
60
60
|

|
|
61
61
|
[](https://www.apache.org/licenses/LICENSE-2.0)
|
|
@@ -100,7 +100,7 @@ source .venv/bin/activate
|
|
|
100
100
|
dara start
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-

|
|
104
104
|
|
|
105
105
|
Note: `pip` installation uses [PEP 660](https://peps.python.org/pep-0660/) `pyproject.toml`-based editable installs which require `pip >= 21.3` and `setuptools >= 64.0.0`. You can upgrade both with:
|
|
106
106
|
|
|
@@ -117,9 +117,9 @@ Explore some of our favorite apps - a great way of getting started and getting t
|
|
|
117
117
|
|
|
118
118
|
| Dara App | Description |
|
|
119
119
|
| -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
120
|
-
|  | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
|
|
121
|
+
|  | Demonstrates how to enable the user to interact with plots, trigger actions based on clicks, mouse movements and other interactions with `Bokeh` or `Plotly` plots |
|
|
122
|
+
|  | Demonstrates how to use the `CausalGraphViewer` component to display your graphs or networks, customising the displayed information through colors and tooltips, and updating the page based on user interaction. |
|
|
123
123
|
|
|
124
124
|
Check out our [App Gallery](https://dara.causalens.com/gallery) for more inspiration!
|
|
125
125
|
|
|
@@ -146,9 +146,9 @@ And the supporting UI packages and tools.
|
|
|
146
146
|
- `ui-utils` - miscellaneous utility functions
|
|
147
147
|
- `ui-widgets` - widget components
|
|
148
148
|
|
|
149
|
-
More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.23.
|
|
149
|
+
More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.23.2-alpha.1/CONTRIBUTING.md) file.
|
|
150
150
|
|
|
151
151
|
## License
|
|
152
152
|
|
|
153
|
-
Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.23.
|
|
153
|
+
Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.23.2-alpha.1/LICENSE).
|
|
154
154
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
dara/core/__init__.py,sha256=yTp-lXT0yy9XqLGYWlmjPgFG5g2eEg2KhKo8KheTHoo,1408
|
|
2
2
|
dara/core/_assets/__init__.py,sha256=13vMoWHvl1zcFcjNHh8lbTwWOvu4f7krYSco978qxwM,723
|
|
3
3
|
dara/core/_assets/auto_js/dara.core.css,sha256=yT3PKpi2sKI2-kQIF8xtVbTPQqgpK7-Ua7tfzDPuSsI,4095881
|
|
4
|
-
dara/core/_assets/auto_js/dara.core.umd.cjs,sha256=
|
|
4
|
+
dara/core/_assets/auto_js/dara.core.umd.cjs,sha256=JBxFuCmrugsMdISIVkEEWbdCSht8gydVvwt2Vksnmyw,5166175
|
|
5
5
|
dara/core/_assets/auto_js/react-dom.development.js,sha256=vR2Fq5LXMKS5JsTo2CMl6oGCJT8scJV2wXSteTtx8aE,1077040
|
|
6
6
|
dara/core/_assets/auto_js/react-is.development.js,sha256=2IRgmaphdMq6wx2MbsmVUQ0UwapGETNjgano3XEkGcc,7932
|
|
7
7
|
dara/core/_assets/auto_js/react-query.development.js,sha256=lI2fTKMvWmjbagGQaVtZYr51_-C_U_so064JwetuDS0,130366
|
|
@@ -12,12 +12,12 @@ dara/core/actions.py,sha256=rC5Tu79AFNWMv0CJuchBnoy6pETIFh_1RTSqxrolArI,947
|
|
|
12
12
|
dara/core/auth/__init__.py,sha256=HJoYIVPzbpwzN_RUHjGpSJj4o5TmHz9yFyZGiRiObCk,906
|
|
13
13
|
dara/core/auth/base.py,sha256=NJmUJqA-W8AVKIQbX_0BoHoZqtU1Iz6cJ16RKdcdIyU,3642
|
|
14
14
|
dara/core/auth/basic.py,sha256=sglIaogCslG2HlDMjFsaaJhOJeXUW-QQLTIYPaUPxAU,4927
|
|
15
|
-
dara/core/auth/definitions.py,sha256=
|
|
15
|
+
dara/core/auth/definitions.py,sha256=ZZMXJbMFY42Q5YaJCMwZT0bivnxq7TaIoug7uYqAGzE,3988
|
|
16
16
|
dara/core/auth/oidc/__init__.py,sha256=UWdhFvDqLCoILaKVWbmrrJgiMgg9wlVZgCxRvf_HGHM,65
|
|
17
|
-
dara/core/auth/oidc/config.py,sha256=
|
|
17
|
+
dara/core/auth/oidc/config.py,sha256=XdiJSC2-9g8fqM-FkmU0c079VqnrApVv-MKwEBwk50s,23753
|
|
18
18
|
dara/core/auth/oidc/definitions.py,sha256=KLlUl2Y7n6prX0JSAwPde6J43iMs6uUY07OG7kumRPw,17568
|
|
19
|
-
dara/core/auth/oidc/routes.py,sha256=
|
|
20
|
-
dara/core/auth/oidc/settings.py,sha256=
|
|
19
|
+
dara/core/auth/oidc/routes.py,sha256=wlYp8VZP4C4BFHqJPPYHi3PdLd8SetdG2CtG3B4laBk,5579
|
|
20
|
+
dara/core/auth/oidc/settings.py,sha256=wU6fmkqhTNzMNFUxizFaaavH2cKuXeqRIzMi3TpwLhA,2233
|
|
21
21
|
dara/core/auth/oidc/utils.py,sha256=IL17tStRNpkAQI3M17nrzGeb4xr47zSLPo4wT7LMwBQ,5145
|
|
22
22
|
dara/core/auth/routes.py,sha256=dtOxpFotnt4XQ4spW3mbyM7ThYRvfIA_oRK5X5lyYHg,7256
|
|
23
23
|
dara/core/auth/utils.py,sha256=_aHZ98qMJ0VLE9Zfvj5biPARhe973_eyTx2qxnufzRE,7290
|
|
@@ -134,8 +134,8 @@ dara/core/visual/themes/__init__.py,sha256=aM4mgoIYo2neBSw5FRzswsht7PUKjLthiHLmF
|
|
|
134
134
|
dara/core/visual/themes/dark.py,sha256=QazCRDqh_SCOyQhdwMkH1wbHf301oL7gCFj91plbLww,2020
|
|
135
135
|
dara/core/visual/themes/definitions.py,sha256=dtET2YUlwXkO6gJ23MqSb8gIq-LxJ343CWsgueWSifM,2787
|
|
136
136
|
dara/core/visual/themes/light.py,sha256=dtHb6Q1HOb5r_AvJfe0vZajikVc-GnBEUrGsTcI5MHA,2022
|
|
137
|
-
dara_core-1.23.
|
|
138
|
-
dara_core-1.23.
|
|
139
|
-
dara_core-1.23.
|
|
140
|
-
dara_core-1.23.
|
|
141
|
-
dara_core-1.23.
|
|
137
|
+
dara_core-1.23.2a1.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
|
|
138
|
+
dara_core-1.23.2a1.dist-info/METADATA,sha256=X5eVDXyUa0Ikj_AGLfwFOIrMgxyI2NeLL1rvPG-YZB8,7598
|
|
139
|
+
dara_core-1.23.2a1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
140
|
+
dara_core-1.23.2a1.dist-info/entry_points.txt,sha256=nAT9o1kJCmTK1saDh29PFGFD6cbxDDDjTj31HDEDwfU,197
|
|
141
|
+
dara_core-1.23.2a1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|