From 1b30468366c9887da80dc5ac6ad6fcca5c0dc060 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 22 Aug 2021 10:52:02 -0500 Subject: [PATCH] Auth: move some actions into security.js --- app/soapbox/actions/apps.js | 8 ++ app/soapbox/actions/auth.js | 95 ++----------------- app/soapbox/actions/oauth.js | 8 ++ app/soapbox/actions/security.js | 93 ++++++++++++++++++ .../auth_login/components/password_reset.js | 2 +- app/soapbox/features/security/index.js | 2 +- 6 files changed, 121 insertions(+), 87 deletions(-) diff --git a/app/soapbox/actions/apps.js b/app/soapbox/actions/apps.js index 11262427b..b0824d384 100644 --- a/app/soapbox/actions/apps.js +++ b/app/soapbox/actions/apps.js @@ -1,3 +1,11 @@ +/** + * Apps: manage OAuth applications. + * Particularly useful for auth. + * https://docs.joinmastodon.org/methods/apps/ + * @module soapbox/actions/apps + * @see module:soapbox/actions/auth + */ + import { baseClient } from '../api'; export const APP_CREATE_REQUEST = 'APP_CREATE_REQUEST'; diff --git a/app/soapbox/actions/auth.js b/app/soapbox/actions/auth.js index b4dd3d4dc..2918d41a2 100644 --- a/app/soapbox/actions/auth.js +++ b/app/soapbox/actions/auth.js @@ -1,3 +1,12 @@ +/** + * Auth: login & registration workflow. + * This file contains abstractions over auth concepts. + * @module soapbox/actions/auth + * @see module:soapbox/actions/apps + * @see module:soapbox/actions/oauth + * @see module:soapbox/actions/security + */ + import { defineMessages } from 'react-intl'; import api, { baseClient } from '../api'; import { importFetchedAccount } from './importer'; @@ -20,23 +29,7 @@ export const VERIFY_CREDENTIALS_REQUEST = 'VERIFY_CREDENTIALS_REQUEST'; export const VERIFY_CREDENTIALS_SUCCESS = 'VERIFY_CREDENTIALS_SUCCESS'; export const VERIFY_CREDENTIALS_FAIL = 'VERIFY_CREDENTIALS_FAIL'; -export const RESET_PASSWORD_REQUEST = 'RESET_PASSWORD_REQUEST'; -export const RESET_PASSWORD_SUCCESS = 'RESET_PASSWORD_SUCCESS'; -export const RESET_PASSWORD_FAIL = 'RESET_PASSWORD_FAIL'; - -export const CHANGE_EMAIL_REQUEST = 'CHANGE_EMAIL_REQUEST'; -export const CHANGE_EMAIL_SUCCESS = 'CHANGE_EMAIL_SUCCESS'; -export const CHANGE_EMAIL_FAIL = 'CHANGE_EMAIL_FAIL'; - -export const DELETE_ACCOUNT_REQUEST = 'DELETE_ACCOUNT_REQUEST'; -export const DELETE_ACCOUNT_SUCCESS = 'DELETE_ACCOUNT_SUCCESS'; -export const DELETE_ACCOUNT_FAIL = 'DELETE_ACCOUNT_FAIL'; - -export const CHANGE_PASSWORD_REQUEST = 'CHANGE_PASSWORD_REQUEST'; -export const CHANGE_PASSWORD_SUCCESS = 'CHANGE_PASSWORD_SUCCESS'; -export const CHANGE_PASSWORD_FAIL = 'CHANGE_PASSWORD_FAIL'; - -const messages = defineMessages({ +export const messages = defineMessages({ loggedOut: { id: 'auth.logged_out', defaultMessage: 'Logged out.' }, invalidCredentials: { id: 'auth.invalid_credentials', defaultMessage: 'Wrong username or password' }, }); @@ -231,74 +224,6 @@ export function fetchCaptcha() { }; } -export function resetPassword(nickNameOrEmail) { - return (dispatch, getState) => { - dispatch({ type: RESET_PASSWORD_REQUEST }); - const params = - nickNameOrEmail.includes('@') - ? { email: nickNameOrEmail } - : { nickname: nickNameOrEmail }; - return api(getState).post('/auth/password', params).then(() => { - dispatch({ type: RESET_PASSWORD_SUCCESS }); - }).catch(error => { - dispatch({ type: RESET_PASSWORD_FAIL, error }); - throw error; - }); - }; -} - -export function changeEmail(email, password) { - return (dispatch, getState) => { - dispatch({ type: CHANGE_EMAIL_REQUEST, email }); - return api(getState).post('/api/pleroma/change_email', { - email, - password, - }).then(response => { - if (response.data.error) throw response.data.error; // This endpoint returns HTTP 200 even on failure - dispatch({ type: CHANGE_EMAIL_SUCCESS, email, response }); - }).catch(error => { - dispatch({ type: CHANGE_EMAIL_FAIL, email, error, skipAlert: true }); - throw error; - }); - }; -} - -export function deleteAccount(intl, password) { - return (dispatch, getState) => { - const account = getLoggedInAccount(getState()); - - dispatch({ type: DELETE_ACCOUNT_REQUEST }); - return api(getState).post('/api/pleroma/delete_account', { - password, - }).then(response => { - if (response.data.error) throw response.data.error; // This endpoint returns HTTP 200 even on failure - dispatch({ type: DELETE_ACCOUNT_SUCCESS, response }); - dispatch({ type: AUTH_LOGGED_OUT, account }); - dispatch(snackbar.success(intl.formatMessage(messages.loggedOut))); - }).catch(error => { - dispatch({ type: DELETE_ACCOUNT_FAIL, error, skipAlert: true }); - throw error; - }); - }; -} - -export function changePassword(oldPassword, newPassword, confirmation) { - return (dispatch, getState) => { - dispatch({ type: CHANGE_PASSWORD_REQUEST }); - return api(getState).post('/api/pleroma/change_password', { - password: oldPassword, - new_password: newPassword, - new_password_confirmation: confirmation, - }).then(response => { - if (response.data.error) throw response.data.error; // This endpoint returns HTTP 200 even on failure - dispatch({ type: CHANGE_PASSWORD_SUCCESS, response }); - }).catch(error => { - dispatch({ type: CHANGE_PASSWORD_FAIL, error, skipAlert: true }); - throw error; - }); - }; -} - export function authLoggedIn(token) { return { type: AUTH_LOGGED_IN, diff --git a/app/soapbox/actions/oauth.js b/app/soapbox/actions/oauth.js index a3f83960f..e3ee95f3d 100644 --- a/app/soapbox/actions/oauth.js +++ b/app/soapbox/actions/oauth.js @@ -1,3 +1,11 @@ +/** + * OAuth: create and revoke tokens. + * Tokens can be used by users and apps. + * https://docs.joinmastodon.org/methods/apps/oauth/ + * @module soapbox/actions/oauth + * @see module:soapbox/actions/auth + */ + import { baseClient } from '../api'; export const OAUTH_TOKEN_CREATE_REQUEST = 'OAUTH_TOKEN_CREATE_REQUEST'; diff --git a/app/soapbox/actions/security.js b/app/soapbox/actions/security.js index abb3f6972..e5e15b6f5 100644 --- a/app/soapbox/actions/security.js +++ b/app/soapbox/actions/security.js @@ -1,4 +1,13 @@ +/** + * Security: Pleroma-specific account management features. + * @module soapbox/actions/security + * @see module:soapbox/actions/auth + */ + import api from '../api'; +import { getLoggedInAccount } from 'soapbox/utils/auth'; +import snackbar from 'soapbox/actions/snackbar'; +import { AUTH_LOGGED_OUT, messages } from './auth'; export const FETCH_TOKENS_REQUEST = 'FETCH_TOKENS_REQUEST'; export const FETCH_TOKENS_SUCCESS = 'FETCH_TOKENS_SUCCESS'; @@ -8,6 +17,22 @@ export const REVOKE_TOKEN_REQUEST = 'REVOKE_TOKEN_REQUEST'; export const REVOKE_TOKEN_SUCCESS = 'REVOKE_TOKEN_SUCCESS'; export const REVOKE_TOKEN_FAIL = 'REVOKE_TOKEN_FAIL'; +export const RESET_PASSWORD_REQUEST = 'RESET_PASSWORD_REQUEST'; +export const RESET_PASSWORD_SUCCESS = 'RESET_PASSWORD_SUCCESS'; +export const RESET_PASSWORD_FAIL = 'RESET_PASSWORD_FAIL'; + +export const CHANGE_PASSWORD_REQUEST = 'CHANGE_PASSWORD_REQUEST'; +export const CHANGE_PASSWORD_SUCCESS = 'CHANGE_PASSWORD_SUCCESS'; +export const CHANGE_PASSWORD_FAIL = 'CHANGE_PASSWORD_FAIL'; + +export const CHANGE_EMAIL_REQUEST = 'CHANGE_EMAIL_REQUEST'; +export const CHANGE_EMAIL_SUCCESS = 'CHANGE_EMAIL_SUCCESS'; +export const CHANGE_EMAIL_FAIL = 'CHANGE_EMAIL_FAIL'; + +export const DELETE_ACCOUNT_REQUEST = 'DELETE_ACCOUNT_REQUEST'; +export const DELETE_ACCOUNT_SUCCESS = 'DELETE_ACCOUNT_SUCCESS'; +export const DELETE_ACCOUNT_FAIL = 'DELETE_ACCOUNT_FAIL'; + export function fetchOAuthTokens() { return (dispatch, getState) => { dispatch({ type: FETCH_TOKENS_REQUEST }); @@ -29,3 +54,71 @@ export function revokeOAuthTokenById(id) { }); }; } + +export function changePassword(oldPassword, newPassword, confirmation) { + return (dispatch, getState) => { + dispatch({ type: CHANGE_PASSWORD_REQUEST }); + return api(getState).post('/api/pleroma/change_password', { + password: oldPassword, + new_password: newPassword, + new_password_confirmation: confirmation, + }).then(response => { + if (response.data.error) throw response.data.error; // This endpoint returns HTTP 200 even on failure + dispatch({ type: CHANGE_PASSWORD_SUCCESS, response }); + }).catch(error => { + dispatch({ type: CHANGE_PASSWORD_FAIL, error, skipAlert: true }); + throw error; + }); + }; +} + +export function resetPassword(nickNameOrEmail) { + return (dispatch, getState) => { + dispatch({ type: RESET_PASSWORD_REQUEST }); + const params = + nickNameOrEmail.includes('@') + ? { email: nickNameOrEmail } + : { nickname: nickNameOrEmail }; + return api(getState).post('/auth/password', params).then(() => { + dispatch({ type: RESET_PASSWORD_SUCCESS }); + }).catch(error => { + dispatch({ type: RESET_PASSWORD_FAIL, error }); + throw error; + }); + }; +} + +export function changeEmail(email, password) { + return (dispatch, getState) => { + dispatch({ type: CHANGE_EMAIL_REQUEST, email }); + return api(getState).post('/api/pleroma/change_email', { + email, + password, + }).then(response => { + if (response.data.error) throw response.data.error; // This endpoint returns HTTP 200 even on failure + dispatch({ type: CHANGE_EMAIL_SUCCESS, email, response }); + }).catch(error => { + dispatch({ type: CHANGE_EMAIL_FAIL, email, error, skipAlert: true }); + throw error; + }); + }; +} + +export function deleteAccount(intl, password) { + return (dispatch, getState) => { + const account = getLoggedInAccount(getState()); + + dispatch({ type: DELETE_ACCOUNT_REQUEST }); + return api(getState).post('/api/pleroma/delete_account', { + password, + }).then(response => { + if (response.data.error) throw response.data.error; // This endpoint returns HTTP 200 even on failure + dispatch({ type: DELETE_ACCOUNT_SUCCESS, response }); + dispatch({ type: AUTH_LOGGED_OUT, account }); + dispatch(snackbar.success(intl.formatMessage(messages.loggedOut))); + }).catch(error => { + dispatch({ type: DELETE_ACCOUNT_FAIL, error, skipAlert: true }); + throw error; + }); + }; +} diff --git a/app/soapbox/features/auth_login/components/password_reset.js b/app/soapbox/features/auth_login/components/password_reset.js index ba52ea646..0c2f8bb54 100644 --- a/app/soapbox/features/auth_login/components/password_reset.js +++ b/app/soapbox/features/auth_login/components/password_reset.js @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; -import { resetPassword } from 'soapbox/actions/auth'; +import { resetPassword } from 'soapbox/actions/security'; import { SimpleForm, FieldsGroup, TextInput } from 'soapbox/features/forms'; import { Redirect } from 'react-router-dom'; import snackbar from 'soapbox/actions/snackbar'; diff --git a/app/soapbox/features/security/index.js b/app/soapbox/features/security/index.js index 1783255e9..592809242 100644 --- a/app/soapbox/features/security/index.js +++ b/app/soapbox/features/security/index.js @@ -16,7 +16,7 @@ import { changeEmail, changePassword, deleteAccount, -} from 'soapbox/actions/auth'; +} from 'soapbox/actions/security'; import { fetchOAuthTokens, revokeOAuthTokenById } from 'soapbox/actions/security'; import { fetchUserMfaSettings } from '../../actions/mfa'; import snackbar from 'soapbox/actions/snackbar';