Skip to content

Establish incredibly simple time series auth restriction using the ex… - #2012

Merged
MikeNeilson merged 4 commits into
developfrom
hotfix/private-timeseries
Oct 26, 2026
Merged

MikeNeilson merged 4 commits into
developfrom
hotfix/private-timeseries

Conversation

@MikeNeilson

Copy link
Copy Markdown
Contributor

…isting DMZ include list.

Summary

Simplest possible way to allow restricting access to time series given current design.

Related Issue

Validation

Integration tests of the logic. Autheticated users with CWMS Users role can see everything, anyone else can only get values if the given time series in in the DMZ Include List. Basically what we had when the public database only received the public data.

NOTE: zero performance testing was done. difference should be minimal. Will have @msweier test in dev once merged and deployed.

Checklist

  • AI tools used

@msweier msweier left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR. Much needed! I used an AI to find a couple things.

return;
} else if (hasTsGroupInfo) {
// just group provided
latestValues = dao.findRecentsInRange(office, categoryId, groupId, pastLimit, futureLimit, unitSystem);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/timeseries/recent can be bypassed by asking for the whole group. The DMZ check works for when you specify TSIDs, but there doesn't appear to be a DMZ check if you ask for the whole group.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, there wasn't a good way to handle that that I could think of. The group is defined and it's pulled in. Though we could loop through after and remove. Not great performance, but this is a quickly developed stop gap.

.where(
DSL.and(
DSL.val(office).isNull().or(TS_ID.DB_OFFICE_ID.eq(office)),
TS_ID.CWMS_TS_ID.eq(names))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider making this case insenstive. I think the endpoint is generally case insenstive for the TSIDs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, you're right. That should be case insensitive.

.from(TS_ID)
.where(
DSL.and(
DSL.val(office).isNull().or(TS_ID.DB_OFFICE_ID.eq(office)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the office is null this condition will always be true and there is a chance if there is the same TSID across multiple offices that the DMZ screening logic may use the wrong office and expose a TSID that was intended to be private (although I think this is a remote chance). Consider requiring an OFFICE id or remove the isNull() and filter on an uppercased OFFICE id.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately there's a few points where the office id isn't available so we're kinda stuck. I think that's fewer and far between but I've decided to err on the side of caution.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also it won't always be true. The query works out to

(? is null or db_office_id = ?) and cwms_ts_id = ?

That's why the DSL.and (condition 1, contion 2) construct is used, to force that grouping.
If the office bind has a value it can't be null and thus if not equal will comes out to false.

@MikeNeilson MikeNeilson left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. good points, a few of which I think I can "fix".

.from(TS_ID)
.where(
DSL.and(
DSL.val(office).isNull().or(TS_ID.DB_OFFICE_ID.eq(office)),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately there's a few points where the office id isn't available so we're kinda stuck. I think that's fewer and far between but I've decided to err on the side of caution.

return;
} else if (hasTsGroupInfo) {
// just group provided
latestValues = dao.findRecentsInRange(office, categoryId, groupId, pastLimit, futureLimit, unitSystem);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, there wasn't a good way to handle that that I could think of. The group is defined and it's pulled in. Though we could loop through after and remove. Not great performance, but this is a quickly developed stop gap.

.where(
DSL.and(
DSL.val(office).isNull().or(TS_ID.DB_OFFICE_ID.eq(office)),
TS_ID.CWMS_TS_ID.eq(names))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, you're right. That should be case insensitive.

@MikeNeilson
MikeNeilson force-pushed the hotfix/private-timeseries branch from f04f2c6 to accc955 Compare October 26, 2026 15:21
@MikeNeilson
MikeNeilson requested a review from msweier October 26, 2026 20:18
@MikeNeilson
MikeNeilson merged commit 7763976 into develop Oct 26, 2026
12 checks passed
@MikeNeilson
MikeNeilson deleted the hotfix/private-timeseries branch October 26, 2026 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants