Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c1c7e2192 | ||
|
|
b8d262f745 | ||
|
|
a1fd694248 | ||
|
|
634d6ceab1 |
@@ -15,13 +15,13 @@ type: application
|
|||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# This is the chart version. This version number should be incremented each time you make changes
|
||||||
# to the chart and its templates, including the app version.
|
# to the chart and its templates, including the app version.
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||||
version: 0.1.15
|
version: 0.1.17
|
||||||
|
|
||||||
# This is the version number of the application being deployed. This version number should be
|
# This is the version number of the application being deployed. This version number should be
|
||||||
# incremented each time you make changes to the application. Versions are not expected to
|
# incremented each time you make changes to the application. Versions are not expected to
|
||||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||||
# It is recommended to use it with quotes.
|
# It is recommended to use it with quotes.
|
||||||
appVersion: "1.15.4"
|
appVersion: "1.15.6"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
- alias: postgresql
|
- alias: postgresql
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
REACT_APP_VERSION=1.15.4
|
REACT_APP_VERSION=1.15.6
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ const Login = React.memo(
|
|||||||
isSubmittingUsingOidc,
|
isSubmittingUsingOidc,
|
||||||
error,
|
error,
|
||||||
withOidc,
|
withOidc,
|
||||||
|
isOidcEnforced,
|
||||||
onAuthenticate,
|
onAuthenticate,
|
||||||
onAuthenticateUsingOidc,
|
onAuthenticateUsingOidc,
|
||||||
onMessageDismiss,
|
onMessageDismiss,
|
||||||
@@ -107,8 +108,10 @@ const Login = React.memo(
|
|||||||
}, [onAuthenticate, data]);
|
}, [onAuthenticate, data]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!isOidcEnforced) {
|
||||||
emailOrUsernameField.current.focus();
|
emailOrUsernameField.current.focus();
|
||||||
}, []);
|
}
|
||||||
|
}, [isOidcEnforced]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (wasSubmitting && !isSubmitting && error) {
|
if (wasSubmitting && !isSubmitting && error) {
|
||||||
@@ -159,6 +162,7 @@ const Login = React.memo(
|
|||||||
onDismiss={onMessageDismiss}
|
onDismiss={onMessageDismiss}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{!isOidcEnforced && (
|
||||||
<Form size="large" onSubmit={handleSubmit}>
|
<Form size="large" onSubmit={handleSubmit}>
|
||||||
<div className={styles.inputWrapper}>
|
<div className={styles.inputWrapper}>
|
||||||
<div className={styles.inputLabel}>{t('common.emailOrUsername')}</div>
|
<div className={styles.inputLabel}>{t('common.emailOrUsername')}</div>
|
||||||
@@ -195,15 +199,20 @@ const Login = React.memo(
|
|||||||
disabled={isSubmitting || isSubmittingUsingOidc}
|
disabled={isSubmitting || isSubmittingUsingOidc}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
|
)}
|
||||||
{withOidc && (
|
{withOidc && (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
fluid={isOidcEnforced}
|
||||||
|
primary={isOidcEnforced}
|
||||||
|
size={isOidcEnforced ? 'large' : undefined}
|
||||||
|
icon={isOidcEnforced ? 'right arrow' : undefined}
|
||||||
|
labelPosition={isOidcEnforced ? 'right' : undefined}
|
||||||
|
content={t('action.logInWithSSO')}
|
||||||
loading={isSubmittingUsingOidc}
|
loading={isSubmittingUsingOidc}
|
||||||
disabled={isSubmitting || isSubmittingUsingOidc}
|
disabled={isSubmitting || isSubmittingUsingOidc}
|
||||||
onClick={onAuthenticateUsingOidc}
|
onClick={onAuthenticateUsingOidc}
|
||||||
>
|
/>
|
||||||
{t('action.logInWithSSO')}
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -242,6 +251,7 @@ Login.propTypes = {
|
|||||||
isSubmittingUsingOidc: PropTypes.bool.isRequired,
|
isSubmittingUsingOidc: PropTypes.bool.isRequired,
|
||||||
error: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
error: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||||
withOidc: PropTypes.bool.isRequired,
|
withOidc: PropTypes.bool.isRequired,
|
||||||
|
isOidcEnforced: PropTypes.bool.isRequired,
|
||||||
onAuthenticate: PropTypes.func.isRequired,
|
onAuthenticate: PropTypes.func.isRequired,
|
||||||
onAuthenticateUsingOidc: PropTypes.func.isRequired,
|
onAuthenticateUsingOidc: PropTypes.func.isRequired,
|
||||||
onMessageDismiss: PropTypes.func.isRequired,
|
onMessageDismiss: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const AccountPane = React.memo(
|
|||||||
organization,
|
organization,
|
||||||
language,
|
language,
|
||||||
isLocked,
|
isLocked,
|
||||||
|
isUsernameLocked,
|
||||||
isAvatarUpdating,
|
isAvatarUpdating,
|
||||||
usernameUpdateForm,
|
usernameUpdateForm,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
@@ -104,7 +105,7 @@ const AccountPane = React.memo(
|
|||||||
value={language || 'auto'}
|
value={language || 'auto'}
|
||||||
onChange={handleLanguageChange}
|
onChange={handleLanguageChange}
|
||||||
/>
|
/>
|
||||||
{!isLocked && (
|
{(!isLocked || !isUsernameLocked) && (
|
||||||
<>
|
<>
|
||||||
<Divider horizontal section>
|
<Divider horizontal section>
|
||||||
<Header as="h4">
|
<Header as="h4">
|
||||||
@@ -113,13 +114,14 @@ const AccountPane = React.memo(
|
|||||||
})}
|
})}
|
||||||
</Header>
|
</Header>
|
||||||
</Divider>
|
</Divider>
|
||||||
|
{!isUsernameLocked && (
|
||||||
<div className={styles.action}>
|
<div className={styles.action}>
|
||||||
<UserUsernameEditPopup
|
<UserUsernameEditPopup
|
||||||
usePasswordConfirmation
|
|
||||||
defaultData={usernameUpdateForm.data}
|
defaultData={usernameUpdateForm.data}
|
||||||
username={username}
|
username={username}
|
||||||
isSubmitting={usernameUpdateForm.isSubmitting}
|
isSubmitting={usernameUpdateForm.isSubmitting}
|
||||||
error={usernameUpdateForm.error}
|
error={usernameUpdateForm.error}
|
||||||
|
usePasswordConfirmation={!isLocked} // FIXME: hack
|
||||||
onUpdate={onUsernameUpdate}
|
onUpdate={onUsernameUpdate}
|
||||||
onMessageDismiss={onUsernameUpdateMessageDismiss}
|
onMessageDismiss={onUsernameUpdateMessageDismiss}
|
||||||
>
|
>
|
||||||
@@ -130,6 +132,9 @@ const AccountPane = React.memo(
|
|||||||
</Button>
|
</Button>
|
||||||
</UserUsernameEditPopup>
|
</UserUsernameEditPopup>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{!isLocked && (
|
||||||
|
<>
|
||||||
<div className={styles.action}>
|
<div className={styles.action}>
|
||||||
<UserEmailEditPopup
|
<UserEmailEditPopup
|
||||||
usePasswordConfirmation
|
usePasswordConfirmation
|
||||||
@@ -165,6 +170,8 @@ const AccountPane = React.memo(
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Tab.Pane>
|
</Tab.Pane>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -179,6 +186,7 @@ AccountPane.propTypes = {
|
|||||||
organization: PropTypes.string,
|
organization: PropTypes.string,
|
||||||
language: PropTypes.string,
|
language: PropTypes.string,
|
||||||
isLocked: PropTypes.bool.isRequired,
|
isLocked: PropTypes.bool.isRequired,
|
||||||
|
isUsernameLocked: PropTypes.bool.isRequired,
|
||||||
isAvatarUpdating: PropTypes.bool.isRequired,
|
isAvatarUpdating: PropTypes.bool.isRequired,
|
||||||
/* eslint-disable react/forbid-prop-types */
|
/* eslint-disable react/forbid-prop-types */
|
||||||
usernameUpdateForm: PropTypes.object.isRequired,
|
usernameUpdateForm: PropTypes.object.isRequired,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const UserSettingsModal = React.memo(
|
|||||||
language,
|
language,
|
||||||
subscribeToOwnCards,
|
subscribeToOwnCards,
|
||||||
isLocked,
|
isLocked,
|
||||||
|
isUsernameLocked,
|
||||||
isAvatarUpdating,
|
isAvatarUpdating,
|
||||||
usernameUpdateForm,
|
usernameUpdateForm,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
@@ -50,6 +51,7 @@ const UserSettingsModal = React.memo(
|
|||||||
organization={organization}
|
organization={organization}
|
||||||
language={language}
|
language={language}
|
||||||
isLocked={isLocked}
|
isLocked={isLocked}
|
||||||
|
isUsernameLocked={isUsernameLocked}
|
||||||
isAvatarUpdating={isAvatarUpdating}
|
isAvatarUpdating={isAvatarUpdating}
|
||||||
usernameUpdateForm={usernameUpdateForm}
|
usernameUpdateForm={usernameUpdateForm}
|
||||||
emailUpdateForm={emailUpdateForm}
|
emailUpdateForm={emailUpdateForm}
|
||||||
@@ -108,6 +110,7 @@ UserSettingsModal.propTypes = {
|
|||||||
language: PropTypes.string,
|
language: PropTypes.string,
|
||||||
subscribeToOwnCards: PropTypes.bool.isRequired,
|
subscribeToOwnCards: PropTypes.bool.isRequired,
|
||||||
isLocked: PropTypes.bool.isRequired,
|
isLocked: PropTypes.bool.isRequired,
|
||||||
|
isUsernameLocked: PropTypes.bool.isRequired,
|
||||||
isAvatarUpdating: PropTypes.bool.isRequired,
|
isAvatarUpdating: PropTypes.bool.isRequired,
|
||||||
/* eslint-disable react/forbid-prop-types */
|
/* eslint-disable react/forbid-prop-types */
|
||||||
usernameUpdateForm: PropTypes.object.isRequired,
|
usernameUpdateForm: PropTypes.object.isRequired,
|
||||||
|
|||||||
@@ -136,13 +136,15 @@ const ActionsStep = React.memo(
|
|||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
{!user.isLocked && (
|
{!user.isUsernameLocked && (
|
||||||
<>
|
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleEditUsernameClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleEditUsernameClick}>
|
||||||
{t('action.editUsername', {
|
{t('action.editUsername', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{!user.isLocked && (
|
||||||
|
<>
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleEditEmailClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleEditEmailClick}>
|
||||||
{t('action.editEmail', {
|
{t('action.editEmail', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const Item = React.memo(
|
|||||||
isAdmin,
|
isAdmin,
|
||||||
isLocked,
|
isLocked,
|
||||||
isRoleLocked,
|
isRoleLocked,
|
||||||
|
isUsernameLocked,
|
||||||
isDeletionLocked,
|
isDeletionLocked,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
passwordUpdateForm,
|
passwordUpdateForm,
|
||||||
@@ -61,6 +62,7 @@ const Item = React.memo(
|
|||||||
phone,
|
phone,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
isLocked,
|
isLocked,
|
||||||
|
isUsernameLocked,
|
||||||
isDeletionLocked,
|
isDeletionLocked,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
passwordUpdateForm,
|
passwordUpdateForm,
|
||||||
@@ -95,6 +97,7 @@ Item.propTypes = {
|
|||||||
isAdmin: PropTypes.bool.isRequired,
|
isAdmin: PropTypes.bool.isRequired,
|
||||||
isLocked: PropTypes.bool.isRequired,
|
isLocked: PropTypes.bool.isRequired,
|
||||||
isRoleLocked: PropTypes.bool.isRequired,
|
isRoleLocked: PropTypes.bool.isRequired,
|
||||||
|
isUsernameLocked: PropTypes.bool.isRequired,
|
||||||
isDeletionLocked: PropTypes.bool.isRequired,
|
isDeletionLocked: PropTypes.bool.isRequired,
|
||||||
/* eslint-disable react/forbid-prop-types */
|
/* eslint-disable react/forbid-prop-types */
|
||||||
emailUpdateForm: PropTypes.object.isRequired,
|
emailUpdateForm: PropTypes.object.isRequired,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import Item from './Item';
|
|||||||
const UsersModal = React.memo(
|
const UsersModal = React.memo(
|
||||||
({
|
({
|
||||||
items,
|
items,
|
||||||
|
canAdd,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onUsernameUpdate,
|
onUsernameUpdate,
|
||||||
onUsernameUpdateMessageDismiss,
|
onUsernameUpdateMessageDismiss,
|
||||||
@@ -112,6 +113,7 @@ const UsersModal = React.memo(
|
|||||||
isAdmin={item.isAdmin}
|
isAdmin={item.isAdmin}
|
||||||
isLocked={item.isLocked}
|
isLocked={item.isLocked}
|
||||||
isRoleLocked={item.isRoleLocked}
|
isRoleLocked={item.isRoleLocked}
|
||||||
|
isUsernameLocked={item.isUsernameLocked}
|
||||||
isDeletionLocked={item.isDeletionLocked}
|
isDeletionLocked={item.isDeletionLocked}
|
||||||
emailUpdateForm={item.emailUpdateForm}
|
emailUpdateForm={item.emailUpdateForm}
|
||||||
passwordUpdateForm={item.passwordUpdateForm}
|
passwordUpdateForm={item.passwordUpdateForm}
|
||||||
@@ -129,11 +131,13 @@ const UsersModal = React.memo(
|
|||||||
</Table.Body>
|
</Table.Body>
|
||||||
</Table>
|
</Table>
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
|
{canAdd && (
|
||||||
<Modal.Actions>
|
<Modal.Actions>
|
||||||
<UserAddPopupContainer>
|
<UserAddPopupContainer>
|
||||||
<Button positive content={t('action.addUser')} />
|
<Button positive content={t('action.addUser')} />
|
||||||
</UserAddPopupContainer>
|
</UserAddPopupContainer>
|
||||||
</Modal.Actions>
|
</Modal.Actions>
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -141,6 +145,7 @@ const UsersModal = React.memo(
|
|||||||
|
|
||||||
UsersModal.propTypes = {
|
UsersModal.propTypes = {
|
||||||
items: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
items: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
|
canAdd: PropTypes.bool.isRequired,
|
||||||
onUpdate: PropTypes.func.isRequired,
|
onUpdate: PropTypes.func.isRequired,
|
||||||
onUsernameUpdate: PropTypes.func.isRequired,
|
onUsernameUpdate: PropTypes.func.isRequired,
|
||||||
onUsernameUpdateMessageDismiss: PropTypes.func.isRequired,
|
onUsernameUpdateMessageDismiss: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const mapStateToProps = (state) => {
|
|||||||
isSubmittingUsingOidc,
|
isSubmittingUsingOidc,
|
||||||
error,
|
error,
|
||||||
withOidc: !!oidcConfig,
|
withOidc: !!oidcConfig,
|
||||||
|
isOidcEnforced: oidcConfig && oidcConfig.isEnforced,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const mapStateToProps = (state) => {
|
|||||||
language,
|
language,
|
||||||
subscribeToOwnCards,
|
subscribeToOwnCards,
|
||||||
isLocked,
|
isLocked,
|
||||||
|
isUsernameLocked,
|
||||||
isAvatarUpdating,
|
isAvatarUpdating,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
passwordUpdateForm,
|
passwordUpdateForm,
|
||||||
@@ -32,6 +33,7 @@ const mapStateToProps = (state) => {
|
|||||||
language,
|
language,
|
||||||
subscribeToOwnCards,
|
subscribeToOwnCards,
|
||||||
isLocked,
|
isLocked,
|
||||||
|
isUsernameLocked,
|
||||||
isAvatarUpdating,
|
isAvatarUpdating,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
passwordUpdateForm,
|
passwordUpdateForm,
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import entryActions from '../entry-actions';
|
|||||||
import UsersModal from '../components/UsersModal';
|
import UsersModal from '../components/UsersModal';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
|
const oidcConfig = selectors.selectOidcConfig(state);
|
||||||
const users = selectors.selectUsersExceptCurrent(state);
|
const users = selectors.selectUsersExceptCurrent(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: users,
|
items: users,
|
||||||
|
canAdd: !oidcConfig || !oidcConfig.isEnforced,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export default class extends BaseModel {
|
|||||||
isAdmin: attr(),
|
isAdmin: attr(),
|
||||||
isLocked: attr(),
|
isLocked: attr(),
|
||||||
isRoleLocked: attr(),
|
isRoleLocked: attr(),
|
||||||
|
isUsernameLocked: attr(),
|
||||||
isDeletionLocked: attr(),
|
isDeletionLocked: attr(),
|
||||||
deletedAt: attr(),
|
deletedAt: attr(),
|
||||||
createdAt: attr({
|
createdAt: attr({
|
||||||
|
|||||||
@@ -44,8 +44,13 @@ services:
|
|||||||
# - OIDC_CLIENT_SECRET=
|
# - OIDC_CLIENT_SECRET=
|
||||||
# - OIDC_SCOPES=openid email profile
|
# - OIDC_SCOPES=openid email profile
|
||||||
# - OIDC_ADMIN_ROLES=admin
|
# - OIDC_ADMIN_ROLES=admin
|
||||||
|
# - OIDC_EMAIL_ATTRIBUTE=email
|
||||||
|
# - OIDC_NAME_ATTRIBUTE=name
|
||||||
|
# - OIDC_USERNAME_ATTRIBUTE=preferred_username
|
||||||
# - OIDC_ROLES_ATTRIBUTE=groups
|
# - OIDC_ROLES_ATTRIBUTE=groups
|
||||||
|
# - OIDC_IGNORE_USERNAME=true
|
||||||
# - OIDC_IGNORE_ROLES=true
|
# - OIDC_IGNORE_ROLES=true
|
||||||
|
# - OIDC_ENFORCED=true
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "planka",
|
"name": "planka",
|
||||||
"version": "1.15.4",
|
"version": "1.15.6",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "planka",
|
"name": "planka",
|
||||||
"version": "1.15.4",
|
"version": "1.15.6",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "planka",
|
"name": "planka",
|
||||||
"version": "1.15.4",
|
"version": "1.15.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://plankanban.github.io/planka",
|
"homepage": "https://plankanban.github.io/planka",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -27,8 +27,13 @@ SECRET_KEY=notsecretkey
|
|||||||
# OIDC_CLIENT_SECRET=
|
# OIDC_CLIENT_SECRET=
|
||||||
# OIDC_SCOPES=openid email profile
|
# OIDC_SCOPES=openid email profile
|
||||||
# OIDC_ADMIN_ROLES=admin
|
# OIDC_ADMIN_ROLES=admin
|
||||||
|
# OIDC_EMAIL_ATTRIBUTE=email
|
||||||
|
# OIDC_NAME_ATTRIBUTE=name
|
||||||
|
# OIDC_USERNAME_ATTRIBUTE=preferred_username
|
||||||
# OIDC_ROLES_ATTRIBUTE=groups
|
# OIDC_ROLES_ATTRIBUTE=groups
|
||||||
|
# OIDC_IGNORE_USERNAME=true
|
||||||
# OIDC_IGNORE_ROLES=true
|
# OIDC_IGNORE_ROLES=true
|
||||||
|
# OIDC_ENFORCED=true
|
||||||
|
|
||||||
## Do not edit this
|
## Do not edit this
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,11 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async fn(inputs) {
|
async fn(inputs) {
|
||||||
const remoteAddress = getRemoteAddress(this.req);
|
if (sails.config.custom.oidcEnforced) {
|
||||||
|
throw Errors.USE_SINGLE_SIGN_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteAddress = getRemoteAddress(this.req);
|
||||||
const user = await sails.helpers.users.getOneByEmailOrUsername(inputs.emailOrUsername);
|
const user = await sails.helpers.users.getOneByEmailOrUsername(inputs.emailOrUsername);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ module.exports = {
|
|||||||
response_mode: 'fragment',
|
response_mode: 'fragment',
|
||||||
}),
|
}),
|
||||||
endSessionUrl: oidcClient.issuer.end_session_endpoint ? oidcClient.endSessionUrl({}) : null,
|
endSessionUrl: oidcClient.issuer.end_session_endpoint ? oidcClient.endSessionUrl({}) : null,
|
||||||
|
isEnforced: sails.config.custom.oidcEnforced,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
const zxcvbn = require('zxcvbn');
|
const zxcvbn = require('zxcvbn');
|
||||||
|
|
||||||
const Errors = {
|
const Errors = {
|
||||||
|
NOT_ENOUGH_RIGHTS: {
|
||||||
|
notEnoughRights: 'Not enough rights',
|
||||||
|
},
|
||||||
EMAIL_ALREADY_IN_USE: {
|
EMAIL_ALREADY_IN_USE: {
|
||||||
emailAlreadyInUse: 'Email already in use',
|
emailAlreadyInUse: 'Email already in use',
|
||||||
},
|
},
|
||||||
@@ -56,6 +59,9 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
exits: {
|
exits: {
|
||||||
|
notEnoughRights: {
|
||||||
|
responseType: 'forbidden',
|
||||||
|
},
|
||||||
emailAlreadyInUse: {
|
emailAlreadyInUse: {
|
||||||
responseType: 'conflict',
|
responseType: 'conflict',
|
||||||
},
|
},
|
||||||
@@ -65,6 +71,10 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async fn(inputs) {
|
async fn(inputs) {
|
||||||
|
if (sails.config.custom.oidcEnforced) {
|
||||||
|
throw Errors.NOT_ENOUGH_RIGHTS;
|
||||||
|
}
|
||||||
|
|
||||||
const values = _.pick(inputs, [
|
const values = _.pick(inputs, [
|
||||||
'email',
|
'email',
|
||||||
'password',
|
'password',
|
||||||
|
|||||||
@@ -53,11 +53,7 @@ module.exports = {
|
|||||||
async fn(inputs) {
|
async fn(inputs) {
|
||||||
const { currentUser } = this.req;
|
const { currentUser } = this.req;
|
||||||
|
|
||||||
if (inputs.id === currentUser.id) {
|
if (inputs.id !== currentUser.id && !currentUser.isAdmin) {
|
||||||
if (!inputs.currentPassword) {
|
|
||||||
throw Errors.INVALID_CURRENT_PASSWORD;
|
|
||||||
}
|
|
||||||
} else if (!currentUser.isAdmin) {
|
|
||||||
throw Errors.USER_NOT_FOUND; // Forbidden
|
throw Errors.USER_NOT_FOUND; // Forbidden
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,16 +63,19 @@ module.exports = {
|
|||||||
throw Errors.USER_NOT_FOUND;
|
throw Errors.USER_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.email === sails.config.custom.defaultAdminEmail || user.isSso) {
|
if (user.email === sails.config.custom.defaultAdminEmail) {
|
||||||
throw Errors.NOT_ENOUGH_RIGHTS;
|
throw Errors.NOT_ENOUGH_RIGHTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (user.isSso) {
|
||||||
inputs.id === currentUser.id &&
|
if (!sails.config.custom.oidcIgnoreUsername) {
|
||||||
!bcrypt.compareSync(inputs.currentPassword, user.password)
|
throw Errors.NOT_ENOUGH_RIGHTS;
|
||||||
) {
|
}
|
||||||
|
} else if (inputs.id === currentUser.id) {
|
||||||
|
if (!inputs.currentPassword || !bcrypt.compareSync(inputs.currentPassword, user.password)) {
|
||||||
throw Errors.INVALID_CURRENT_PASSWORD;
|
throw Errors.INVALID_CURRENT_PASSWORD;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const values = _.pick(inputs, ['username']);
|
const values = _.pick(inputs, ['username']);
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,10 @@ module.exports = {
|
|||||||
throw 'invalidCodeOrNonce';
|
throw 'invalidCodeOrNonce';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userInfo.email || !userInfo.name) {
|
if (
|
||||||
|
!userInfo[sails.config.custom.oidcEmailAttribute] ||
|
||||||
|
!userInfo[sails.config.custom.oidcNameAttribute]
|
||||||
|
) {
|
||||||
throw 'missingValues';
|
throw 'missingValues';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +59,14 @@ module.exports = {
|
|||||||
|
|
||||||
const values = {
|
const values = {
|
||||||
isAdmin,
|
isAdmin,
|
||||||
email: userInfo.email,
|
email: userInfo[sails.config.custom.oidcEmailAttribute],
|
||||||
isSso: true,
|
isSso: true,
|
||||||
name: userInfo.name,
|
name: userInfo[sails.config.custom.oidcNameAttribute],
|
||||||
username: userInfo.preferred_username,
|
|
||||||
subscribeToOwnCards: false,
|
subscribeToOwnCards: false,
|
||||||
};
|
};
|
||||||
|
if (!sails.config.custom.oidcIgnoreUsername) {
|
||||||
|
values.username = userInfo[sails.config.custom.oidcUsernameAttribute];
|
||||||
|
}
|
||||||
|
|
||||||
let user;
|
let user;
|
||||||
// This whole block technically needs to be executed in a transaction
|
// This whole block technically needs to be executed in a transaction
|
||||||
@@ -95,7 +100,10 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateFieldKeys = ['email', 'isSso', 'name', 'username'];
|
const updateFieldKeys = ['email', 'isSso', 'name'];
|
||||||
|
if (!sails.config.custom.oidcIgnoreUsername) {
|
||||||
|
updateFieldKeys.push('username');
|
||||||
|
}
|
||||||
if (!sails.config.custom.oidcIgnoreRoles) {
|
if (!sails.config.custom.oidcIgnoreRoles) {
|
||||||
updateFieldKeys.push('isAdmin');
|
updateFieldKeys.push('isAdmin');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ module.exports = {
|
|||||||
..._.omit(this, ['password', 'isSso', 'avatar', 'passwordChangedAt']),
|
..._.omit(this, ['password', 'isSso', 'avatar', 'passwordChangedAt']),
|
||||||
isLocked: this.isSso || isDefaultAdmin,
|
isLocked: this.isSso || isDefaultAdmin,
|
||||||
isRoleLocked: (this.isSso && !sails.config.custom.oidcIgnoreRoles) || isDefaultAdmin,
|
isRoleLocked: (this.isSso && !sails.config.custom.oidcIgnoreRoles) || isDefaultAdmin,
|
||||||
|
isUsernameLocked: (this.isSso && !sails.config.custom.oidcIgnoreUsername) || isDefaultAdmin,
|
||||||
isDeletionLocked: isDefaultAdmin,
|
isDeletionLocked: isDefaultAdmin,
|
||||||
avatarUrl:
|
avatarUrl:
|
||||||
this.avatar &&
|
this.avatar &&
|
||||||
|
|||||||
@@ -38,8 +38,13 @@ module.exports.custom = {
|
|||||||
oidcClientSecret: process.env.OIDC_CLIENT_SECRET,
|
oidcClientSecret: process.env.OIDC_CLIENT_SECRET,
|
||||||
oidcScopes: process.env.OIDC_SCOPES || 'openid email profile',
|
oidcScopes: process.env.OIDC_SCOPES || 'openid email profile',
|
||||||
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [],
|
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [],
|
||||||
|
oidcEmailAttribute: process.env.OIDC_EMAIL_ATTRIBUTE || 'email',
|
||||||
|
oidcNameAttribute: process.env.OIDC_NAME_ATTRIBUTE || 'name',
|
||||||
|
oidcUsernameAttribute: process.env.OIDC_USERNAME_ATTRIBUTE || 'preferred_username',
|
||||||
oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE || 'groups',
|
oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE || 'groups',
|
||||||
|
oidcIgnoreUsername: process.env.OIDC_IGNORE_USERNAME === 'true',
|
||||||
oidcIgnoreRoles: process.env.OIDC_IGNORE_ROLES === 'true',
|
oidcIgnoreRoles: process.env.OIDC_IGNORE_ROLES === 'true',
|
||||||
|
oidcEnforced: process.env.OIDC_ENFORCED === 'true',
|
||||||
|
|
||||||
// TODO: move client base url to environment variable?
|
// TODO: move client base url to environment variable?
|
||||||
oidcRedirectUri: `${
|
oidcRedirectUri: `${
|
||||||
|
|||||||
Reference in New Issue
Block a user