Skip to content

Commit d8826a7

Browse files
Merge pull request #967 from ibi-group/auth-disable-qbd
Support disabling Auth0
2 parents fc75eb0 + 9fac323 commit d8826a7

17 files changed

+208
-115
lines changed

i18n/english.yml

+3
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,9 @@ components:
13511351
myAccount: My account
13521352
logout: Log out
13531353
UserHomePage:
1354+
authDisabledInfo: >-
1355+
You are running %appTitle% without user authentication enabled.
1356+
Features such as user management, account management and feed activity watching are unavailable.
13541357
createFirst: Create my first project
13551358
help:
13561359
content: 'A project is used to group GTFS feeds. For example, the feeds in a project may be in the same region or they may collectively define a planning scenario.'

i18n/german.yml

+3
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,9 @@ components:
13631363
logout: Abmelden
13641364
myAccount: Mein Konto
13651365
UserHomePage:
1366+
authDisabledInfo: >-
1367+
You are running %appTitle% without user authentication enabled.
1368+
Features such as user management, account management and feed activity watching are unavailable.
13661369
createFirst: Erstelle mein erstes Projekt
13671370
help:
13681371
content: Ein Projekt dient dazu, GTFS-Feeds zu gruppieren. Zum Beispiel können

i18n/polish.yml

+3
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,9 @@ components:
13391339
logout: Log out
13401340
myAccount: My account
13411341
UserHomePage:
1342+
authDisabledInfo: >-
1343+
You are running %appTitle% without user authentication enabled.
1344+
Features such as user management, account management and feed activity watching are unavailable.
13421345
createFirst: Create my first project
13431346
help:
13441347
content: A project is used to group GTFS feeds. For example, the feeds in a

lib/admin/components/AdminPage.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {getComponentMessages, isModuleEnabled} from '../../common/util/config'
2222
import * as projectActions from '../../manager/actions/projects'
2323
import type {DataToolsConfig, Project} from '../../types'
2424
import type {AppState, ManagerUserState, RouterProps} from '../../types/reducers'
25+
import { AUTH0_DISABLED } from '../../common/constants'
2526

2627
import OrganizationList from './OrganizationList'
2728
import ServerSettings from './ServerSettings'
@@ -51,8 +52,8 @@ class AdminPage extends React.Component<Props> {
5152
} = this.props
5253
// Set default path to user admin view.
5354
if (!activeComponent) browserHistory.push('/admin/users')
54-
// Always load a fresh list of users on load.
55-
fetchUsers()
55+
// Always load a fresh list of users on load, if auth is not disabled.
56+
if (!AUTH0_DISABLED) fetchUsers()
5657
// Always load projects to prevent interference with public feeds viewer
5758
// loading of projects.
5859
fetchProjects()
@@ -71,7 +72,8 @@ class AdminPage extends React.Component<Props> {
7172
const {activeComponent} = this.props
7273
const restricted = <p className='text-center lead'>Restricted access</p>
7374
switch (activeComponent) {
74-
case 'users': return <UserList />
75+
case 'users':
76+
return AUTH0_DISABLED ? restricted : <UserList />
7577
case 'organizations':
7678
if (!isApplicationAdmin || isModuleEnabled('enterprise')) return restricted
7779
else return <OrganizationList />

lib/admin/components/ApplicationStatus.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import BootstrapTable from 'react-bootstrap-table/lib/BootstrapTable'
99
import TableHeaderColumn from 'react-bootstrap-table/lib/TableHeaderColumn'
1010

1111
import * as adminActions from '../actions/admin'
12+
import { AUTH0_DISABLED } from '../../common/constants'
1213
import {getComponentMessages} from '../../common/util/config'
1314
import {formatTimestamp} from '../../common/util/date-time'
1415
import type {ServerJob} from '../../types'
@@ -114,14 +115,19 @@ class ApplicationStatusView extends Component<Props, {refreshTime: ?Date}> {
114115
return <TableHeaderColumn {...col} key={index} />
115116
})}
116117
</BootstrapTable>
117-
<h3>{this.messages('userLogs')}</h3>
118-
<Button
119-
bsStyle='danger'
120-
bsSize='large'
121-
block
122-
href='https://manage.auth0.com/#/logs'>
123-
<Icon type='star' /> {this.messages('viewUserLogs')}
124-
</Button>
118+
{!AUTH0_DISABLED && (
119+
<>
120+
<h3>{this.messages('userLogs')}</h3>
121+
<Button
122+
block
123+
bsStyle='danger'
124+
bsSize='large'
125+
href='https://manage.auth0.com/#/logs'
126+
>
127+
<Icon type='star' /> {this.messages('viewUserLogs')}
128+
</Button>
129+
</>
130+
)}
125131
</div>
126132
)
127133
}

lib/common/components/UserButtons.js

+13-9
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { Button, ButtonToolbar } from 'react-bootstrap'
55
import { LinkContainer } from 'react-router-bootstrap'
66
import Icon from '@conveyal/woonerf/components/icon'
77

8-
import {getComponentMessages} from '../../common/util/config'
9-
import type {ManagerUserState} from '../../types/reducers'
8+
import { AUTH0_DISABLED } from '../constants'
9+
import { getComponentMessages } from '../../common/util/config'
10+
import type { ManagerUserState } from '../../types/reducers'
1011

1112
type Props = {
1213
logout: () => any,
@@ -42,13 +43,16 @@ export default class UserButtons extends Component<Props> {
4243
</Button>
4344
</LinkContainer>
4445
)}
45-
<Button
46-
bsSize='small'
47-
bsStyle='primary'
48-
onClick={logout}
49-
style={buttonStyle}>
50-
<Icon type='sign-out' /> {this.messages('logout')}
51-
</Button>
46+
{/* "Log out" Button (unless auth is disabled) */}
47+
{!AUTH0_DISABLED && (
48+
<Button
49+
bsSize='small'
50+
bsStyle='primary'
51+
onClick={logout}
52+
style={buttonStyle}>
53+
<Icon type='sign-out' /> {this.messages('logout')}
54+
</Button>
55+
)}
5256
</ButtonToolbar>
5357
)
5458
}

lib/common/constants/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const AUTH0_CLIENT_ID = process.env.AUTH0_CLIENT_ID || ''
1515
export const AUTH0_CONNECTION_NAME = process.env.AUTH0_CONNECTION_NAME || ''
1616
export const AUTH0_DEFAULT_SCOPE = 'app_metadata profile email openid user_metadata'
1717
export const AUTH0_DOMAIN = process.env.AUTH0_DOMAIN || ''
18+
export const AUTH0_DISABLED = Boolean(process.env.DISABLE_AUTH)
1819

1920
export const AUTO_DEPLOY_TYPES = Object.freeze({
2021
ON_FEED_FETCH: 'ON_FEED_FETCH',

lib/common/containers/App.js

+24-14
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import MainAlertsViewer from '../../alerts/containers/MainAlertsViewer'
1313
import ActiveAlertEditor from '../../alerts/containers/ActiveAlertEditor'
1414
import Login from '../components/Login'
1515
import PageNotFound from '../components/PageNotFound'
16-
import { AUTH0_CLIENT_ID, AUTH0_DEFAULT_SCOPE, AUTH0_DOMAIN } from '../constants'
16+
import { AUTH0_CLIENT_ID, AUTH0_DEFAULT_SCOPE, AUTH0_DISABLED, AUTH0_DOMAIN } from '../constants'
1717
import ActiveGtfsEditor from '../../editor/containers/ActiveGtfsEditor'
1818
import ActiveFeedSourceViewer from '../../manager/containers/ActiveFeedSourceViewer'
1919
import ActiveProjectsList from '../../manager/containers/ActiveProjectsList'
@@ -29,6 +29,7 @@ import type { dispatchFn, getStateFn } from '../../types/reducers'
2929

3030
import ActiveUserRetriever from './ActiveUserRetriever'
3131
import AppInfoRetriever from './AppInfoRetriever'
32+
import LocalUserRetriever from './LocalUserRetriever'
3233
import wrapComponentInAuthStrategy from './wrapComponentInAuthStrategy'
3334

3435
function loginOptional (ComponentToWrap) {
@@ -127,26 +128,35 @@ export default class App extends React.Component<AppProps> {
127128
path: '*'
128129
}
129130
]
130-
const routerWithAuth0 = (
131-
<Auth0Provider
132-
audience=''
133-
// Continue to cache tokens in localstorage (speeds up updating login state when refreshing pages).
134-
cacheLocation='localstorage'
135-
clientId={AUTH0_CLIENT_ID}
136-
domain={AUTH0_DOMAIN}
137-
onRedirectCallback={this.handleRedirect}
138-
redirectUri={window.location.origin}
139-
scope={AUTH0_DEFAULT_SCOPE}
140-
>
141-
<ActiveUserRetriever />
131+
const appContent = (
132+
<>
142133
<AppInfoRetriever />
143134
<Router
144135
history={browserHistory}
145136
onUpdate={logPageView}>
146137
{routes.map((r, i) => (<Route {...r} key={i} />))}
147138
</Router>
148-
</Auth0Provider>
139+
</>
149140
)
141+
const routerWithAuth0 = AUTH0_DISABLED ? <>
142+
<LocalUserRetriever />
143+
{appContent}
144+
</>
145+
: (
146+
<Auth0Provider
147+
audience=''
148+
// Continue to cache tokens in localstorage (speeds up updating login state when refreshing pages).
149+
cacheLocation='localstorage'
150+
clientId={AUTH0_CLIENT_ID}
151+
domain={AUTH0_DOMAIN}
152+
onRedirectCallback={this.handleRedirect}
153+
redirectUri={window.location.origin}
154+
scope={AUTH0_DEFAULT_SCOPE}
155+
>
156+
<ActiveUserRetriever />
157+
{appContent}
158+
</Auth0Provider>
159+
)
150160
// Initialize toast notifications.
151161
toast.configure()
152162
// Configure bugsnag if key is provided.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// @flow
2+
// $FlowFixMe useEffect not recognized by flow.
3+
import { useEffect } from 'react'
4+
import { connect } from 'react-redux'
5+
6+
import * as userActions from '../../manager/actions/user'
7+
import { AUTH0_CLIENT_ID } from '../constants'
8+
9+
type Props = {
10+
receiveTokenAndProfile: typeof userActions.receiveTokenAndProfile
11+
}
12+
13+
const profile = {
14+
app_metadata: {
15+
'datatools': [
16+
{
17+
'permissions': [
18+
{
19+
'type': 'administer-application'
20+
}
21+
],
22+
'projects': [],
23+
'client_id': AUTH0_CLIENT_ID,
24+
'subscriptions': []
25+
}
26+
],
27+
'roles': [
28+
'user'
29+
]
30+
},
31+
// FIXME: pick a better email address for both backend and frontend.
32+
33+
name: 'localuser',
34+
nickname: 'Local User',
35+
picture: 'https://d2tyb7byn1fef9.cloudfront.net/ibi_group_black-512x512.png',
36+
sub: 'localuser',
37+
user_id: 'localuser',
38+
user_metadata: {}
39+
}
40+
41+
const token = 'local-user-token'
42+
43+
/**
44+
* This component provides a user profile for configs without authentication.
45+
*/
46+
const LocalUserRetriever = ({ receiveTokenAndProfile }: Props) => {
47+
// Update the user info in the redux state on initialization.
48+
useEffect(() => {
49+
receiveTokenAndProfile({ profile, token })
50+
}, [])
51+
52+
// Component renders nothing.
53+
return null
54+
}
55+
56+
const mapDispatchToProps = {
57+
receiveTokenAndProfile: userActions.receiveTokenAndProfile
58+
}
59+
60+
export default connect(null, mapDispatchToProps)(LocalUserRetriever)

lib/common/containers/WatchButton.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import {connect} from 'react-redux'
77
import * as userActions from '../../manager/actions/user'
88
import * as statusActions from '../../manager/actions/status'
99
import {getComponentMessages, getConfigProperty} from '../util/config'
10-
1110
import type {AppState, ManagerUserState} from '../../types/reducers'
11+
import { AUTH0_DISABLED } from '../constants'
1212

1313
type ContainerProps = {
1414
componentClass?: string,
@@ -84,9 +84,12 @@ class WatchButton extends Component<Props> {
8484
}
8585

8686
render () {
87+
// Do not render watch button if notifications are not enabled or if auth is disabled.
88+
// (Notifications require an email address verified with Auth0.)
89+
if (AUTH0_DISABLED || !getConfigProperty('application.notifications_enabled')) {
90+
return null
91+
}
8792
const {componentClass} = this.props
88-
// Do not render watch button if notifications are not enabled.
89-
if (!getConfigProperty('application.notifications_enabled')) return null
9093
switch (componentClass) {
9194
case 'menuItem':
9295
return (

lib/common/containers/wrapComponentInAuthStrategy.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { browserHistory } from 'react-router'
99

1010
import {getComponentMessages} from '../util/config'
1111
import type {AppState, ManagerUserState} from '../../types/reducers'
12+
import { AUTH0_DISABLED } from '../constants'
1213

1314
type AuthWrapperProps = {
1415
user: ManagerUserState
@@ -68,7 +69,7 @@ export default function wrapComponentInAuthStrategy (
6869
(state: AppState) => ({user: state.user})
6970
)(AuthStrategyWrapper)
7071

71-
if (requireAuth || requireAdmin) {
72+
if (!AUTH0_DISABLED && (requireAuth || requireAdmin)) {
7273
return withAuthenticationRequired(connectedComponent)
7374
}
7475

lib/manager/components/FeedSourceViewer.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ import * as snapshotActions from '../../editor/actions/snapshots'
1414
import * as gtfsPlusActions from '../../gtfsplus/actions/gtfsplus'
1515
import ManagerPage from '../../common/components/ManagerPage'
1616
import {getComponentMessages, isModuleEnabled} from '../../common/util/config'
17-
import ManagerHeader from './ManagerHeader'
1817
import ActiveFeedVersionNavigator from '../containers/ActiveFeedVersionNavigator'
19-
import FeedSourceSettings from './FeedSourceSettings'
20-
import NotesViewer from './NotesViewer'
2118
import ActiveEditorFeedSourcePanel from '../../editor/containers/ActiveEditorFeedSourcePanel'
2219
import {isEditingDisabled} from '../util'
23-
2420
import type {Props as ContainerProps} from '../containers/ActiveFeedSourceViewer'
2521
import type {Feed, Note, Project} from '../../types'
2622
import type {ManagerUserState} from '../../types/reducers'
2723

24+
import NotesViewer from './NotesViewer'
25+
import FeedSourceSettings from './FeedSourceSettings'
26+
import ManagerHeader from './ManagerHeader'
27+
2828
type Props = ContainerProps & {
2929
activeComponent: string,
3030
activeSubComponent: string,

lib/manager/components/UserHomePage.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import { Auth0ContextInterface } from '@auth0/auth0-react'
44
import Icon from '@conveyal/woonerf/components/icon'
55
import React, {Component} from 'react'
6-
import {Grid, Row, Col, Button, ButtonToolbar, Jumbotron} from 'react-bootstrap'
6+
import { Alert, Button, ButtonToolbar, Col, Grid, Jumbotron, Row } from 'react-bootstrap'
77
import objectPath from 'object-path'
88

99
import * as feedsActions from '../actions/feeds'
1010
import * as userActions from '../actions/user'
1111
import * as visibilityFilterActions from '../actions/visibilityFilter'
1212
import ManagerPage from '../../common/components/ManagerPage'
13-
import { DEFAULT_DESCRIPTION, DEFAULT_TITLE } from '../../common/constants'
13+
import { AUTH0_DISABLED, DEFAULT_DESCRIPTION, DEFAULT_TITLE } from '../../common/constants'
1414
import {getConfigProperty, getComponentMessages} from '../../common/util/config'
1515
import {defaultSorter} from '../../common/util/util'
1616
import type {Props as ContainerProps} from '../containers/ActiveUserHomePage'
@@ -85,6 +85,7 @@ export default class UserHomePage extends Component<Props, State> {
8585
} = this.props
8686
const visibleProjects = projects.sort(defaultSorter)
8787
const activeProject = project
88+
const appTitle = getConfigProperty('application.title') || 'datatools'
8889
return (
8990
<ManagerPage
9091
ref='page'
@@ -96,7 +97,7 @@ export default class UserHomePage extends Component<Props, State> {
9697
<Col md={8} xs={12}>
9798
{/* Top Welcome Box */}
9899
<Jumbotron style={{ padding: 30 }}>
99-
<h2>{this.messages('welcomeTo')} {getConfigProperty('application.title') || DEFAULT_TITLE}!</h2>
100+
<h2>{this.messages('welcomeTo')} {appTitle || DEFAULT_TITLE}!</h2>
100101
<p>{getConfigProperty('application.description') || DEFAULT_DESCRIPTION}</p>
101102
<ButtonToolbar>
102103
<Button
@@ -108,6 +109,13 @@ export default class UserHomePage extends Component<Props, State> {
108109
</Button>
109110
</ButtonToolbar>
110111
</Jumbotron>
112+
{/* Info banner shown if auth is disabled. */}
113+
{AUTH0_DISABLED && (
114+
<Alert bsStyle='info'>
115+
<Icon type='info-circle' />
116+
{this.messages('authDisabledInfo').replace('%appTitle%', appTitle)}
117+
</Alert>
118+
)}
111119
{/* Recent Activity List */}
112120
<h3 style={{ borderBottom: '2px solid #ddd', marginTop: 0, paddingBottom: 5 }}>
113121
<Icon type='comments-o' /> {this.messages('recentActivity')}

lib/manager/containers/FeedSourceTable.js

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {connect} from 'react-redux'
44

55
import FeedSourceTable from '../components/FeedSourceTable'
66
import {getFilteredFeeds} from '../util'
7-
87
import type {Project} from '../../types'
98
import type {AppState} from '../../types/reducers'
109

0 commit comments

Comments
 (0)