completed code and example

This commit is contained in:
dcore94 2024-07-04 11:20:11 +02:00
parent f574b323ce
commit 8748836d9f
2 changed files with 180 additions and 24 deletions

View File

@ -6,11 +6,11 @@
<script src="../boot/d4s-boot.js"></script>
</head>
<body>
<d4s-boot-2 context="_myextapp"
<d4s-boot-2 context="%2Fgcube%2Fdevsec%2FCCP"
gateway="next.d4science.org"
redirect-url="http://localhost:8080"
url="https://accounts.dev.d4science.org/auth">
</d4s-boot-2>
<d4s-extapp-role-manager appid="076bfe7d-12a2-41d1-a720-05911f0ae527"></d4s-extapp-role-manager>
<d4s-extapp-role-manager groupid="8131621c-f896-4d41-81fd-3f6294bf1376" appid="076bfe7d-12a2-41d1-a720-05911f0ae527"></d4s-extapp-role-manager>
</body>
</html>

View File

@ -3,35 +3,169 @@ class ExtAppRoleController extends HTMLElement{
#boot;
#roles;
#users;
#mappings;
#serviceurl;
#appid;
#groupid;
#loading = false;
#header;
#section;
#userfilter = null;
#rolefilter = null;
constructor(){
super()
this.#boot = document.querySelector("d4s-boot-2")
this.#serviceurl = this.#boot.url
this.attachShadow({ mode: "open" });
}
connectedCallback(){
this.#appid = this.getAttribute("appid")
this.#groupid = this.getAttribute("groupid")
if(!this.#loading){
this.#loading = true
this.loadData()
}
this.renderStructure()
}
renderStructure(){
this.shadowRoot.innerHTML = `
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"/>
<style>
.dyn-opacity{
opacity: .8;
transition: opacity .5s;
}
.dyn-opacity:hover{
opacity: 1;
}
</style>
<header></header>
<section></section>
`
this.#header = this.shadowRoot.querySelector("header")
this.#section = this.shadowRoot.querySelector("section")
}
renderHeader(){
this.#header.innerHTML = `
<div class="row g-0 mb-3" style="gap: 1rem;align-items:baseline;">
<input class="col-3 form-control" id="search_user_filter" placeholder="Search by name" style="min-width: 10rem;max-width: 100rem;width: 30rem">
<div class="role-filter col-9 d-flex" style="gap:1rem;">
${
this.#roles.map(r=>{
return r.name === 'uma_protection' ? '' : `
<div class="form-check form-switch">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" name="${r.id}">
<span>${r.name}</span>
</label>
</div>
`
}).join("")
}
</div>
</div>`
this.attachHeaderEvents()
}
renderUsers(){
var users = this.#rolefilter && this.#rolefilter.length ?
this.#users.filter(u=>{
const assigned = u.mappings.map(m=>m.id)
return this.#rolefilter.reduce((a,f)=>{
return a && (assigned.indexOf(f) !== -1)
}, true)
}) : this.#users
users = this.#userfilter ?
this.#users.filter(u=>{
return u.username.toLowerCase().startsWith(this.#userfilter) ||
u.lastName.toLowerCase().startsWith(this.#userfilter) ||
u.firstName.toLowerCase().startsWith(this.#userfilter) ||
u.email.toLowerCase().startsWith(this.#userfilter)}) : users
const html = `
<ul name="users" style="list-style:none" class="d-flex flex-row flex-wrap gap-2 p-0">
${users.map( u=>{
return `
<li name="user" data-userid="${u.id}" style="user-select:none">
<div class="card" style="width:20rem;height:10rem">
<div class="card-header">
<span name="name" class="fw-bold">${u.firstName} ${u.lastName}</span>
</div>
<div class="card-body d-flex" style="overflow-y:auto;padding:.5rem">
<ul name="roles" style="list-style:none;min-width:70%;overflow-y:auto;align-items: flex-start" class="d-flex flex-row flex-wrap gap-1 pt-1 pb-1 ps-0 me-2">
${this.renderMappings(u)}
</ul>
</divExtAppRoleController>
</div>
</li>`
}).join('')}
</li>
</ul>
`
this.#section.innerHTML = html
this.attachSectionEvents()
}
renderMappings(u){
const html = `
<ul name="roles" style="list-style:none;min-width:70%;overflow-y:auto" class="d-flex flex-row flex-wrap gap-1 pt-1 pb-1 ps-0 me-2">
${this.#roles ? this.#roles.map(r=>{
const assigned = this.isAssigned(u, r)
return r.name !== 'uma_protection' ? `
<li data-userid="${u.id}" data-roleid="${r.id}" name="role" class="dyn-opacity btn m-0 p-0 ${assigned ? 'active' : ''}">
<span style="pointer-events: none" name="name" class="badge bg-${assigned ? 'success' : 'secondary'}">${r.name}</span>
</li>` : ''
}).join('') : ''}
</ul>
`
return html
}
isAssigned(u, r){
return u.mappings && u.mappings.filter(m=>m.id === r.id).length > 0
}
attachSectionEvents(){
this.#section.querySelector("ul[name=users]").addEventListener("click", ev=>{
const tgt = ev.target
if(tgt.getAttribute("name") === "role"){
const uid = tgt.getAttribute("data-userid")
const rid = tgt.getAttribute("data-roleid")
if(tgt.classList.contains("active")){
this.toggleMapping(uid, rid)
}else{
this.toggleMapping(uid, rid, true)
}
}
})
}
attachHeaderEvents(){
this.#header.querySelector("#search_user_filter").addEventListener("input", ev=>{
this.#userfilter = ev.target.value.toLowerCase()
this.renderUsers()
})
this.#header.querySelector("div.role-filter").addEventListener("change", ev=>{
this.#rolefilter = Array.prototype.slice.call(ev.currentTarget.querySelectorAll("input:checked")).map(r=>r.name)
this.renderUsers()
})
}
loadData(){
Promise.all([
this.loadEligibleUsers(), this.loadRoles()
]).then(()=>{
console.log("Done. Ui should be complete now")
//console.log("Done. Ui should be complete now")
}).catch(err=>alert(err))
}
loadEligibleUsers(){
const url = this.#serviceurl + `/admin/realms/d4science/clients/${this.#appid}/roles/eligible/users?briefRepresentation=true&max=-1&first=0`
const url = this.#serviceurl + `/admin/realms/d4science/groups/${this.#groupid}/members?first=0&max=-1&briefRepresentation=true`
return this.#boot.secureFetch(url).then(resp=>{
if(resp.ok){
return resp.json()
@ -41,11 +175,10 @@ class ExtAppRoleController extends HTMLElement{
throw "Fetching users: Unspecified error"
}
}).then(json=>{
console.log("Fetched eligible users can show table")
this.#users = json
return loadMappings()
}).then(()=>{
console.log(this.#mappings)
const prom = this.loadMappings()
this.renderUsers()
return prom
})
}
@ -61,30 +194,53 @@ class ExtAppRoleController extends HTMLElement{
}
}).then(json=>{
this.#roles = json
console.log("Roles loaded, could draw header")
this.renderHeader()
})
}
loadMappings(){
return Promise.all(
this.#users.forEach(u => {
const url = this.#serviceurl + `/admin/realms/d4science/users/${u.id}/clients/${this.#appid}/roles`
this.#boot.secureFetch(url).then(resp=>{
if(resp.ok){
return resp.json()
}else if(resp.status === 403){
throw "Fetching role mappings: You are not allowed to manage roles for this application."
}else{
throw "Fetching role mappings: Unspecified error"
}
}).then(json=>{
u.mappings = json
console.log(`Fetched mappings for user ${u.id} can update the proper table`)
})
this.#users.map(u => {
return this.loadMapping(u)
})
)
}
loadMapping(u){
const url = this.#serviceurl + `/admin/realms/d4science/users/${u.id}/role-mappings/clients/${this.#appid}`
return this.#boot.secureFetch(url).then(resp=>{
if(resp.ok){
return resp.json()
}else if(resp.status === 403){
throw "Fetching role mappings: You are not allowed to manage roles for this application."
}else{
throw "Fetching role mappings: Unspecified error"
}
}).then(json=>{
u.mappings = json
this.renderUsers()
})
}
toggleMapping(uid, rid, add){
const url = this.#serviceurl + `/admin/realms/d4science/users/${uid}/role-mappings/clients/${this.#appid}`
const role = this.#roles.filter(r=>r.id === rid)
if(role.length !== 1){
alert("Something went wrong with looking up the role")
return
}
this.#boot.secureFetch(url, { method : add ? "POST" : "DELETE", body : JSON.stringify(role), headers : { "Content-Type" : "application/json"}}).then(resp=>{
if(resp.ok){
return resp.text()
}else if(resp.status === 403){
throw "Assigning or removing role: You are not allowed to manage roles for this application."
}else{
throw "Assigning or removing role: Unspecified error"
}
}).then(()=>{
return this.loadMapping(this.#users.filter(u=>u.id === uid)[0])
}).catch(err=>alert(err))
}
}
window.customElements.define('d4s-extapp-role-manager', ExtAppRoleController);