mirror of
https://github.com/gravitational/teleport
synced 2024-10-20 17:23:22 +00:00
Fix max_duration when session TTL is short (#32356)
* Fix max_duration when session TTL is short * prettier * Fix linter issues * The commit makes minor corrections to the comment, aligning it with the standard comment style for better code readability and to avoid potential linting errors. The beginning of the multi-line comment has been changed from '/*' to '/**', making it consistent with the standard JavaScript documentation comment style. This ensures our linter recognizes it as a documentation comment, thus preventing any unnecessary linter warnings or errors. * Update middleValues function in AccessRequests service The middleValues function has been updated to include 'accessRequest.created' in addition to the 'sessionTTL' and 'maxDuration'. * prettier * Address code review comments Support session TTL < max duration < 1d case * Round access request duration to 10 minutes
This commit is contained in:
parent
d0e50809da
commit
21895cc241
|
@ -83,10 +83,12 @@ export async function getDurationOptions(
|
|||
return [];
|
||||
}
|
||||
|
||||
return middleValues(accessRequest.sessionTTL, accessRequest.maxDuration).map(
|
||||
duration => ({
|
||||
value: duration.timestamp,
|
||||
label: formatDuration(duration.duration),
|
||||
})
|
||||
);
|
||||
return middleValues(
|
||||
accessRequest.created,
|
||||
accessRequest.sessionTTL,
|
||||
accessRequest.maxDuration
|
||||
).map(duration => ({
|
||||
value: duration.timestamp,
|
||||
label: formatDuration(duration.duration),
|
||||
}));
|
||||
}
|
||||
|
|
283
web/packages/teleport/src/AccessRequests/utils.test.ts
Normal file
283
web/packages/teleport/src/AccessRequests/utils.test.ts
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Duration } from 'date-fns';
|
||||
|
||||
import {
|
||||
middleValues,
|
||||
roundToNearestTenMinutes,
|
||||
} from 'teleport/AccessRequests/utils';
|
||||
|
||||
// Generate testing response
|
||||
function generateResponse(
|
||||
currentDate: Date,
|
||||
values: Array<{
|
||||
days: number;
|
||||
hours: number;
|
||||
minutes: number;
|
||||
}>
|
||||
) {
|
||||
const defaultValues = {
|
||||
years: 0,
|
||||
months: 0,
|
||||
minutes: 0,
|
||||
seconds: 0,
|
||||
};
|
||||
const result = [];
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
const { days, hours, minutes } = values[i];
|
||||
const duration = {
|
||||
...defaultValues,
|
||||
days,
|
||||
hours,
|
||||
minutes,
|
||||
};
|
||||
let d = new Date(currentDate);
|
||||
d.setDate(currentDate.getDate() + days);
|
||||
d.setHours(currentDate.getHours() + hours);
|
||||
d.setMinutes(currentDate.getMinutes() + minutes);
|
||||
const timestamp = d.getTime();
|
||||
result.push({
|
||||
timestamp,
|
||||
duration,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
describe('generate middle times', () => {
|
||||
const cases: {
|
||||
name: string;
|
||||
created: string;
|
||||
sessionTTL: string;
|
||||
maxDuration: string;
|
||||
expected: Array<{
|
||||
days: number;
|
||||
hours: number;
|
||||
minutes: number;
|
||||
}>;
|
||||
}[] = [
|
||||
{
|
||||
name: '3 days max',
|
||||
created: '2021-09-01T00:00:00.000Z',
|
||||
sessionTTL: '2021-09-01T01:00:00.000Z',
|
||||
maxDuration: '2021-09-04T00:00:00.000Z',
|
||||
expected: [
|
||||
{
|
||||
days: 0,
|
||||
hours: 1,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 1,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 2,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 3,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '1 day max',
|
||||
created: '2021-09-01T00:00:00.000Z',
|
||||
sessionTTL: '2021-09-01T01:00:00.000Z',
|
||||
maxDuration: '2021-09-02T00:00:00.000Z',
|
||||
expected: [
|
||||
{
|
||||
days: 0,
|
||||
hours: 1,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 1,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'session ttl is 10 min',
|
||||
created: '2021-09-01T00:00:00.000Z',
|
||||
sessionTTL: '2021-09-01T00:10:00.000Z',
|
||||
maxDuration: '2021-09-03T00:00:00.000Z',
|
||||
expected: [
|
||||
{
|
||||
days: 0,
|
||||
hours: 0,
|
||||
minutes: 10,
|
||||
},
|
||||
{
|
||||
days: 1,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 2,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '10 minutes min - real values',
|
||||
created: '2023-09-21T20:50:52.669012121Z',
|
||||
sessionTTL: '2023-09-21T21:00:52.669081473Z',
|
||||
maxDuration: '2023-09-27T20:50:52.669081473Z',
|
||||
expected: [
|
||||
{
|
||||
days: 0,
|
||||
hours: 0,
|
||||
minutes: 10,
|
||||
},
|
||||
{
|
||||
days: 1,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 2,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 3,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 4,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 5,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 6,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'only one option generated',
|
||||
created: '2023-09-21T10:00:52.669012121Z',
|
||||
sessionTTL: '2023-09-21T15:00:52.669081473Z',
|
||||
maxDuration: '2023-09-21T15:00:52.669081473Z',
|
||||
expected: [
|
||||
{
|
||||
days: 0,
|
||||
hours: 5,
|
||||
minutes: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'generate all options if max duration is grater than session ttl but less than 1d',
|
||||
created: '2023-09-21T10:00:52.669012121Z',
|
||||
sessionTTL: '2023-09-21T15:00:52.669081473Z',
|
||||
maxDuration: '2023-09-21T17:00:52.669081473Z',
|
||||
expected: [
|
||||
{
|
||||
days: 0,
|
||||
hours: 5,
|
||||
minutes: 0,
|
||||
},
|
||||
{
|
||||
days: 0,
|
||||
hours: 7,
|
||||
minutes: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
test.each(cases)(
|
||||
'$name',
|
||||
({ sessionTTL, maxDuration, created, expected }) => {
|
||||
const result = middleValues(
|
||||
new Date(created),
|
||||
new Date(sessionTTL),
|
||||
new Date(maxDuration)
|
||||
);
|
||||
expect(result).toEqual(generateResponse(new Date(created), expected));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('round to nearest 10 minutes', () => {
|
||||
const cases: {
|
||||
name: string;
|
||||
input: Duration;
|
||||
expected: Duration;
|
||||
}[] = [
|
||||
{
|
||||
name: 'round up',
|
||||
input: { minutes: 9, seconds: 0 },
|
||||
expected: { minutes: 10, seconds: 0 },
|
||||
},
|
||||
{
|
||||
name: 'round down',
|
||||
input: { minutes: 11, seconds: 0 },
|
||||
expected: { minutes: 10, seconds: 0 },
|
||||
},
|
||||
{
|
||||
name: 'round to 10',
|
||||
input: { minutes: 15, seconds: 0 },
|
||||
expected: { minutes: 20, seconds: 0 },
|
||||
},
|
||||
{
|
||||
name: 'do not round to 0',
|
||||
input: { minutes: 1, seconds: 0 },
|
||||
expected: { minutes: 10, seconds: 0 },
|
||||
},
|
||||
{
|
||||
name: 'round minutes to 0 when days or hours are present',
|
||||
input: { hours: 3, minutes: 1, seconds: 0 },
|
||||
expected: { hours: 3, minutes: 0, seconds: 0 },
|
||||
},
|
||||
{
|
||||
name: 'do not round to 0',
|
||||
input: { minutes: 0, seconds: 0 },
|
||||
expected: { minutes: 10, seconds: 0 },
|
||||
},
|
||||
{
|
||||
name: 'seconds are removed',
|
||||
input: { minutes: 9, seconds: 10 },
|
||||
expected: { minutes: 10, seconds: 0 },
|
||||
},
|
||||
{
|
||||
name: "duration doesn't change when days are present",
|
||||
input: { days: 1, minutes: 9, seconds: 10 },
|
||||
expected: { days: 1, minutes: 10, seconds: 0 },
|
||||
},
|
||||
];
|
||||
|
||||
test.each(cases)('$name', ({ input, expected }) => {
|
||||
const result = roundToNearestTenMinutes(input);
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
|
@ -20,57 +20,88 @@ import {
|
|||
Duration,
|
||||
intervalToDuration,
|
||||
isAfter,
|
||||
isBefore,
|
||||
} from 'date-fns';
|
||||
|
||||
interface TimeDuration {
|
||||
type TimeDuration = {
|
||||
timestamp: number;
|
||||
duration: Duration;
|
||||
};
|
||||
|
||||
// Round the duration to the nearest 10 minutes
|
||||
// Example:
|
||||
// 9m -> 10m
|
||||
// 10m -> 10m
|
||||
// 11m -> 10m
|
||||
// 15m -> 20m
|
||||
// 1d -> 1d
|
||||
// 1d 1h -> 1d 1h
|
||||
// The only exception is 0m, which is rounded to 10m
|
||||
export function roundToNearestTenMinutes(date: Duration): Duration {
|
||||
let minutes = date.minutes;
|
||||
let roundedMinutes = Math.round(minutes / 10) * 10; // Round to the nearest 10
|
||||
if (roundedMinutes === 0 && !date.days && !date.hours) {
|
||||
// Do not round down to 0. This
|
||||
roundedMinutes = 10;
|
||||
}
|
||||
date.minutes = roundedMinutes;
|
||||
date.seconds = 0;
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
export function middleValues(start: Date, end: Date): TimeDuration[] {
|
||||
const now = new Date();
|
||||
|
||||
const roundDuration = (d: Date) =>
|
||||
roundToNearestHour(
|
||||
// Generate a list of middle values between start and end. The first value is the
|
||||
// session TTL that is rounded to the nearest hour. The rest of the values are
|
||||
// rounded to the nearest day. Example:
|
||||
//
|
||||
// created: 2021-09-01T00:00:00.000Z
|
||||
// start: 2021-09-01T01:00:00.000Z
|
||||
// end: 2021-09-03T00:00:00.000Z
|
||||
// now: 2021-09-01T00:00:00.000Z
|
||||
//
|
||||
// returns: [1h, 1d, 2d, 3d]
|
||||
export function middleValues(
|
||||
created: Date,
|
||||
start: Date,
|
||||
end: Date
|
||||
): TimeDuration[] {
|
||||
const getInterval = (d: Date) =>
|
||||
roundToNearestTenMinutes(
|
||||
intervalToDuration({
|
||||
start: now,
|
||||
start: created,
|
||||
end: d,
|
||||
})
|
||||
);
|
||||
|
||||
const points: Date[] = [start];
|
||||
|
||||
if (isAfter(addDays(start, 1), end)) {
|
||||
if (isAfter(addDays(created, 1), end)) {
|
||||
// Add all possible options to the list. This covers the case when the
|
||||
// max duration is less than 24 hours.
|
||||
if (isBefore(addHours(points[points.length - 1], 1), end)) {
|
||||
points.push(end);
|
||||
}
|
||||
|
||||
return points.map(d => ({
|
||||
timestamp: d.getTime(),
|
||||
duration: roundDuration(d),
|
||||
duration: getInterval(d),
|
||||
}));
|
||||
}
|
||||
|
||||
points.push(addDays(now, 1));
|
||||
points.push(addDays(created, 1));
|
||||
|
||||
while (points[points.length - 1] <= end) {
|
||||
points.push(addHours(points[points.length - 1], 24));
|
||||
// I also prefer while(true), but our linter doesn't
|
||||
for (;;) {
|
||||
const next = addHours(points[points.length - 1], 24);
|
||||
// Allow next == end
|
||||
if (next > end) {
|
||||
break;
|
||||
}
|
||||
points.push(next);
|
||||
}
|
||||
|
||||
return points.map(d => ({
|
||||
timestamp: d.getTime(),
|
||||
duration: roundDuration(d),
|
||||
duration: getInterval(d),
|
||||
}));
|
||||
}
|
||||
|
||||
export function roundToNearestHour(duration: Duration): Duration {
|
||||
if (duration.minutes > 30) {
|
||||
duration.hours += 1;
|
||||
}
|
||||
|
||||
if (duration.hours >= 24) {
|
||||
duration.days += 1;
|
||||
duration.hours -= 24;
|
||||
}
|
||||
|
||||
duration.minutes = 0;
|
||||
duration.seconds = 0;
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue