Skip to content

Commit

Permalink
Merge pull request #9 from open-craft/kaustav/port_code_drift_quince
Browse files Browse the repository at this point in the history
chore: port code drift to quince
  • Loading branch information
kaustavb12 authored Apr 15, 2024
2 parents 48122ff + 8dd8889 commit c2a6149
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 5 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ORDER_HISTORY_URL=null
REFRESH_ACCESS_TOKEN_ENDPOINT=null
SEGMENT_KEY=''
SITE_NAME=null
TPA_UNLINKED_ACCOUNT_PROVISION_URL=''
INFO_EMAIL=''
# ***** Cookies *****
USER_RETENTION_COOKIE_NAME=null
Expand Down
6 changes: 5 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ The authentication micro-frontend also requires the following additional variabl
- Name of MFE, this will be used by the API to get runtime configurations for the specific micro frontend. For a frontend repo `frontend-app-appName`, use `appName` as APP_ID.
- ``authn`` | ``''``

* - ``TPA_UNLINKED_ACCOUNT_PROVISION_URL``
- URL to redirect to when the identity provided by third-party authentication is not yet linked to a platform account. This allows for redirecting to a custom sign-up flow handled by an external service to create the linked account. An empty string (the default) disables this feature.
- ``http://example.com/signup`` | ``''``


edX-specific Environment Variables
**********************************
Expand Down Expand Up @@ -187,4 +191,4 @@ Please see `LICENSE <https://github.com/openedx/frontend-app-authn/blob/master/L
:target: https://github.com/openedx/edx-developer-docs/actions/workflows/ci.yml
:alt: Continuous Integration
.. |semantic-release| image:: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
:target: https://github.com/semantic-release/semantic-release
:target: https://github.com/semantic-release/semantic-release
5 changes: 3 additions & 2 deletions src/common-components/EnterpriseSSO.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import { LOGIN_PAGE, SUPPORTED_ICON_CLASSES } from '../data/constants';
const EnterpriseSSO = (props) => {
const { formatMessage } = useIntl();
const tpaProvider = props.provider;
const disablePublicAccountCreation = getConfig().ALLOW_PUBLIC_ACCOUNT_CREATION === false;
const hideRegistrationLink = getConfig().ALLOW_PUBLIC_ACCOUNT_CREATION === false
|| getConfig().SHOW_REGISTRATION_LINKS === false;

const handleSubmit = (e, url) => {
e.preventDefault();
Expand Down Expand Up @@ -74,7 +75,7 @@ const EnterpriseSSO = (props) => {
className="w-100"
onClick={(e) => handleClick(e)}
>
{disablePublicAccountCreation
{hideRegistrationLink
? formatMessage(messages['enterprisetpa.login.button.text.public.account.creation.disabled'])
: formatMessage(messages['enterprisetpa.login.button.text'])}
</Button>
Expand Down
2 changes: 2 additions & 0 deletions src/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const configuration = {
ENABLE_POST_REGISTRATION_RECOMMENDATIONS: process.env.ENABLE_POST_REGISTRATION_RECOMMENDATIONS || false,
MARKETING_EMAILS_OPT_IN: process.env.MARKETING_EMAILS_OPT_IN || '',
SHOW_CONFIGURABLE_EDX_FIELDS: process.env.SHOW_CONFIGURABLE_EDX_FIELDS || false,
SHOW_REGISTRATION_LINKS: process.env.SHOW_REGISTRATION_LINKS !== 'false',
// Links
ACTIVATION_EMAIL_SUPPORT_LINK: process.env.ACTIVATION_EMAIL_SUPPORT_LINK || null,
AUTHN_PROGRESSIVE_PROFILING_SUPPORT_LINK: process.env.AUTHN_PROGRESSIVE_PROFILING_SUPPORT_LINK || null,
Expand All @@ -19,6 +20,7 @@ const configuration = {
SEARCH_CATALOG_URL: process.env.SEARCH_CATALOG_URL || null,
TOS_AND_HONOR_CODE: process.env.TOS_AND_HONOR_CODE || null,
TOS_LINK: process.env.TOS_LINK || null,
TPA_UNLINKED_ACCOUNT_PROVISION_URL: process.env.TPA_UNLINKED_ACCOUNT_PROVISION_URL || null,
// Base container images
BANNER_IMAGE_LARGE: process.env.BANNER_IMAGE_LARGE || '',
BANNER_IMAGE_MEDIUM: process.env.BANNER_IMAGE_MEDIUM || '',
Expand Down
12 changes: 12 additions & 0 deletions src/login/LoginPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,18 @@ class LoginPage extends React.Component {
} = this.props;
const { currentProvider, providers, secondaryProviders } = this.props.thirdPartyAuthContext;

const unlinkedProvisionUrl = getConfig().TPA_UNLINKED_ACCOUNT_PROVISION_URL;

/**
* When currentProvider exists and we are in a login page, it is
* because the third-party authenticated account is not linked.
* See also ThirdPartyAuthAlert.jsx.
*/
if (currentProvider && unlinkedProvisionUrl) {
window.location.href = unlinkedProvisionUrl;
return null;
}

if (this.tpaHint) {
if (thirdPartyAuthApiStatus === PENDING_STATE) {
return <Skeleton height={36} />;
Expand Down
47 changes: 47 additions & 0 deletions src/login/tests/LoginPage.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Provider } from 'react-redux';
import { getConfig, mergeConfig } from '@edx/frontend-platform';
import { sendPageEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
import { render } from '@testing-library/react';
import { mount } from 'enzyme';
import { MemoryRouter } from 'react-router-dom';
import renderer from 'react-test-renderer';
Expand Down Expand Up @@ -766,4 +767,50 @@ describe('LoginPage', () => {

expect(store.dispatch).toHaveBeenCalledWith(loginRemovePasswordResetBanner());
});

it('should not redirect to provisioning URL when not configured', () => {
mergeConfig({
TPA_UNLINKED_ACCOUNT_PROVISION_URL: '',
});

store = mockStore({
...initialState,
commonComponents: {
...initialState.commonComponents,
thirdPartyAuthContext: {
...initialState.commonComponents.thirdPartyAuthContext,
currentProvider: ssoProvider.name,
},
},
});

delete window.location;
window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE) };

render(reduxWrapper(<IntlLoginPage {...props} />));
expect(window.location.href).toEqual(getConfig().BASE_URL.concat(LOGIN_PAGE));
});

it('should redirect to provisioning URL on unlinked third-party auth account', () => {
mergeConfig({
TPA_UNLINKED_ACCOUNT_PROVISION_URL: 'http://example.com/signup',
});

store = mockStore({
...initialState,
commonComponents: {
...initialState.commonComponents,
thirdPartyAuthContext: {
...initialState.commonComponents.thirdPartyAuthContext,
currentProvider: ssoProvider.name,
},
},
});

delete window.location;
window.location = { href: getConfig().BASE_URL.concat(LOGIN_PAGE) };

render(reduxWrapper(<IntlLoginPage {...props} />));
expect(window.location.href).toEqual('http://example.com/signup');
});
});
8 changes: 7 additions & 1 deletion src/logistration/Logistration.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const Logistration = (props) => {
const [key, setKey] = useState('');
const navigate = useNavigate();
const disablePublicAccountCreation = getConfig().ALLOW_PUBLIC_ACCOUNT_CREATION === false;
const hideRegistrationLink = getConfig().SHOW_REGISTRATION_LINKS === false;

useEffect(() => {
const authService = getAuthService();
Expand Down Expand Up @@ -116,7 +117,7 @@ const Logistration = (props) => {
<Tab title={tabTitle} eventKey={selectedPage === LOGIN_PAGE ? LOGIN_PAGE : REGISTER_PAGE} />
</Tabs>
)
: (!isValidTpaHint() && (
: (!isValidTpaHint() && !hideRegistrationLink && (
<Tabs defaultActiveKey={selectedPage} id="controlled-tab" onSelect={handleOnSelect}>
<Tab title={formatMessage(messages['logistration.register'])} eventKey={REGISTER_PAGE} />
<Tab title={formatMessage(messages['logistration.sign.in'])} eventKey={LOGIN_PAGE} />
Expand All @@ -126,6 +127,11 @@ const Logistration = (props) => {
<Navigate to={updatePathWithQueryParams(key)} replace />
)}
<div id="main-content" className="main-content">
{!institutionLogin && !isValidTpaHint() && hideRegistrationLink && (
<h3 className="mb-4.5">
{formatMessage(messages[selectedPage === LOGIN_PAGE ? 'logistration.sign.in' : 'logistration.register'])}
</h3>
)}
{selectedPage === LOGIN_PAGE
? <LoginPage institutionLogin={institutionLogin} handleInstitutionLogin={handleInstitutionLogin} />
: (
Expand Down
23 changes: 22 additions & 1 deletion src/logistration/Logistration.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Logistration from './Logistration';
import { clearThirdPartyAuthContextErrorMessage } from '../common-components/data/actions';
import { RenderInstitutionButton } from '../common-components/InstitutionLogistration';
import {
COMPLETE_STATE, LOGIN_PAGE,
COMPLETE_STATE, LOGIN_PAGE, REGISTER_PAGE,
} from '../data/constants';
import { backupRegistrationForm } from '../register/data/actions';

Expand Down Expand Up @@ -111,10 +111,31 @@ describe('Logistration', () => {
expect(logistration.find('#main-content').find('LoginPage').exists()).toBeTruthy();
});

it('should render login/register headings when show registration links is disabled', () => {
mergeConfig({
SHOW_REGISTRATION_LINKS: false,
});

let props = { selectedPage: LOGIN_PAGE };
let logistration = mount(reduxWrapper(<IntlLogistration {...props} />));

// verifying sign in heading
expect(logistration.find('#main-content').find('h3').text()).toEqual('Sign in');

// register page is still accessible when SHOW_REGISTRATION_LINKS is false
// but it needs to be accessed directly
props = { selectedPage: REGISTER_PAGE };
logistration = mount(reduxWrapper(<IntlLogistration {...props} />));

// verifying register heading
expect(logistration.find('#main-content').find('h3').text()).toEqual('Register');
});

it('should render only login page when public account creation is disabled', () => {
mergeConfig({
ALLOW_PUBLIC_ACCOUNT_CREATION: false,
DISABLE_ENTERPRISE_LOGIN: 'true',
SHOW_REGISTRATION_LINKS: 'true',
});

store = mockStore({
Expand Down

0 comments on commit c2a6149

Please sign in to comment.