[feat] access control lists (with access tokens) (#2864)
This commit is contained in:
@@ -21,6 +21,60 @@ window.AccountPageLogic = {
|
||||
newPasswordRepeat: null,
|
||||
username: null,
|
||||
pubkey: null
|
||||
},
|
||||
apiAcl: {
|
||||
showNewAclDialog: false,
|
||||
showPasswordDialog: false,
|
||||
showNewTokenDialog: false,
|
||||
data: [],
|
||||
passwordGuardedFunction: null,
|
||||
newAclName: '',
|
||||
newTokenName: '',
|
||||
password: '',
|
||||
apiToken: null,
|
||||
selectedTokenId: null,
|
||||
columns: [
|
||||
{
|
||||
name: 'Name',
|
||||
align: 'left',
|
||||
label: this.$t('Name'),
|
||||
field: 'Name',
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
name: 'path',
|
||||
align: 'left',
|
||||
label: this.$t('path'),
|
||||
field: 'path',
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
name: 'read',
|
||||
align: 'left',
|
||||
label: this.$t('read'),
|
||||
field: 'read',
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
name: 'write',
|
||||
align: 'left',
|
||||
label: this.$t('write'),
|
||||
field: 'write',
|
||||
sortable: false
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
rowsPerPage: 100,
|
||||
page: 1
|
||||
}
|
||||
},
|
||||
selectedApiAcl: {
|
||||
id: null,
|
||||
name: null,
|
||||
endpoints: [],
|
||||
token_id_list: [],
|
||||
allRead: false,
|
||||
allWrite: false
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -151,6 +205,215 @@ window.AccountPageLogic = {
|
||||
newPassword: null,
|
||||
newPasswordRepeat: null
|
||||
}
|
||||
},
|
||||
newApiAclDialog() {
|
||||
this.apiAcl.newAclName = null
|
||||
this.apiAcl.showNewAclDialog = true
|
||||
},
|
||||
newTokenAclDialog() {
|
||||
this.apiAcl.newTokenName = null
|
||||
this.apiAcl.newTokenExpiry = null
|
||||
this.apiAcl.showNewTokenDialog = true
|
||||
},
|
||||
handleApiACLSelected(aclId) {
|
||||
this.selectedApiAcl = {
|
||||
id: null,
|
||||
name: null,
|
||||
endpoints: [],
|
||||
token_id_list: []
|
||||
}
|
||||
this.apiAcl.selectedTokenId = null
|
||||
if (!aclId) {
|
||||
return
|
||||
}
|
||||
setTimeout(() => {
|
||||
const selectedApiAcl = this.apiAcl.data.find(t => t.id === aclId)
|
||||
if (!this.selectedApiAcl) {
|
||||
return
|
||||
}
|
||||
this.selectedApiAcl = {...selectedApiAcl}
|
||||
this.selectedApiAcl.allRead = this.selectedApiAcl.endpoints.every(
|
||||
e => e.read
|
||||
)
|
||||
this.selectedApiAcl.allWrite = this.selectedApiAcl.endpoints.every(
|
||||
e => e.write
|
||||
)
|
||||
})
|
||||
},
|
||||
handleAllEndpointsReadAccess() {
|
||||
this.selectedApiAcl.endpoints.forEach(
|
||||
e => (e.read = this.selectedApiAcl.allRead)
|
||||
)
|
||||
},
|
||||
handleAllEndpointsWriteAccess() {
|
||||
this.selectedApiAcl.endpoints.forEach(
|
||||
e => (e.write = this.selectedApiAcl.allWrite)
|
||||
)
|
||||
},
|
||||
async getApiACLs() {
|
||||
try {
|
||||
const {data} = await LNbits.api.request('GET', '/api/v1/auth/acl', null)
|
||||
this.apiAcl.data = data.access_control_list
|
||||
} catch (e) {
|
||||
LNbits.utils.notifyApiError(e)
|
||||
}
|
||||
},
|
||||
askPasswordAndRunFunction(func) {
|
||||
this.apiAcl.passwordGuardedFunction = func
|
||||
this.apiAcl.showPasswordDialog = true
|
||||
},
|
||||
runPasswordGuardedFunction() {
|
||||
this.apiAcl.showPasswordDialog = false
|
||||
const func = this.apiAcl.passwordGuardedFunction
|
||||
if (func) {
|
||||
this[func]()
|
||||
}
|
||||
},
|
||||
async addApiACL() {
|
||||
if (!this.apiAcl.newAclName) {
|
||||
this.$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Name is required.'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const {data} = await LNbits.api.request(
|
||||
'PUT',
|
||||
'/api/v1/auth/acl',
|
||||
null,
|
||||
{
|
||||
id: this.apiAcl.newAclName,
|
||||
name: this.apiAcl.newAclName,
|
||||
password: this.apiAcl.password
|
||||
}
|
||||
)
|
||||
this.apiAcl.data = data.access_control_list
|
||||
const acl = this.apiAcl.data.find(
|
||||
t => t.name === this.apiAcl.newAclName
|
||||
)
|
||||
|
||||
this.handleApiACLSelected(acl.id)
|
||||
this.apiAcl.showNewAclDialog = false
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Access Control List created.'
|
||||
})
|
||||
} catch (e) {
|
||||
LNbits.utils.notifyApiError(e)
|
||||
} finally {
|
||||
this.apiAcl.name = ''
|
||||
this.apiAcl.password = ''
|
||||
}
|
||||
|
||||
this.apiAcl.showNewAclDialog = false
|
||||
},
|
||||
|
||||
async updateApiACLs() {
|
||||
try {
|
||||
const {data} = await LNbits.api.request(
|
||||
'PUT',
|
||||
'/api/v1/auth/acl',
|
||||
null,
|
||||
{
|
||||
id: this.user.id,
|
||||
password: this.apiAcl.password,
|
||||
...this.selectedApiAcl
|
||||
}
|
||||
)
|
||||
this.apiAcl.data = data.access_control_list
|
||||
} catch (e) {
|
||||
LNbits.utils.notifyApiError(e)
|
||||
} finally {
|
||||
this.apiAcl.password = ''
|
||||
}
|
||||
},
|
||||
async deleteApiACL() {
|
||||
if (!this.selectedApiAcl.id) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
await LNbits.api.request('DELETE', '/api/v1/auth/acl', null, {
|
||||
id: this.selectedApiAcl.id,
|
||||
password: this.apiAcl.password
|
||||
})
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Access Control List deleted.'
|
||||
})
|
||||
} catch (e) {
|
||||
LNbits.utils.notifyApiError(e)
|
||||
} finally {
|
||||
this.apiAcl.password = ''
|
||||
}
|
||||
this.apiAcl.data = this.apiAcl.data.filter(
|
||||
t => t.id !== this.selectedApiAcl.id
|
||||
)
|
||||
this.handleApiACLSelected(this.apiAcl.data[0]?.id)
|
||||
},
|
||||
async generateApiToken() {
|
||||
if (!this.selectedApiAcl.id) {
|
||||
return
|
||||
}
|
||||
const expirationTimeMilliseconds =
|
||||
new Date(this.apiAcl.newTokenExpiry) - new Date()
|
||||
try {
|
||||
const {data} = await LNbits.api.request(
|
||||
'POST',
|
||||
'/api/v1/auth/acl/token',
|
||||
null,
|
||||
{
|
||||
acl_id: this.selectedApiAcl.id,
|
||||
token_name: this.apiAcl.newTokenName,
|
||||
password: this.apiAcl.password,
|
||||
expiration_time_minutes: Math.trunc(
|
||||
expirationTimeMilliseconds / 60000
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
this.apiAcl.apiToken = data.api_token
|
||||
this.apiAcl.selectedTokenId = data.id
|
||||
Quasar.Notify.create({
|
||||
type: 'positive',
|
||||
message: 'Token Generated.'
|
||||
})
|
||||
|
||||
await this.getApiACLs()
|
||||
this.handleApiACLSelected(this.selectedApiAcl.id)
|
||||
this.apiAcl.showNewTokenDialog = false
|
||||
} catch (e) {
|
||||
LNbits.utils.notifyApiError(e)
|
||||
} finally {
|
||||
this.apiAcl.password = ''
|
||||
}
|
||||
},
|
||||
async deleteToken() {
|
||||
if (!this.apiAcl.selectedTokenId) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
await LNbits.api.request('DELETE', '/api/v1/auth/acl/token', null, {
|
||||
id: this.apiAcl.selectedTokenId,
|
||||
acl_id: this.selectedApiAcl.id,
|
||||
password: this.apiAcl.password
|
||||
})
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Token deleted.'
|
||||
})
|
||||
|
||||
this.selectedApiAcl.token_id_list =
|
||||
this.selectedApiAcl.token_id_list.filter(
|
||||
t => t.id !== this.apiAcl.selectedTokenId
|
||||
)
|
||||
this.apiAcl.selectedTokenId = null
|
||||
} catch (e) {
|
||||
LNbits.utils.notifyApiError(e)
|
||||
} finally {
|
||||
this.apiAcl.password = ''
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
@@ -166,5 +429,6 @@ window.AccountPageLogic = {
|
||||
if (hash) {
|
||||
this.tab = hash
|
||||
}
|
||||
await this.getApiACLs()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user