Skip to content

Commit b7d728b

Browse files
committed
converted trustedApplications to test out conventions
1 parent 4c7cc5f commit b7d728b

File tree

7 files changed

+222
-235
lines changed

7 files changed

+222
-235
lines changed

index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ if (typeof window !== 'undefined') {
4747

4848
let register = panes.register
4949

50+
register(require('./trustedApplications/trustedApplicationsPane').default) // manage your trusted applications
51+
5052
register(require('./markdown/index.tsx').Pane)
5153

5254
register(require('issue-pane'))
@@ -132,7 +134,6 @@ register(require('./internalPane.js'))
132134
// The home pane is a 2016 experiment. Always there.
133135

134136
register(require('./profile/profilePane').default) // edit your public profile
135-
register(require('./trustedApplications/trustedApplicationsPane').default) // manage your trusted applications
136137
register(require('./home/homePane').default)
137138

138139
// ENDS
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { S } from 'surplus'
2+
import { DataSignal } from 's-js/src/S'
3+
4+
import solidUi from 'solid-ui'
5+
import $rdf, { NamedNode } from 'rdflib'
6+
import { TrustedApplication } from './trustedApplications.models'
7+
import { getStatementsToAdd, getStatementsToDelete } from './trustedApplications.service'
8+
9+
const { authn, ns, store } = solidUi
10+
11+
export class TrustedApplicationsController {
12+
public isLoading: DataSignal<boolean>
13+
public isLoggedIn: DataSignal<boolean>
14+
public isEditable: DataSignal<boolean>
15+
public applications: DataSignal<TrustedApplication[]>
16+
public newApplication: DataSignal<TrustedApplication>
17+
18+
constructor (public subject: NamedNode) {
19+
this.isLoading = S.value(true)
20+
this.isLoggedIn = S.value(false)
21+
this.isEditable = S.value(false)
22+
this.applications = S.data([])
23+
this.newApplication = S.value(TrustedApplication.createNew(subject))
24+
25+
authn.solidAuthClient.currentSession()
26+
.then((session: any) => {
27+
this.isLoggedIn(!!session)
28+
this.isEditable(!!store.updater.editable(subject.doc().uri, store))
29+
this.isLoading(false)
30+
31+
const applications = store.each(subject, ns.acl('trustedApp'), undefined, undefined)
32+
.flatMap((app: any) => {
33+
return store.each(app, ns.acl('origin'), undefined, undefined)
34+
.map((origin: any) => ({ appModes: store.each(app, ns.acl('mode'), undefined, undefined), origin }))
35+
})
36+
.map(({ appModes, origin }: {appModes: NamedNode[], origin: NamedNode}) => TrustedApplication.fromNamedNodes(subject, origin, appModes))
37+
.sort(this.sortApplications)
38+
this.applications(applications)
39+
})
40+
.catch((err: any) => {
41+
this.isLoggedIn(false)
42+
this.isLoading(false)
43+
console.error('Error fetching currentSession:', err)
44+
})
45+
}
46+
47+
addOrEditApplication (appToAddOrEdit: TrustedApplication): void {
48+
let origin
49+
try {
50+
origin = $rdf.sym(appToAddOrEdit.origin())
51+
} catch (err) {
52+
return alert('Please provide an application URL you want to trust')
53+
}
54+
55+
const modes = appToAddOrEdit.modes
56+
.filter(checkbox => checkbox.isChecked())
57+
.map(checkbox => checkbox.value)
58+
59+
const deletions = getStatementsToDelete(origin, this.subject, store, ns)
60+
const additions = getStatementsToAdd(origin, this.generateRandomString(), modes, this.subject, ns)
61+
store.updater.update(deletions, additions, () => {
62+
let applications = this.applications()
63+
const addedOrUpdatedApp = TrustedApplication.copy(appToAddOrEdit)
64+
const index = applications.findIndex(app => app.origin() === appToAddOrEdit.origin())
65+
if (index === -1) {
66+
applications.push(addedOrUpdatedApp)
67+
} else {
68+
applications.splice(index, 1, addedOrUpdatedApp)
69+
}
70+
this.applications(applications.sort(this.sortApplications))
71+
this.newApplication(TrustedApplication.createNew(this.subject))
72+
})
73+
}
74+
75+
removeApplication (appToRemove: TrustedApplication): void {
76+
let originToRemove
77+
try {
78+
originToRemove = $rdf.sym(appToRemove.origin())
79+
} catch (err) {
80+
return alert('Please provide an application URL you want to remove trust from')
81+
}
82+
83+
const deletions = getStatementsToDelete(originToRemove, this.subject, store, ns)
84+
store.updater.update(deletions, null, () => {
85+
const applications = this.applications().filter(app => app.origin() !== appToRemove.origin())
86+
this.applications(applications)
87+
})
88+
}
89+
90+
private generateRandomString (): string {
91+
return Math.random().toString(36).substring(7)
92+
}
93+
94+
private sortApplications (a: TrustedApplication, b: TrustedApplication): number {
95+
return a.origin() > b.origin() ? 1 : -1
96+
}
97+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { NamedNode } from 'rdflib'
2+
import S, { DataSignal } from 's-js'
3+
import solidUi from 'solid-ui'
4+
5+
const { ns } = solidUi
6+
7+
export class TrustedApplication {
8+
public isNew: boolean
9+
public subject: DataSignal<string>
10+
public origin: DataSignal<string>
11+
public modes: Mode[]
12+
13+
private constructor (_subject: string, _origin: string, _modes: string[]) {
14+
this.subject = S.value(_subject)
15+
this.isNew = !_origin
16+
this.origin = S.value(_origin)
17+
this.modes = ['Read', 'Write', 'Append', 'Control'].map(mode => ({
18+
name: mode,
19+
value: ns.acl(mode),
20+
isChecked: S.value(_modes.some(appMode => appMode === ns.acl(mode).value))
21+
}))
22+
}
23+
24+
static createNew (_subject: NamedNode): TrustedApplication {
25+
return new TrustedApplication(_subject.value, '', [ns.acl('Read').value])
26+
}
27+
28+
static fromNamedNodes (_subject: NamedNode, _origin: NamedNode | null, _modes: NamedNode[]): TrustedApplication {
29+
return new TrustedApplication(_subject.value, _origin ? _origin.value : '', _modes.map(mode => mode.value))
30+
}
31+
32+
static copy (app: TrustedApplication): TrustedApplication {
33+
const modes = app.modes.filter(mode => mode.isChecked()).map(mode => ns.acl(mode.name).value)
34+
return new TrustedApplication(app.subject(), app.origin(), modes)
35+
}
36+
}
37+
38+
type Mode = {
39+
name: string
40+
value: string
41+
isChecked: DataSignal<boolean>
42+
}
File renamed without changes.

trustedApplications/trustedApplications.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-env jest */
22
const $rdf = require('rdflib')
33
const ns = require('solid-namespace')($rdf)
4-
const { getStatementsToDelete, getStatementsToAdd } = require('./trustedApplicationsUtils')
4+
const { getStatementsToDelete, getStatementsToAdd } = require('./trustedApplications.service')
55

66
describe('getStatementsToDelete', () => {
77
it('should return an empty array when there are no statements', () => {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import * as Surplus from 'surplus'
2+
import { TrustedApplicationsController } from './trustedApplications.controller'
3+
import solidUi from 'solid-ui'
4+
import data from 'surplus-mixin-data'
5+
import { TrustedApplication } from './trustedApplications.models'
6+
7+
const { S } = Surplus
8+
const { widgets } = solidUi
9+
10+
export const TrustedApplicationsView = (controller: TrustedApplicationsController) =>
11+
<section class={'trusted-applications-pane'}
12+
style={{ border: '0.3em solid #418d99', borderRadius: '0.5em', padding: '0.7em', marginTop: '0.7em' }}>
13+
{controller.isLoading()
14+
? <div>Profile is loading...</div>
15+
: controller.isLoggedIn()
16+
? <div>
17+
<h3>Manage your trusted applications</h3>
18+
{controller.isEditable()
19+
? [
20+
<p>Here you can manage the applications you trust.</p>,
21+
<table class={'results'}>
22+
<tr>
23+
<th>Application URL</th>
24+
<th>Access modes</th>
25+
<th>Actions</th>
26+
</tr>
27+
{controller.applications().map(app => applicationRow(controller, app))}
28+
{applicationRow(controller, controller.newApplication())}
29+
</table>,
30+
<h4>Notes</h4>,
31+
<ol>
32+
<li>Trusted applications will get access to all resources that you have access to.</li>
33+
<li>You can limit which modes they have by default.</li>
34+
<li>They will not gain more access than you have.</li>
35+
</ol>,
36+
<p>Application URLs must be valid URL. Examples are http://localhost:3000, https://trusted.app, and
37+
https://sub.trusted.app.</p>
38+
]
39+
: widgets.errorMessageBlock(document, `Your profile ${controller.subject.doc().uri} is not editable, so we cannot do much here.`)}
40+
</div>
41+
: <div>You are not logged in</div>}
42+
</section>
43+
44+
function applicationRow (controller: TrustedApplicationsController, app: TrustedApplication) {
45+
return <tr>
46+
<td>
47+
<input class={'textinput'} placeholder={'Write new URL here'} fn={data(app.origin)}/>
48+
</td>
49+
<td>
50+
{app.modes.map(mode => {
51+
return <label>
52+
<input type={'checkbox'} fn={data(mode.isChecked)}/>
53+
<span>{mode.name}</span>
54+
</label>
55+
})}
56+
</td>
57+
<td>
58+
{
59+
app.isNew
60+
? <button class={'controlButton'}
61+
style={{ background: 'LightGreen' }}
62+
onClick={() => controller.addOrEditApplication(app)}>Add</button>
63+
: [
64+
<button class={'controlButton'}
65+
style={{ background: 'LightGreen' }}
66+
onClick={() => controller.addOrEditApplication(app)}>Update</button>,
67+
<button class={'controlButton'}
68+
style={{ background: 'LightCoral' }}
69+
onClick={() => controller.removeApplication(app)}>Delete</button>
70+
]
71+
}
72+
</td>
73+
</tr>
74+
}

0 commit comments

Comments
 (0)