Skip to content

Commit

Permalink
Security Preference instructions in Settings and Files tab (keybase#1…
Browse files Browse the repository at this point in the history
…1378)

* Security Preference instructions in Settings and Files tab

* fixes

* review feedback from chrisnojima, some of

* some tweaks in security-prefs component
  • Loading branch information
songgao authored Apr 17, 2018
1 parent b0a1368 commit 3fea6a7
Show file tree
Hide file tree
Showing 19 changed files with 320 additions and 10 deletions.
5 changes: 5 additions & 0 deletions shared/actions/fs-gen.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions shared/actions/fs-platform-specific.desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,26 @@ export function uninstallKBFSSuccess(result: RPCTypes.UninstallResult) {
app.exit(0)
}

export function openSecurityPreferences() {
return Saga.call(
() =>
new Promise((resolve, reject) => {
Electron.shell.openExternal(
'x-apple.systempreferences:com.apple.preference.security?General',
{},
err => {
if (err) {
reject(err)
return
}
logger.info('Opened Security Preferences')
resolve()
}
)
})
)
}

// Invoking the cached installer package has to happen from the topmost process
// or it won't be visible to the user. The service also does this to support command line
// operations.
Expand Down
1 change: 1 addition & 0 deletions shared/actions/fs-platform-specific.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ declare export function installKBFSSuccess(RPCTypes.InstallResult): void
declare export function uninstallKBFSConfirmSaga(): Promise<*>
declare export function uninstallKBFS(): void
declare export function uninstallKBFSSuccess(result: RPCTypes.UninstallResult): void
declare export function openSecurityPreferences(): Saga.SagaGenerator<any, any>
1 change: 1 addition & 0 deletions shared/actions/fs-platform-specific.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export function* installFuseSaga(): Saga.SagaGenerator<any, any> {}
export function installDokanSaga() {}
export function installKBFS() {}
export function installKBFSSuccess(payload: RPCTypes.InstallResult) {}
export function openSecurityPreferences() {}
export function uninstallKBFSConfirmSaga(): Promise<*> {
return new Promise((resolve, reject) => reject(new Error('unimplemented')))
}
Expand Down
5 changes: 4 additions & 1 deletion shared/actions/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
installKBFSSuccess,
installFuseSaga,
installDokanSaga,
openSecurityPreferences,
uninstallKBFSConfirmSaga,
uninstallKBFS,
uninstallKBFSSuccess,
Expand Down Expand Up @@ -277,7 +278,9 @@ function* fsSaga(): Saga.SagaGenerator<any, any> {
yield Saga.safeTakeEveryPure(FsGen.uninstallKBFS, uninstallKBFS, uninstallKBFSSuccess)

if (!isMobile) {
// TODO: enable this when we need it on mobile.
yield Saga.safeTakeEveryPure(FsGen.openSecurityPreferences, openSecurityPreferences)

// TODO: enable these when we need it on mobile.
yield Saga.safeTakeEvery(FsGen.fsActivity, pollSyncStatusUntilDone)
yield Saga.safeTakeEveryPure(FsGen.setupFSHandlers, _setupFSHandlers)
}
Expand Down
2 changes: 2 additions & 0 deletions shared/actions/json/fs.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"kbfsInstalling?": "boolean",
"fuseInstalling?": "boolean",
"kextPermissionError?": "boolean",
"securityPrefsPropmted?": "boolean",
"showBanner?": "boolean",
"syncing?": "boolean"
},
Expand All @@ -78,6 +79,7 @@
},
"uninstallKBFS": {},
"fsActivity": {},
"openSecurityPreferences": {},

"setupFSHandlers": {}
}
Expand Down
1 change: 1 addition & 0 deletions shared/constants/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const makeFlags: I.RecordFactory<Types._Flags> = I.Record({
kbfsInstalling: false,
fuseInstalling: false,
kextPermissionError: false,
securityPrefsPropmted: false,
showBanner: false,
syncing: false,
})
Expand Down
1 change: 1 addition & 0 deletions shared/constants/types/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export type _Flags = {
kbfsInstalling: boolean,
fuseInstalling: boolean,
kextPermissionError: boolean,
securityPrefsPropmted: boolean,
showBanner: boolean,
syncing: boolean,
}
Expand Down
33 changes: 33 additions & 0 deletions shared/fs/common/security-prefs-container.desktop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// @flow
import {connect, compose, lifecycle, type TypedState, type Dispatch} from '../../util/container'
import * as FsGen from '../../actions/fs-gen'
import InstallSecurityPrefs from './security-prefs.desktop'
import {navigateUp} from '../../actions/route-tree'
import {isLinux} from '../../constants/platform'

const mapStateToProps = (state: TypedState) => {
const kbfsEnabled = isLinux || (state.fs.fuseStatus && state.fs.fuseStatus.kextStarted)
return {
appFocusedCount: state.config.appFocusedCount,
needAction: !kbfsEnabled && state.fs.flags.kextPermissionError,
}
}

const mapDispatchToProps = (dispatch: Dispatch) => ({
back: () => dispatch(navigateUp()),
openSecurityPrefs: () => dispatch(FsGen.createOpenSecurityPreferences()),
_getFuseStatus: () => dispatch(FsGen.createFuseStatus()),
})

export default compose(
connect(mapStateToProps, mapDispatchToProps),
lifecycle({
componentDidUpdate(prevProps) {
if (this.props.appFocusedCount !== prevProps.appFocusedCount) {
// If the window is recently re-focused, it's possible that user has
// gone to the security preferences to allow the kext. So check again.
this.props._getFuseStatus()
}
},
})
)(InstallSecurityPrefs)
4 changes: 4 additions & 0 deletions shared/fs/common/security-prefs-container.js.flow
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow
import {compose} from '../../util/container'

export default compose()(()=>null)
4 changes: 4 additions & 0 deletions shared/fs/common/security-prefs-container.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

const Empty = () => null
export default Empty
53 changes: 53 additions & 0 deletions shared/fs/common/security-prefs-prompting-hoc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// @flow
import {branch, compose, connect, renderNothing, type Dispatch, type TypedState} from '../../util/container'
import {isLinux, isMobile} from '../../constants/platform'
import * as FsGen from '../../actions/fs-gen'
import {navigateAppend} from '../../actions/route-tree'

// On desktop, SecurityPrefsPromptingHoc prompts user about going to security
// preferences to allow the kext if needed. It prompts at most once per
// app-restart. Which means for example, if the user goes to Files tab and gets
// an prompt. they won't get prompted again in Settings, or after they click
// "Back" in the prompt and go back to Files tab again. This is to avoid
// spamming the user. We have a link in the Settings page so if the user wants
// they can still find the instructions.

const mapStateToProps = (state: TypedState) => {
const {securityPrefsPropmted, kextPermissionError} = state.fs.flags
const kbfsEnabled = isLinux || (state.fs.fuseStatus && state.fs.fuseStatus.kextStarted)
return {
shouldPromptSecurityPrefs: !securityPrefsPropmted && !kbfsEnabled && kextPermissionError,
}
}

const mapDispatchToProps = (dispatch: Dispatch) => ({
showSecurityPrefsOnce: () => {
dispatch(
FsGen.createSetFlags({
securityPrefsPropmted: true,
})
)
dispatch(
navigateAppend([
{
props: {},
selected: 'securityPrefs',
},
])
)
},
})

const displayOnce = ({shouldPromptSecurityPrefs, showSecurityPrefsOnce}) => {
shouldPromptSecurityPrefs && showSecurityPrefsOnce()
return shouldPromptSecurityPrefs
}

const DesktopSecurityPrefsPromptingHoc = compose(
connect(mapStateToProps, mapDispatchToProps),
branch(displayOnce, renderNothing)
)

const SecurityPrefsPromptingHoc = isMobile ? (i: any) => i : DesktopSecurityPrefsPromptingHoc

export default SecurityPrefsPromptingHoc
141 changes: 141 additions & 0 deletions shared/fs/common/security-prefs.desktop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// @flow
import React from 'react'
import {BackButton, Box, Text} from '../../common-adapters'
import {globalStyles, globalMargins, globalColors, platformStyles} from '../../styles'
import {fileUIName} from '../../constants/platform'

type Props = {
back: () => void,
openSecurityPrefs: () => void,
needAction: boolean,
}

const securityPreferenceIllustration = require('../../images/install/security-preferences.png')

const InstallSecurityPrefs = (props: Props) =>
props.needAction ? (
<Box style={stylesContainer}>
<Box style={stylesHeader}>
<BackButton key="back" onClick={props.back} style={stylesClose} />
</Box>
<Text type="HeaderBig" style={stylesTitle}>
Ghhh. Try this.
</Text>
<Text type="Body" style={{paddingBottom: 24}}>
Open your macOS Security & Privacy Settings and follow these steps.
</Text>
<Box style={{...globalStyles.flexBoxRow, marginRight: 20}}>
<Box style={{position: 'relative'}}>
<img width={500} height={437} src={securityPreferenceIllustration} />
<Box
style={{...styleHighlight, height: 30, left: 42, position: 'absolute', top: 350, width: 162}}
/>
<Text type="BodySemibold" style={{...stylesNumberList, left: 72, position: 'absolute', top: 374}}>
1
</Text>
<Box
style={{...styleHighlight, height: 30, left: 352, position: 'absolute', top: 282, width: 94}}
/>
<Text type="BodySemibold" style={{...stylesNumberList, left: 432, position: 'absolute', top: 302}}>
2
</Text>
</Box>
<Box style={{...globalStyles.flexBoxColumn, marginTop: 30}}>
<Box style={globalStyles.flexBoxRow}>
<Text type="BodySemibold" style={stylesNumberList}>
1
</Text>
<Text type="BodySemibold" style={styleListText}>
Click the lock icon then enter your password
</Text>
</Box>
<Box style={globalStyles.flexBoxRow}>
<Text type="BodySemibold" style={stylesNumberList}>
2
</Text>
<Text type="BodySemibold" style={styleListText}>
Click "Allow"
</Text>
</Box>
<Text
type="BodySemiboldLink"
style={{fontSize: 14, paddingTop: 20}}
onClick={props.openSecurityPrefs}
>
Open Security & Privacy Settings
</Text>
</Box>
</Box>
</Box>
) : (
<Box style={stylesContainer}>
<Box style={stylesHeader}>
<BackButton key="back" onClick={props.back} style={stylesClose} />
</Box>
<Text type="HeaderBig" style={stylesTitleSuccess}>
Success!
</Text>
<Text type="Body">Your Keybase folders will now appear in your {fileUIName}.</Text>
{/* TODO: implement the rest of design */}
</Box>
)

const stylesContainer = {
...globalStyles.flexBoxColumn,
alignItems: 'center',
flex: 1,
height: '100%',
justifyContent: 'start',
overflowY: 'auto',
position: 'relative',
}

const stylesNumberList = platformStyles({
isElectron: {
backgroundColor: globalColors.blue,
borderRadius: '50%',
color: globalColors.white,
height: 20,
marginRight: 13,
minWidth: 20,
paddingTop: 1,
textAlign: 'center',
width: 20,
},
})

const styleListText = {
paddingBottom: 16,
paddingTop: 1,
}

const styleHighlight = {
backgroundColor: globalColors.black_05,
borderColor: globalColors.blue,
borderRadius: 100,
borderStyle: 'solid',
borderWidth: 2,
}

const stylesHeader = {
...globalStyles.flexBoxRow,
justifyContent: 'start',
width: '100%',
height: 48,
}

const stylesClose = {
marginLeft: globalMargins.tiny,
}

const stylesTitle = {
marginTop: globalMargins.tiny,
marginBottom: globalMargins.small,
}

const stylesTitleSuccess = {
...stylesTitle,
color: globalColors.green2,
}

export default InstallSecurityPrefs
3 changes: 2 additions & 1 deletion shared/fs/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Files from '.'
import * as FsGen from '../actions/fs-gen'
import * as Types from '../constants/types/fs'
import * as Constants from '../constants/fs'
import SecurityPrefsPromptingHoc from './common/security-prefs-prompting-hoc'

const mapStateToProps = (state: TypedState, {path}) => {
const itemDetail = state.fs.pathItems.get(path)
Expand Down Expand Up @@ -77,4 +78,4 @@ const FilesLoadingHoc = compose(
setDisplayName('FilesLoadingHoc')
)(ConnectedFiles)

export default FilesLoadingHoc
export default SecurityPrefsPromptingHoc(FilesLoadingHoc)
Loading

0 comments on commit 3fea6a7

Please sign in to comment.