Skip to content

Commit

Permalink
enable reconfiguration restrictions / extension mode
Browse files Browse the repository at this point in the history
  • Loading branch information
buck54321 committed Aug 1, 2023
1 parent 4ab15ac commit baa08e1
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 28 deletions.
3 changes: 3 additions & 0 deletions client/app/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ type CoreConfig struct {
NoAutoDBBackup bool `long:"no-db-backup" description:"Disable creation of a database backup on shutdown."`
UnlockCoinsOnLogin bool `long:"release-wallet-coins" description:"On login or wallet creation, instruct the wallet to release any coins that it may have locked."`
SimnetFiatRates bool `long:"simnet-fiat-rates" description:"Fetch fiat rates when running in simnet mode."`

ExtensionModeFile string `long:"extension-mode-file" description:"path to a file that specifies options for running core as an extension."`
}

// WebConfig encapsulates the configuration needed for the web server.
Expand Down Expand Up @@ -195,6 +197,7 @@ func (cfg *Config) Core(log dex.Logger) *core.Config {
NoAutoWalletLock: cfg.NoAutoWalletLock,
NoAutoDBBackup: cfg.NoAutoDBBackup,
SimnetFiatRates: cfg.SimnetFiatRates,
ExtensionModeFile: cfg.ExtensionModeFile,
}
}

Expand Down
28 changes: 24 additions & 4 deletions client/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"encoding/binary"
"encoding/csv"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math"
Expand Down Expand Up @@ -1394,6 +1395,10 @@ type Config struct {
// UnlockCoinsOnLogin indicates that on wallet connect during login, or on
// creation of a new wallet, all coins with the wallet should be unlocked.
UnlockCoinsOnLogin bool
// ExtensionModeFile is the path to a file that specifies configuration
// for running core in extension mode, which gives the caller options for
// e.g. limiting the ability to configure wallets.
ExtensionModeFile string
}

// Core is the core client application. Core manages DEX connections, wallets,
Expand All @@ -1412,6 +1417,8 @@ type Core struct {
locale map[Topic]*translation
localePrinter *message.Printer

extensionModeConfig *ExtensionModeConfig

// construction or init sets credentials
credMtx sync.RWMutex
credentials *db.PrimaryCredentials
Expand Down Expand Up @@ -1509,7 +1516,7 @@ func New(cfg *Config) (*Core, error) {

locale, found := locales[lang.String()]
if !found {
return nil, fmt.Errorf("No translations for language %s", lang)
return nil, fmt.Errorf("no translations for language %s", lang)
}

// Try to get the primary credentials, but ignore no-credentials error here
Expand All @@ -1524,6 +1531,17 @@ func New(cfg *Config) (*Core, error) {
return nil, err
}

var xCfg *ExtensionModeConfig
if cfg.ExtensionModeFile != "" {
b, err := os.ReadFile(cfg.ExtensionModeFile)
if err != nil {
return nil, fmt.Errorf("error reading extension mode file at %q: %w", cfg.ExtensionModeFile, err)
}
if err := json.Unmarshal(b, &xCfg); err != nil {
return nil, fmt.Errorf("error unmarshalling extension mode file: %w", err)
}
}

c := &Core{
cfg: cfg,
credentials: creds,
Expand All @@ -1545,9 +1563,10 @@ func New(cfg *Config) (*Core, error) {
latencyQ: wait.NewTickerQueue(recheckInterval),
noteChans: make(map[uint64]chan Notification),

locale: locale,
localePrinter: message.NewPrinter(lang),
seedGenerationTime: seedGenerationTime,
locale: locale,
localePrinter: message.NewPrinter(lang),
extensionModeConfig: xCfg,
seedGenerationTime: seedGenerationTime,

fiatRateSources: make(map[string]*commonRateSource),
pendingWallets: make(map[uint32]bool),
Expand Down Expand Up @@ -2362,6 +2381,7 @@ func (c *Core) User() *User {
SeedGenerationTime: c.seedGenerationTime,
FiatRates: c.fiatConversions(),
Net: c.net,
ExtensionConfig: c.extensionModeConfig,
}
}

Expand Down
17 changes: 17 additions & 0 deletions client/core/example-extension-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"restrictedWallets": {
"dcr": {
"hiddenFields": [
"account",
"unmixedaccount",
"tradingaccount",
"username",
"password",
"rpclisten",
"rpccert"
],
"disableWalletType": true,
"disablePassword": true
}
}
}
19 changes: 19 additions & 0 deletions client/core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,24 @@ type WalletState struct {
Approved map[uint32]asset.ApprovalStatus `json:"approved"`
}

// ExtensionModeConfig is configuration for running core in extension mode,
// primarily for restricting certain wallet reconfiguration options.
type ExtensionModeConfig struct {
// RestrictedWallets are wallets that need restrictions on reconfiguration
// options.
RestrictedWallets map[string] /*symbol*/ struct {
// HiddenFields are configuration fields (asset.ConfigOption.Key) that
// should not be displayed to the user.
HiddenFields []string `json:"hiddenFields"`
// DisableWalletType indicates that we should not offer the user an
// an option to chznge the wallet type.
DisableWalletType bool `json:"disableWalletType"`
// DisablePassword indicates that we should not offer the user an option
// to change the wallet password.
DisablePassword bool `json:"disablePassword"`
} `json:"restrictedWallets"`
}

// User is information about the user's wallets and DEX accounts.
type User struct {
Exchanges map[string]*Exchange `json:"exchanges"`
Expand All @@ -146,6 +164,7 @@ type User struct {
Assets map[uint32]*SupportedAsset `json:"assets"`
FiatRates map[uint32]float64 `json:"fiatRates"`
Net dex.Network `json:"net"`
ExtensionConfig *ExtensionModeConfig `json:"extensionModeConfig,omitempty"`
}

// SupportedAsset is data about an asset and possibly the wallet associated
Expand Down
2 changes: 1 addition & 1 deletion client/webserver/site/src/html/bodybuilder.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
{{end}}

{{define "bottom"}}
<script src="/js/entry.js?v=028e1498|b813f78d"></script>
<script src="/js/entry.js?v=3834e0f1|347ca238"></script>
</body>
</html>
{{end}}
24 changes: 18 additions & 6 deletions client/webserver/site/src/js/forms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ export class NewWalletForm {
break
}
}
const displayCreateBtn = walletDef.seeded || Boolean(this.current.asset.token)
const { asset, parentAsset, winfo } = this.current
const displayCreateBtn = walletDef.seeded || Boolean(asset.token)
if (appPwCached && displayCreateBtn && !containsRequired) {
Doc.show(page.oneBttnBox)
} else if (displayCreateBtn) {
Expand All @@ -339,8 +340,6 @@ export class NewWalletForm {
page.submitAdd.textContent = intl.prep(intl.ID_ADD)
}

const { asset, parentAsset, winfo } = this.current

if (parentAsset) {
const parentAndTokenOpts = JSON.parse(JSON.stringify(configOpts))
// Add the regAsset field to the configurations so proper logos will be displayed
Expand All @@ -353,8 +352,8 @@ export class NewWalletForm {
for (const opt of tokenOptsCopy) opt.regAsset = asset.id
parentAndTokenOpts.push(...tokenOptsCopy)
}
this.subform.update(parentAndTokenOpts, false)
} else this.subform.update(configOpts, false)
this.subform.update(asset.id, parentAndTokenOpts, false)
} else this.subform.update(asset.id, configOpts, false)
this.setGuideLink(guideLink)

if (this.subform.dynamicOpts.children.length || this.subform.defaultSettings.children.length) {
Expand Down Expand Up @@ -442,6 +441,7 @@ export class WalletConfigForm {
defaultSettingsMsg: PageElement
defaultSettings: PageElement
assetHasActiveOrders: boolean
assetID: number

constructor (form: HTMLElement, sectionize: boolean) {
this.page = Doc.idDescendants(form)
Expand Down Expand Up @@ -531,6 +531,8 @@ export class WalletConfigForm {
})
if (!skipRepeatN) for (let i = 0; i < (opt.repeatN ? opt.repeatN - 1 : 0); i++) this.addOpt(box, opt, insertAfter, true)
} else el = this.textInputTmpl.cloneNode(true) as HTMLElement
const hiddenFields = app().user.extensionModeConfig?.restrictedWallets[baseChainSymbol(this.assetID)]?.hiddenFields || []
if (hiddenFields.indexOf(opt.key) !== -1) Doc.hide(el)
this.configElements.push([opt, el])
const input = el.querySelector('input') as ConfigOptionInput
input.dataset.configKey = opt.key
Expand Down Expand Up @@ -576,10 +578,11 @@ export class WalletConfigForm {
/*
* update creates the dynamic form.
*/
update (configOpts: ConfigOption[] | null, activeOrders: boolean) {
update (assetID: number, configOpts: ConfigOption[] | null, activeOrders: boolean) {
this.assetHasActiveOrders = activeOrders
this.configElements = []
this.configOpts = configOpts || []
this.assetID = assetID
Doc.empty(this.dynamicOpts, this.defaultSettings, this.loadedSettings)

// If there are no options, just hide the entire form.
Expand Down Expand Up @@ -1903,3 +1906,12 @@ function dateToString (date: Date) {
// Another common hack:
// date.toLocaleString("sv-SE", { year: "numeric", month: "2-digit", day: "2-digit" })
}

/*
* baseChainSymbol returns the symbol for the asset's parent if the asset is a
* token, otherwise the symbol for the asset itself.
*/
export function baseChainSymbol (assetID: number) {
const asset = app().user.assets[assetID]
return asset.token ? app().user.assets[asset.token.parentID].symbol : asset.symbol
}
11 changes: 11 additions & 0 deletions client/webserver/site/src/js/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,16 @@ export interface Denomination {
conversionFactor: number
}

export interface ExtensionConfiguredWallet {
hiddenFields: string[]
disableWalletType: boolean
disablePassword: boolean
}

export interface ExtensionModeConfig {
restrictedWallets: Record<string, ExtensionConfiguredWallet>
}

export interface User {
exchanges: Record<string, Exchange>
inited: boolean
Expand All @@ -297,6 +307,7 @@ export interface User {
ok: boolean // added by webserver
bots: BotReport[]
net: number
extensionModeConfig: ExtensionModeConfig
}

export interface CoreNote {
Expand Down
37 changes: 20 additions & 17 deletions client/webserver/site/src/js/wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
WalletConfigForm,
UnlockWalletForm,
DepositAddress,
bind as bindForm
bind as bindForm,
baseChainSymbol
} from './forms'
import State from './state'
import * as intl from './locales'
Expand Down Expand Up @@ -350,15 +351,16 @@ export default class WalletsPage extends BasePage {
* setPWSettingViz sets the visibility of the password field section.
*/
setPWSettingViz (visible: boolean) {
const page = this.page
if (visible) {
Doc.hide(this.page.showIcon)
Doc.show(this.page.hideIcon, this.page.changePW)
this.page.switchPWMsg.textContent = intl.prep(intl.ID_KEEP_WALLET_PASS)
Doc.hide(page.showIcon)
Doc.show(page.hideIcon, page.changePW)
page.switchPWMsg.textContent = intl.prep(intl.ID_KEEP_WALLET_PASS)
return
}
Doc.hide(this.page.hideIcon, this.page.changePW)
Doc.show(this.page.showIcon)
this.page.switchPWMsg.textContent = intl.prep(intl.ID_NEW_WALLET_PASS)
Doc.hide(page.hideIcon, page.changePW)
Doc.show(page.showIcon)
page.switchPWMsg.textContent = intl.prep(intl.ID_NEW_WALLET_PASS)
}

/*
Expand Down Expand Up @@ -1029,18 +1031,20 @@ export default class WalletsPage extends BasePage {
/* Show the form used to change wallet configuration settings. */
async showReconfig (assetID: number, skipAnimation?: boolean) {
const page = this.page
Doc.hide(page.changeWalletType, page.changeTypeHideIcon, page.reconfigErr, page.showChangeType, page.changeTypeHideIcon)
Doc.hide(page.reconfigErr)
Doc.hide(page.enableWallet, page.disableWallet)
Doc.hide(
page.changeWalletType, page.changeTypeHideIcon, page.reconfigErr,
page.showChangeType, page.changeTypeHideIcon, page.reconfigErr,
page.enableWallet, page.disableWallet
)
// Hide update password section by default
this.changeWalletPW = false
this.setPWSettingViz(this.changeWalletPW)
const asset = app().assets[assetID]

const currentDef = app().currentWalletDefinition(assetID)
const walletDefs = asset.token ? [asset.token.definition] : asset.info ? asset.info.availablewallets : []

if (walletDefs.length > 1) {
const disableWalletType = app().user.extensionModeConfig?.restrictedWallets[baseChainSymbol(assetID)]?.disableWalletType
if (walletDefs.length > 1 && !disableWalletType) {
Doc.empty(page.changeWalletTypeSelect)
Doc.show(page.showChangeType, page.changeTypeShowIcon)
page.changeTypeMsg.textContent = intl.prep(intl.ID_CHANGE_WALLET_TYPE)
Expand All @@ -1050,8 +1054,6 @@ export default class WalletsPage extends BasePage {
option.value = option.textContent = wDef.type
page.changeWalletTypeSelect.appendChild(option)
}
} else {
Doc.hide(page.showChangeType)
}

const wallet = app().walletMap[assetID]
Expand All @@ -1078,7 +1080,7 @@ export default class WalletsPage extends BasePage {
return
}
const assetHasActiveOrders = app().haveActiveOrders(assetID)
this.reconfigForm.update(currentDef.configopts || [], assetHasActiveOrders)
this.reconfigForm.update(asset.id, currentDef.configopts || [], assetHasActiveOrders)
this.setGuideLink(currentDef.guidelink)
this.reconfigForm.setConfig(res.map)
this.updateDisplayedReconfigFields(currentDef)
Expand All @@ -1088,7 +1090,7 @@ export default class WalletsPage extends BasePage {
const page = this.page
const walletType = page.changeWalletTypeSelect.value || ''
const walletDef = app().walletDefinition(this.selectedAssetID, walletType)
this.reconfigForm.update(walletDef.configopts || [], false)
this.reconfigForm.update(this.selectedAssetID, walletDef.configopts || [], false)
this.setGuideLink(walletDef.guidelink)
this.updateDisplayedReconfigFields(walletDef)
}
Expand All @@ -1102,7 +1104,8 @@ export default class WalletsPage extends BasePage {
}

updateDisplayedReconfigFields (walletDef: WalletDefinition) {
if (walletDef.seeded || walletDef.type === 'token') {
const disablePassword = app().user.extensionModeConfig?.restrictedWallets[baseChainSymbol(this.selectedAssetID)]?.disablePassword
if (walletDef.seeded || walletDef.type === 'token' || disablePassword) {
Doc.hide(this.page.showChangePW, this.reconfigForm.fileSelector)
this.changeWalletPW = false
this.setPWSettingViz(false)
Expand Down

0 comments on commit baa08e1

Please sign in to comment.