mirror of
https://github.com/wassname/talk.git
synced 2026-08-07 11:29:44 +08:00
[CORL-556] Allow session length configuration (#2530)
* make session duration configurable by tenant * fix expiration date calculation * add form field for session length * use constant for default session * update spec * update fixtures * add missing translation * update snap * fix: use shared variable instead of hardcoded value for tests * fix: adjusted missing expires in parameter
This commit is contained in:
committed by
Wyatt Johnson
parent
ed8be75774
commit
36225e443b
@@ -5,10 +5,12 @@ import { HorizontalGutter } from "coral-ui/components";
|
||||
|
||||
import { OnInitValuesFct } from "./AuthConfigContainer";
|
||||
import AuthIntegrationsConfig from "./AuthIntegrationsConfig";
|
||||
import SessionConfigContainer from "./SessionConfigContainer";
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
auth: PropTypesOf<typeof AuthIntegrationsConfig>["auth"];
|
||||
auth: PropTypesOf<typeof AuthIntegrationsConfig>["auth"] &
|
||||
PropTypesOf<typeof SessionConfigContainer>["auth"];
|
||||
onInitValues: OnInitValuesFct;
|
||||
}
|
||||
|
||||
@@ -18,6 +20,11 @@ const AuthConfig: FunctionComponent<Props> = ({
|
||||
onInitValues,
|
||||
}) => (
|
||||
<HorizontalGutter size="double" data-testid="configure-authContainer">
|
||||
<SessionConfigContainer
|
||||
auth={auth}
|
||||
disabled={disabled}
|
||||
onInitValues={onInitValues}
|
||||
/>
|
||||
<AuthIntegrationsConfig
|
||||
disabled={disabled}
|
||||
auth={auth}
|
||||
|
||||
@@ -135,6 +135,7 @@ const enhanced = withFragmentContainer<Props>({
|
||||
...LocalAuthConfigContainer_auth
|
||||
...OIDCConfigContainer_auth
|
||||
...OIDCConfigContainer_authReadOnly
|
||||
...SessionConfigContainer_auth
|
||||
}
|
||||
`,
|
||||
})(
|
||||
|
||||
@@ -41,6 +41,7 @@ const enhanced = withRouteConfig<Props>({
|
||||
...AuthConfigContainer_settings
|
||||
auth {
|
||||
...AuthConfigContainer_auth
|
||||
...SessionConfigContainer_auth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
|
||||
import { DurationField } from "coral-framework/components";
|
||||
import { ValidationMessage } from "coral-framework/lib/form";
|
||||
import {
|
||||
composeValidators,
|
||||
required,
|
||||
validateWholeNumberGreaterThanOrEqual,
|
||||
} from "coral-framework/lib/validation";
|
||||
import {
|
||||
FieldSet,
|
||||
FormField,
|
||||
HorizontalGutter,
|
||||
InputLabel,
|
||||
} from "coral-ui/components";
|
||||
|
||||
import Header from "../../Header";
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const SessionConfig: FunctionComponent<Props> = ({ disabled }) => (
|
||||
<HorizontalGutter>
|
||||
<HorizontalGutter size="full">
|
||||
<Localized id="configure-auth-settings">
|
||||
<Header>Session settings</Header>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
<FormField container={<FieldSet />}>
|
||||
<Localized id="configure-auth-settings-session-duration-label">
|
||||
<InputLabel container="legend">Session Duration</InputLabel>
|
||||
</Localized>
|
||||
<Field
|
||||
name="auth.sessionDuration"
|
||||
validate={composeValidators(
|
||||
required,
|
||||
validateWholeNumberGreaterThanOrEqual(0)
|
||||
)}
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<DurationField disabled={!!disabled} {...input} />
|
||||
<ValidationMessage meta={meta} />
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</FormField>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
|
||||
export default SessionConfig;
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { SessionConfigContainer_auth as AuthData } from "coral-admin/__generated__/SessionConfigContainer_auth.graphql";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import { OnInitValuesFct } from "./AuthConfigContainer";
|
||||
import SessionConfig from "./SessionConfig";
|
||||
|
||||
interface Props {
|
||||
auth: AuthData;
|
||||
onInitValues: OnInitValuesFct;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
class SessionConfigContainer extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
props.onInitValues({ auth: props.auth });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { disabled } = this.props;
|
||||
return <SessionConfig disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
auth: graphql`
|
||||
fragment SessionConfigContainer_auth on Auth {
|
||||
sessionDuration
|
||||
}
|
||||
`,
|
||||
})(SessionConfigContainer);
|
||||
export default enhanced;
|
||||
@@ -2261,7 +2261,8 @@ compliance.
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
|
||||
>
|
||||
Missing translation "configure-account-features-delete-account-fieldDescriptions"
|
||||
Removes all of their comment data, username, and email
|
||||
address from the site and the database.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
@@ -2326,6 +2327,91 @@ compliance.
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-double"
|
||||
data-testid="configure-authContainer"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-full"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-full"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading3 Typography-colorTextPrimary Header-root"
|
||||
>
|
||||
Session settings
|
||||
</h1>
|
||||
</div>
|
||||
<fieldset
|
||||
className="FieldSet-root Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
|
||||
>
|
||||
<legend
|
||||
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
|
||||
>
|
||||
Session duration
|
||||
</legend>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-itemGutter gutter"
|
||||
>
|
||||
<div
|
||||
className="TextField-root DurationField-value"
|
||||
>
|
||||
<input
|
||||
aria-label="value"
|
||||
autoCapitalize="off"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
className="TextField-input TextField-colorRegular TextField-textAlignCenter"
|
||||
disabled={false}
|
||||
name="auth.sessionDuration-value"
|
||||
onChange={[Function]}
|
||||
placeholder=""
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value="90"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className="SelectField-root"
|
||||
>
|
||||
<select
|
||||
aria-label="unit"
|
||||
className="SelectField-select DurationField-select"
|
||||
disabled={false}
|
||||
name="auth.sessionDuration-unit"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
value="86400"
|
||||
>
|
||||
<option
|
||||
value="3600"
|
||||
>
|
||||
Hours
|
||||
</option>
|
||||
<option
|
||||
value="86400"
|
||||
>
|
||||
Days
|
||||
</option>
|
||||
<option
|
||||
value="604800"
|
||||
>
|
||||
Weeks
|
||||
</option>
|
||||
</select>
|
||||
<span
|
||||
aria-hidden={true}
|
||||
className="SelectField-afterWrapper"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-sm"
|
||||
>
|
||||
expand_more
|
||||
</i>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-double"
|
||||
>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { TOXICITY_THRESHOLD_DEFAULT } from "coral-common/constants";
|
||||
import {
|
||||
DEFAULT_SESSION_LENGTH,
|
||||
TOXICITY_THRESHOLD_DEFAULT,
|
||||
} from "coral-common/constants";
|
||||
import { pureMerge } from "coral-common/utils";
|
||||
import {
|
||||
GQLComment,
|
||||
@@ -86,6 +89,7 @@ export const settings = createFixture<GQLSettings>({
|
||||
},
|
||||
},
|
||||
auth: {
|
||||
sessionDuration: DEFAULT_SESSION_LENGTH,
|
||||
integrations: {
|
||||
local: {
|
||||
enabled: true,
|
||||
@@ -159,6 +163,7 @@ export const settingsWithEmptyAuth = createFixture<GQLSettings>(
|
||||
{
|
||||
id: "settings",
|
||||
auth: {
|
||||
sessionDuration: DEFAULT_SESSION_LENGTH,
|
||||
integrations: {
|
||||
local: {
|
||||
enabled: true,
|
||||
|
||||
@@ -52,3 +52,8 @@ export const ALLOWED_USERNAME_CHANGE_FREQUENCY = 14 * 86400;
|
||||
* deletion.
|
||||
*/
|
||||
export const SCHEDULED_DELETION_TIMESPAN_DAYS = 14;
|
||||
|
||||
/**
|
||||
* DEFAULT_SESSION_LENTTH is the length of time in seconds a session is valid for unless configured in tenant.
|
||||
*/
|
||||
export const DEFAULT_SESSION_LENGTH = 7776000;
|
||||
|
||||
@@ -117,14 +117,18 @@ export async function handleSuccessfulLogin(
|
||||
const tenant = coral.tenant!;
|
||||
|
||||
// Compute the expiry date.
|
||||
const expiresIn = DateTime.fromJSDate(coral.now).plus({ days: 1 });
|
||||
const expiresIn = DateTime.fromJSDate(coral.now).plus({
|
||||
seconds: tenant.auth.sessionDuration,
|
||||
});
|
||||
|
||||
// Grab the token.
|
||||
const token = await signTokenString(
|
||||
signingConfig,
|
||||
user,
|
||||
tenant,
|
||||
{ expiresIn: "1d" },
|
||||
{
|
||||
expiresIn: tenant.auth.sessionDuration,
|
||||
},
|
||||
coral.now
|
||||
);
|
||||
|
||||
@@ -177,14 +181,16 @@ export async function handleOAuth2Callback(
|
||||
const tenant = req.coral!.tenant!;
|
||||
|
||||
// Compute the expiry date.
|
||||
const expiresIn = DateTime.fromJSDate(req.coral!.now).plus({ days: 1 });
|
||||
const expiresIn = DateTime.fromJSDate(req.coral!.now).plus({
|
||||
seconds: tenant.auth.sessionDuration,
|
||||
});
|
||||
|
||||
// Grab the token.
|
||||
const token = await signTokenString(
|
||||
signingConfig,
|
||||
user,
|
||||
tenant,
|
||||
{ expiresIn: "1d" },
|
||||
{ expiresIn: tenant.auth.sessionDuration },
|
||||
req.coral!.now
|
||||
);
|
||||
res.cookie(
|
||||
|
||||
@@ -422,6 +422,7 @@ type LocalAuthIntegration {
|
||||
integration should be displayed in all targets.
|
||||
"""
|
||||
targetFilter: AuthenticationTargetFilter!
|
||||
|
||||
}
|
||||
|
||||
##########################
|
||||
@@ -738,6 +739,10 @@ type Auth {
|
||||
authentication solutions.
|
||||
"""
|
||||
integrations: AuthIntegrations!
|
||||
"""
|
||||
sessionDuration determines the duration in seconds for which an access token is valid
|
||||
"""
|
||||
sessionDuration: Int!
|
||||
}
|
||||
|
||||
################################################################################
|
||||
@@ -3002,6 +3007,10 @@ input SettingsAuthInput {
|
||||
authentication solutions.
|
||||
"""
|
||||
integrations: SettingsAuthIntegrationsInput
|
||||
"""
|
||||
sessionDuration determines the duration in seconds for which an access token is valid
|
||||
"""
|
||||
sessionDuration: Int!
|
||||
}
|
||||
|
||||
input SettingsAkismetExternalIntegrationInput {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Db } from "mongodb";
|
||||
import uuid from "uuid";
|
||||
|
||||
import { DEFAULT_SESSION_LENGTH } from "coral-common/constants";
|
||||
import { LanguageCode } from "coral-common/helpers/i18n/locales";
|
||||
import { DeepPartial, Omit, Sub } from "coral-common/types";
|
||||
import { dotize } from "coral-common/utils/dotize";
|
||||
@@ -114,6 +115,7 @@ export async function createTenant(
|
||||
banned: [],
|
||||
},
|
||||
auth: {
|
||||
sessionDuration: DEFAULT_SESSION_LENGTH,
|
||||
integrations: {
|
||||
local: {
|
||||
enabled: true,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import cookie from "cookie";
|
||||
import { DEFAULT_SESSION_LENGTH } from "coral-common/constants";
|
||||
import { IncomingMessage } from "http";
|
||||
import { Redis } from "ioredis";
|
||||
import Joi from "joi";
|
||||
@@ -229,7 +230,7 @@ export const signTokenString = async (
|
||||
secret,
|
||||
{
|
||||
jwtid: uuid(),
|
||||
expiresIn: "1 day",
|
||||
expiresIn: DEFAULT_SESSION_LENGTH,
|
||||
...options,
|
||||
issuer: tenant.id,
|
||||
subject: user.id,
|
||||
|
||||
@@ -242,6 +242,9 @@ configure-auth-oidc-tokenURL = Token URL
|
||||
configure-auth-oidc-jwksURI = JWKS URI
|
||||
configure-auth-oidc-useLoginOn = Use OpenID Connect login on
|
||||
|
||||
configure-auth-settings = Session settings
|
||||
configure-auth-settings-session-duration-label = Session duration
|
||||
|
||||
### Moderation
|
||||
|
||||
### Recent Comment History
|
||||
@@ -768,6 +771,10 @@ configure-account-features-delete-account = Delete their account
|
||||
configure-account-features-delete-account-details =
|
||||
Removes all of their comment data, username, and email address from the site and the database.
|
||||
|
||||
configure-account-features-delete-account-fieldDescriptions =
|
||||
Removes all of their comment data, username, and email
|
||||
address from the site and the database.
|
||||
|
||||
configure-advanced-stories = Story creation
|
||||
configure-advanced-stories-explanation = Advanced settings for how stories are created within Coral.
|
||||
configure-advanced-stories-lazy = Lazy story creation
|
||||
|
||||
Reference in New Issue
Block a user