mirror of
https://github.com/9p4/jellyfin-plugin-sso.git
synced 2026-09-19 13:12:19 +00:00
Finish implementing linking view page
This commit is contained in:
@@ -1,8 +1,16 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
<!-- Polyfill styles that are missing when serving without dashboard -->
|
||||||
<link rel="stylesheet" href="emby-restyle.css" />
|
<link rel="stylesheet" href="emby-restyle.css" />
|
||||||
<link id="theme-style" rel="stylesheet" />
|
<link id="theme-style" rel="stylesheet" />
|
||||||
|
<!-- Polyfill material icons -->
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/material-design-icons.min.css"
|
||||||
|
integrity="sha256-YEkUHnLEghQI0YDx3I/r6cCeYYJHQEdrN+FN2PneTsk="
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
<script defer>
|
<script defer>
|
||||||
import("./ApiClient.js").then((clientModule) => {
|
import("./ApiClient.js").then((clientModule) => {
|
||||||
import("./linking.js").then((renderer) => {
|
import("./linking.js").then((renderer) => {
|
||||||
@@ -12,8 +20,9 @@
|
|||||||
"/web/themes/dark/theme.css"
|
"/web/themes/dark/theme.css"
|
||||||
);
|
);
|
||||||
|
|
||||||
document.querySelector("a.emby-button.home").href =
|
const homeButton = document.querySelector("a.emby-button.home");
|
||||||
ApiClient.serverAddress();
|
homeButton.href = ApiClient.serverAddress();
|
||||||
|
homeButton.style.display = "";
|
||||||
renderer.default(view);
|
renderer.default(view);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -30,7 +39,7 @@
|
|||||||
<div data-role="content">
|
<div data-role="content">
|
||||||
<div class="content-primary">
|
<div class="content-primary">
|
||||||
<div class="sectionTitleContainer flex align-items-center">
|
<div class="sectionTitleContainer flex align-items-center">
|
||||||
<a class="raised emby-button home">
|
<a class="raised emby-button home" style="display: none">
|
||||||
<span class="material-icons home" aria-hidden="true"></span
|
<span class="material-icons home" aria-hidden="true"></span
|
||||||
><span>Home</span>
|
><span>Home</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -44,9 +53,16 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
Use the following buttons to link your jellyfin account to any one
|
Use the
|
||||||
of the SSO providers. Please note that deletion is currently only
|
<span
|
||||||
available by API.
|
style="font-size: initial"
|
||||||
|
class="fab material-icons add"
|
||||||
|
aria-hidden="true"
|
||||||
|
></span>
|
||||||
|
button to create a new link for the given provider.
|
||||||
|
<br />
|
||||||
|
You may remove existing links by selecting them and pressing the
|
||||||
|
"Delete" button.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
See the
|
See the
|
||||||
@@ -65,9 +81,26 @@
|
|||||||
</a>
|
</a>
|
||||||
for more information.
|
for more information.
|
||||||
</p>
|
</p>
|
||||||
|
<label class="checkbox-wrapper">
|
||||||
|
<input is="emby-checkbox" id="enable-delete" type="checkbox" />
|
||||||
|
<span class="checkbox-label">Enable "Delete" button.</span>
|
||||||
|
</label>
|
||||||
|
<h1 class="sectionTitle">Link a provider</h1>
|
||||||
<div class="verticalSection" title="Link a provider">
|
<div class="verticalSection" title="Link a provider">
|
||||||
<div id="sso-provider-list"></div>
|
<h2 class="sectionTitle">SAML</h2>
|
||||||
|
<div id="sso-provider-list-saml" data-id="saml"></div>
|
||||||
|
<h2 class="sectionTitle">OID</h2>
|
||||||
|
<div id="sso-provider-list-oid" data-id="oid"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
id="btn-delete-selected-links"
|
||||||
|
type="button"
|
||||||
|
class="button-delete raised emby-button"
|
||||||
|
disabled="true"
|
||||||
|
>
|
||||||
|
<span class="material-icons delete" aria-hidden="true"></span>
|
||||||
|
<span>Delete Selected</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+153
-27
@@ -2,9 +2,69 @@ const ssoConfigLinking = {
|
|||||||
pluginUniqueId: "505ce9d1-d916-42fa-86ca-673ef241d7df",
|
pluginUniqueId: "505ce9d1-d916-42fa-86ca-673ef241d7df",
|
||||||
loadProviders: (view) => {
|
loadProviders: (view) => {
|
||||||
const provider_list_id = "sso-provider-list";
|
const provider_list_id = "sso-provider-list";
|
||||||
|
const provider_list_saml_id = `${provider_list_id}-saml`;
|
||||||
|
const provider_list_oid_id = `${provider_list_id}-oid`;
|
||||||
|
|
||||||
var provider_list = view.querySelector("#" + provider_list_id);
|
const provider_list_saml = view.querySelector(`#${provider_list_saml_id}`);
|
||||||
provider_list.innerHTML = "";
|
const provider_list_oid = view.querySelector(`#${provider_list_oid_id}`);
|
||||||
|
provider_list_saml.innerHTML = "";
|
||||||
|
provider_list_oid.innerHTML = "";
|
||||||
|
|
||||||
|
fetch(new Request(ApiClient.getUrl("sso/OID/GetNames"))).then((resp) => {
|
||||||
|
resp.json().then((config_names) => {
|
||||||
|
ssoConfigLinking.loadProviderList(
|
||||||
|
provider_list_oid,
|
||||||
|
config_names,
|
||||||
|
"oid"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
fetch(new Request(ApiClient.getUrl("sso/SAML/GetNames"))).then((resp) => {
|
||||||
|
resp.json().then((config_names) => {
|
||||||
|
ssoConfigLinking.loadProviderList(
|
||||||
|
provider_list_saml,
|
||||||
|
config_names,
|
||||||
|
"saml"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadProviderList: (container, providers, provider_mode) => {
|
||||||
|
providers.forEach((provider_name) => {
|
||||||
|
var provider_config = document.createElement("div");
|
||||||
|
provider_config.classList.add("sso-provider-links-container");
|
||||||
|
provider_config.setAttribute("data-id", provider_name);
|
||||||
|
|
||||||
|
provider_config.innerHTML = `
|
||||||
|
<label
|
||||||
|
class="inputLabel inputLabelUnfocused sso-provider-link-title"
|
||||||
|
>${provider_name}
|
||||||
|
</label>
|
||||||
|
<a
|
||||||
|
class="fab emby-button sso-provider-add-link"
|
||||||
|
>
|
||||||
|
<span class="material-icons add" aria-hidden="true"></span>
|
||||||
|
</a>
|
||||||
|
<div
|
||||||
|
class="sso-provider-existing-links-container"
|
||||||
|
data-provider="${provider_name}"
|
||||||
|
></div>
|
||||||
|
`;
|
||||||
|
var add_provider = provider_config.querySelector(
|
||||||
|
".sso-provider-add-link"
|
||||||
|
);
|
||||||
|
|
||||||
|
//const provider_name_css = ssoConfigLinking.safeCSSId(provider_name);
|
||||||
|
//provider_link.id = "sso-provider-" + provider_name_css;
|
||||||
|
//provider_link.classList.add("sso-provider-" + provider_name_css);
|
||||||
|
add_provider.classList.add("sso-provider");
|
||||||
|
|
||||||
|
add_provider.href = ApiClient.getUrl(
|
||||||
|
`/SSO/${provider_mode}/p/${provider_name}?isLinking=true`
|
||||||
|
);
|
||||||
|
|
||||||
|
container.appendChild(provider_config);
|
||||||
|
});
|
||||||
|
|
||||||
const currentUserId = ApiClient.getCurrentUserId();
|
const currentUserId = ApiClient.getCurrentUserId();
|
||||||
|
|
||||||
@@ -12,49 +72,115 @@ const ssoConfigLinking = {
|
|||||||
ApiClient.fetch(
|
ApiClient.fetch(
|
||||||
{
|
{
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: ApiClient.getUrl(`sso/OID/links/${currentUserId}`),
|
url: ApiClient.getUrl(`sso/${provider_mode}/links/${currentUserId}`),
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
).then((resp) => {
|
).then((resp) => {
|
||||||
resp.json().then((x) => console.log(x));
|
resp.json().then((provider_map) => {
|
||||||
|
console.log({ provider_map, currentUserId });
|
||||||
|
|
||||||
|
Object.keys(provider_map).forEach((provider_name) => {
|
||||||
|
const provider_container = container.querySelector(
|
||||||
|
`.sso-provider-existing-links-container[data-provider="${provider_name}"]`
|
||||||
|
);
|
||||||
|
ssoConfigLinking.populateExistingLinks(
|
||||||
|
provider_container,
|
||||||
|
provider_mode,
|
||||||
|
provider_name,
|
||||||
|
provider_map[provider_name]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
fetch(new Request(ApiClient.getUrl("sso/OID/GetNames"))).then((resp) => {
|
populateExistingLinks: (
|
||||||
resp.json().then((config_names) => {
|
container,
|
||||||
ssoConfigLinking.loadProviderList(provider_list, config_names, "oid");
|
provider_mode,
|
||||||
});
|
provider_name,
|
||||||
|
canonical_names
|
||||||
|
) => {
|
||||||
|
container
|
||||||
|
.querySelectorAll(".sso-provider-link-checkbox-wrapper")
|
||||||
|
.forEach((e) => e.remove());
|
||||||
|
|
||||||
|
const checkboxes = canonical_names.map((canonical_name) => {
|
||||||
|
var out = document.createElement("label");
|
||||||
|
out.classList.add("sso-provider-link-checkbox-wrapper");
|
||||||
|
out.classList.add("checkbox-wrapper");
|
||||||
|
out.innerHTML = `
|
||||||
|
<input
|
||||||
|
is="emby-checkbox"
|
||||||
|
class="sso-link-checkbox"
|
||||||
|
data-id="${canonical_name}"
|
||||||
|
data-mode="${provider_mode}"
|
||||||
|
data-provider="${provider_name}"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
<span class="checkbox-label">${canonical_name}</span>
|
||||||
|
`;
|
||||||
|
return out;
|
||||||
});
|
});
|
||||||
fetch(new Request(ApiClient.getUrl("sso/SAML/GetNames"))).then((resp) => {
|
|
||||||
resp.json().then((config_names) => {
|
checkboxes.forEach((e) => {
|
||||||
ssoConfigLinking.loadProviderList(provider_list, config_names, "saml");
|
container.appendChild(e);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadProviderList: (element, providers, provider_mode) => {
|
|
||||||
providers.forEach((provider_name) => {
|
|
||||||
var provider_link = document.createElement("a");
|
|
||||||
|
|
||||||
provider_link.classList.add("raised");
|
handleDeleteButtonPressed: (evt, view) => {
|
||||||
provider_link.classList.add("block");
|
if (evt.target.disabled) return;
|
||||||
provider_link.classList.add("emby-button");
|
|
||||||
|
|
||||||
//const provider_name_css = ssoConfigLinking.safeCSSId(provider_name);
|
const currentUserId = ApiClient.getCurrentUserId();
|
||||||
//provider_link.id = "sso-provider-" + provider_name_css;
|
if (!currentUserId) return;
|
||||||
//provider_link.classList.add("sso-provider-" + provider_name_css);
|
|
||||||
provider_link.classList.add("sso-provider");
|
|
||||||
|
|
||||||
provider_link.text = provider_name;
|
const delete_requests = [...view.querySelectorAll(".sso-link-checkbox")]
|
||||||
|
.filter((checkbox_link) => {
|
||||||
|
const canonical_name = checkbox_link.getAttribute("data-id");
|
||||||
|
const provider_name = checkbox_link.getAttribute("data-provider");
|
||||||
|
const provider_mode = checkbox_link.getAttribute("data-mode");
|
||||||
|
|
||||||
provider_link.href = ApiClient.getUrl(
|
if (![canonical_name, provider_name, provider_mode].every((e) => e)) {
|
||||||
`/SSO/${provider_mode}/p/${provider_name}?isLinking=true`
|
return false;
|
||||||
);
|
}
|
||||||
|
|
||||||
element.appendChild(provider_link);
|
if (!checkbox_link.checked) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((checked_link) => {
|
||||||
|
const canonical_name = checked_link.getAttribute("data-id");
|
||||||
|
const provider_name = checked_link.getAttribute("data-provider");
|
||||||
|
const provider_mode = checked_link.getAttribute("data-mode");
|
||||||
|
|
||||||
|
return ApiClient.fetch({
|
||||||
|
type: "DELETE",
|
||||||
|
url: ApiClient.getUrl(
|
||||||
|
`sso/${provider_mode}/link/${provider_name}/${currentUserId}/${canonical_name}`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all(delete_requests).then((values) => {
|
||||||
|
console.log({ message: "Delete requests handled", values });
|
||||||
|
window.location.reload();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function (view) {
|
export default function (view) {
|
||||||
ssoConfigLinking.loadProviders(view);
|
ssoConfigLinking.loadProviders(view);
|
||||||
|
|
||||||
|
view.querySelector("#enable-delete").addEventListener("change", (e) => {
|
||||||
|
view.querySelector("#btn-delete-selected-links").disabled =
|
||||||
|
!e.target.checked;
|
||||||
|
});
|
||||||
|
|
||||||
|
view
|
||||||
|
.querySelector("#btn-delete-selected-links")
|
||||||
|
.addEventListener("click", (e) =>
|
||||||
|
ssoConfigLinking.handleDeleteButtonPressed(e, view)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
theme.css
|
||||||
|
*/
|
||||||
|
.button-delete {
|
||||||
|
background: rgb(247, 0, 0);
|
||||||
|
color: rgba(255, 255, 255, 0.87);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-delete:disabled {
|
||||||
|
background: rgb(105, 0, 0);
|
||||||
|
color: rgba(127, 127, 127, 0.87);
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Emby Button
|
Emby Button
|
||||||
*/
|
*/
|
||||||
@@ -370,3 +385,65 @@ form {
|
|||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fix checkboxes */
|
||||||
|
/* Customize the label (the container) */
|
||||||
|
*/ .checkbox-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-wrapper [type="checkbox"] {
|
||||||
|
/*display: none;*/
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 400;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label:before,
|
||||||
|
.checkbox-label:after {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label:before {
|
||||||
|
display: flex;
|
||||||
|
content: " ";
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
border: 0.14em solid white;
|
||||||
|
border-radius: 0.14em;
|
||||||
|
/* background: #fff; */
|
||||||
|
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label:after {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
content: " ";
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
border: 0.14em solid white;
|
||||||
|
border-radius: 0.14em;
|
||||||
|
background: none;
|
||||||
|
font-family: Material Icons;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-wrapper input[type="checkbox"]:checked + .checkbox-label:after {
|
||||||
|
background-color: #2196f3;
|
||||||
|
content: "\e5ca";
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user