Skip to content
This repository has been archived by the owner on Jun 30, 2021. It is now read-only.

Commit

Permalink
create key from account (#942)
Browse files Browse the repository at this point in the history
* be able to create key from account scope

* some renaming

* make new reducer for nested key when getting access keys from sub account

* call it

* fix account_role

* pass correct id

* Fix key update

* refetch after update

* remove accountrole from main keys

* Fix linter
  • Loading branch information
nicholasmueller authored and Thibault committed Apr 19, 2019
1 parent 4243a62 commit 495a107
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 51 deletions.
20 changes: 15 additions & 5 deletions apps/admin_api/lib/admin_api/v1/controllers/key_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ defmodule AdminAPI.V1.KeyController do
"""
@spec update(Plug.Conn.t(), map()) :: Plug.Conn.t()
def update(conn, %{"id" => id} = attrs) do
attrs = %{
name: attrs["name"],
global_role: attrs["global_role"],
enabled: !attrs["expired"]
}
attrs =
%{}
|> add_to_attrs(attrs, "name", :name, attrs["name"])
|> add_to_attrs(attrs, "global_role", :global_role, attrs["global_role"])
|> add_to_attrs(attrs, "expired", :enabled, !attrs["expired"])

with %Key{} = key <- Key.get(id) || {:error, :unauthorized},
{:ok, _} <- authorize(:update, conn.assigns, key),
Expand Down Expand Up @@ -224,6 +224,16 @@ defmodule AdminAPI.V1.KeyController do

defp do_delete(conn, nil), do: handle_error(conn, :key_not_found)

defp add_to_attrs(new_attrs, attrs, string_name, atom_name, value) do
case Map.has_key?(attrs, string_name) do
true ->
Map.put(new_attrs, atom_name, value)

false ->
new_attrs
end
end

defp authorize(action, actor, %Account{} = account) do
AccountPolicy.authorize(action, actor, account)
end
Expand Down
12 changes: 10 additions & 2 deletions apps/admin_panel/assets/src/omg-access-key/action.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import * as accessKeyService from '../services/accessKeyService'
import { createActionCreator, createPaginationActionCreator } from '../utils/createActionCreator'
export const createAccessKey = ({ name, globalRole }) =>

export const createAccessKey = ({ name, globalRole, accountId, roleName }) =>
createActionCreator({
actionName: 'ACCESS_KEY',
action: 'CREATE',
service: () => accessKeyService.createAccessKey({ name, globalRole })
service: () => accessKeyService.createAccessKey({
name,
globalRole,
accountId,
roleName
})
})

export const deleteAccessKey = id =>
createActionCreator({
actionName: 'ACCESS_KEY',
action: 'DELETE',
service: () => accessKeyService.deleteAccessKeyById(id)
})

export const updateAccessKey = ({ id, expired }) =>
createActionCreator({
actionName: 'ACCESS_KEY',
Expand Down
3 changes: 3 additions & 0 deletions apps/admin_panel/assets/src/omg-access-key/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export const accessKeysReducer = createReducer(
'ACCESS_KEYS/REQUEST/SUCCESS': (state, { data }) => {
return _.merge(state, _.keyBy(data, 'id'))
},
'MEMBERSHIP_ACCESS_KEYS/REQUEST/SUCCESS': (state, { data }) => {
return _.merge(state, _.keyBy(data, 'key.id'))
},
'ACCESS_KEY/UPDATE/SUCCESS': (state, { data }) => {
return { ...state, ...{ [data.id]: data } }
},
Expand Down
3 changes: 2 additions & 1 deletion apps/admin_panel/assets/src/omg-access-key/selector.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const selectAccessKeys = state => _.sortBy(_.values(state.accessKeyss), 'created_at').reverse()
export const selectAccessKeys = state => _.sortBy(_.values(state.accessKeys), 'created_at').reverse()

export const selectAccessKeysLoadingStatus = state => state.loadingStatus.accessKeys

export const selectGetAccessKeyById = state => id => state.accessKeys[id]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@ export default createFetcher('accessKeys', getKeysAccountId, (state, props) => (
data: selectAccessKeysCachedQuery(state)(props.cacheKey),
pagination: selectCachedQueryPagination(state)(props.cacheKey)
}))

2 changes: 1 addition & 1 deletion apps/admin_panel/assets/src/omg-account/action.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const getKeysAccountId = ({
matchAny
}) =>
createPaginationActionCreator({
actionName: 'ACCESS_KEYS',
actionName: 'MEMBERSHIP_ACCESS_KEYS',
action: 'REQUEST',
service: () =>
accountService.getKeysByAccountId({
Expand Down
6 changes: 6 additions & 0 deletions apps/admin_panel/assets/src/omg-cache/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export const cacheReducer = createReducer(
[action.cacheKey]: { ids: action.data.map(d => d.id), pagination: action.pagination }
}
},
'MEMBERSHIP_ACCESS_KEYS/REQUEST/SUCCESS': (state, action) => {
return {
...state,
[action.cacheKey]: { ids: action.data.map(d => d.key.id), pagination: action.pagination }
}
},
'WALLETS/REQUEST/SUCCESS': (state, action) => {
return {
...state,
Expand Down
57 changes: 42 additions & 15 deletions apps/admin_panel/assets/src/omg-create-admin-key-modal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,16 @@ CreateAdminKeyModal.propTypes = {
createAccessKey: PropTypes.func,
onRequestClose: PropTypes.func,
onSubmitSuccess: PropTypes.func,
assignKey: PropTypes.func
assignKey: PropTypes.func,
accountId: PropTypes.string
}

function CreateAdminKeyModal (props) {
const [label, setLabel] = useState('')
const [submitStatus, setSubmitStatus] = useState('DEFAULT')
const [role, setRole] = useState('none')
const [roleName, setRoleName] = useState('viewer')

function onRequestClose () {
setLabel('')
setRole('none')
Expand All @@ -88,7 +91,12 @@ function CreateAdminKeyModal (props) {
async function onSubmit (e) {
e.preventDefault()
setSubmitStatus('SUBMITTED')
const { data } = await props.createAccessKey({ name: label, globalRole: role })
const { data } = await props.createAccessKey({
name: label,
globalRole: role,
accountId: props.accountId,
roleName
})
if (data) {
setSubmitStatus('SUCCESS')
props.onSubmitSuccess(data)
Expand Down Expand Up @@ -117,19 +125,38 @@ function CreateAdminKeyModal (props) {
onChange={e => setLabel(e.target.value)}
value={label}
/>
<InputLabel>Global Role</InputLabel>
<StyledSelect
normalPlaceholder='Role ( optional )'
value={_.startCase(role)}
onSelectItem={item => onSelectRole(item.key)}
options={[
{ key: 'super_admin', value: 'Super Admin' },
{ key: 'admin', value: 'Admin' },
{ key: 'viewer', value: 'Viewer' },
{ key: 'none', value: 'None' }
]}
optionRenderer={value => _.startCase(value)}
/>
{!props.accountId && (
<>
<InputLabel>Global Role</InputLabel>
<StyledSelect
normalPlaceholder='Role ( optional )'
value={_.startCase(role)}
onSelectItem={item => onSelectRole(item.key)}
options={[
{ key: 'super_admin', value: 'Super Admin' },
{ key: 'admin', value: 'Admin' },
{ key: 'viewer', value: 'Viewer' },
{ key: 'none', value: 'None' }
]}
optionRenderer={value => _.startCase(value)}
/>
</>
)}
{props.accountId && (
<>
<InputLabel>Select Role</InputLabel>
<StyledSelect
normalPlaceholder='Role name'
value={_.startCase(roleName)}
onSelectItem={item => setRoleName(item.key)}
options={[
{ key: 'admin', value: 'Admin' },
{ key: 'viewer', value: 'Viewer' },
]}
optionRenderer={value => _.startCase(value)}
/>
</>
)}
<CreateAdminKeyButton
styleType='primary'
type='submit'
Expand Down
40 changes: 23 additions & 17 deletions apps/admin_panel/assets/src/omg-page-api/AdminKeySection.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import { compose } from 'recompose'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import moment from 'moment'

import { Switch, Icon, Button } from '../omg-uikit'
import Table from '../omg-table'
import AccessKeyFetcher from '../omg-access-key/accessKeysFetcher'
import moment from 'moment'
import ConfirmationModal from '../omg-confirmation-modal'
import { connect } from 'react-redux'
import { compose } from 'recompose'
import { createApiKey } from '../omg-api-keys/action'
import { createAccessKey, updateAccessKey, downloadKey } from '../omg-access-key/action'
import CreateAdminKeyModal from '../omg-create-admin-key-modal'
import queryString from 'query-string'
import { withRouter } from 'react-router-dom'
import Copy from '../omg-copy'
import { createSearchAdminKeyQuery } from '../omg-access-key/searchField'

const KeySection = styled.div`
position: relative;
p {
Expand Down Expand Up @@ -151,7 +153,7 @@ class ApiKeyPage extends Component {
onRequestClose: PropTypes.func,
columnsAdminKeys: PropTypes.array,
search: PropTypes.string,
downloadKey: PropTypes.func
downloadKey: PropTypes.func,
}

static defaultProps = {
Expand All @@ -160,7 +162,6 @@ class ApiKeyPage extends Component {
{ key: 'name', title: 'NAME' },
{ key: 'key', title: 'ACCESS KEY' },
{ key: 'global_role', title: 'GLOBAL ROLE' },
{ key: 'account_role', title: 'ACCOUNT ROLE' },
{ key: 'created_at', title: 'CREATED AT' },
{ key: 'status', title: 'STATUS' }
]
Expand Down Expand Up @@ -190,6 +191,7 @@ class ApiKeyPage extends Component {
}
onClickAccessKeySwitch = ({ id, expired, fetch }) => async e => {
await this.props.updateAccessKey({ id, expired })
fetch();
}
onClickDownloadKey = e => {
this.props.downloadKey({
Expand Down Expand Up @@ -274,19 +276,22 @@ class ApiKeyPage extends Component {
}}
{...this.props}
render={({ data, individualLoadingStatus, pagination, fetch }) => {
const apiKeysRows = data.map((key, index) => {
if (key.hasOwnProperty('key')) {
key = key.key;
const apiKeysRows = data.map((item, index) => {

let role;
if (item.hasOwnProperty('key')) {
role = item.role;
item = item.key;
}
return {
id: index,
key: key.access_key,
user: key.account_id,
created_at: key.created_at,
status: key.expired,
name: key.name || 'Not Provided',
global_role: key.global_role || 'none',
account_role: key.account_role || 'none'
id: item.id,
key: item.access_key,
user: item.account_id,
created_at: item.created_at,
status: item.expired,
name: item.name || 'Not Provided',
global_role: item.global_role || 'none',
account_role: role || 'none'
}
})
return (
Expand All @@ -303,6 +308,7 @@ class ApiKeyPage extends Component {
pageEntity='access_key_page'
/>
<CreateAdminKeyModal
accountId={this.props.match.params.accountId}
open={this.props.createAdminKeyModalOpen}
onRequestClose={this.props.onRequestClose}
onSubmitSuccess={this.onSubmitSuccess(fetch)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,36 @@ import { withRouter } from 'react-router-dom'
import queryString from 'query-string'
import { createSearchAdminSubKeyQuery } from '../omg-access-key/searchField'
import AssignKeyModal from '../omg-assign-key-account-modal'

const AccountKeySubPageContainer = styled.div``
const AssignButton = styled(Button)`
padding-left: 40px;
padding-right: 40px;
`

export default withRouter(function AccountKeySubPage (props) {
const [assignAdminKeyModalOpen, setAssignAdminKeyModalOpen] = useState(false)
const [createAdminKeyModalOpen, setCreateAdminKeyModalOpen] = useState(false)
const [fetcher, setFetcher] = useState(_.noop)

const { search, access_key_page } = queryString.parse(props.location.search)
return (
<AccountKeySubPageContainer>
<TopNavigation
title='Keys'
divider={false}
buttons={[
<Button size='small' onClick={e => onClickCreateAdminKey} styleType={'secondary'}>
<AssignButton key='assign' onClick={e => setAssignAdminKeyModalOpen(true)}>
Assign
</AssignButton>,
<Button key='generate-admin-key' size='small' onClick={e => setCreateAdminKeyModalOpen(true)} styleType={'secondary'}>
<span>Generate Admin Key</span>
</Button>,
<AssignButton key='key' onClick={e => setCreateAdminKeyModalOpen(true)}>
Assign Admin Key
</AssignButton>
</Button>
]}
/>
<AdminKeySection
createAdminKeyModalOpen={createAdminKeyModalOpen}
onRequestClose={e => setCreateAdminKeyModalOpen(false)}
fetcher={AccountKeyFetcher}
registerFetch={fetcher => setFetcher(fetcher)}
columnsAdminKeys={[
Expand All @@ -52,9 +57,9 @@ export default withRouter(function AccountKeySubPage (props) {
/>
<AssignKeyModal
onSubmitSuccess={() => fetcher.fetch()}
open={createAdminKeyModalOpen}
open={assignAdminKeyModalOpen}
accountId={props.match.params.accountId}
onRequestClose={() => setCreateAdminKeyModalOpen(false)}
onRequestClose={() => setAssignAdminKeyModalOpen(false)}
/>
</AccountKeySubPageContainer>
)
Expand Down
6 changes: 4 additions & 2 deletions apps/admin_panel/assets/src/services/accessKeyService.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ export function getAccessKeys ({ perPage, page, matchAll, matchAny, sort }) {
})
}

export function createAccessKey ({ name, globalRole }) {
export function createAccessKey ({ name, globalRole, accountId, roleName }) {
return authenticatedRequest({
path: '/access_key.create',
data: {
name,
global_role: globalRole
global_role: globalRole,
account_id: accountId,
role_name: roleName
}
})
}
Expand Down

0 comments on commit 495a107

Please sign in to comment.