diff --git a/jsx/src/components/ServerDashboard/ServerDashboard.jsx b/jsx/src/components/ServerDashboard/ServerDashboard.jsx index e48be0c0..9b08f0fa 100644 --- a/jsx/src/components/ServerDashboard/ServerDashboard.jsx +++ b/jsx/src/components/ServerDashboard/ServerDashboard.jsx @@ -10,6 +10,14 @@ import "./server-dashboard.css"; import { timeSince } from "../../util/timeSince"; import PaginationFooter from "../PaginationFooter/PaginationFooter"; +const AccessServerButton = ({ userName, serverName }) => ( + + + +); + const ServerDashboard = (props) => { // sort methods var usernameDesc = (e) => e.sort((a, b) => (a.name > b.name ? 1 : -1)), @@ -29,6 +37,7 @@ const ServerDashboard = (props) => { var [errorAlert, setErrorAlert] = useState(null); var [sortMethod, setSortMethod] = useState(null); + var [disabledButtons, setDisabledButtons] = useState({}); var user_data = useSelector((state) => state.user_data), user_page = useSelector((state) => state.user_page), @@ -72,6 +81,101 @@ const ServerDashboard = (props) => { user_data = sortMethod(user_data); } + const StopServerButton = ({ serverName, userName }) => { + var [isDisabled, setIsDisabled] = useState(false); + return ( + + ); + }; + + const StartServerButton = ({ serverName, userName }) => { + var [isDisabled, setIsDisabled] = useState(false); + return ( + + ); + }; + + const EditUserCell = ({ user, numServers, serverName }) => { + if (serverName) return null; + return ( + + + + ); + }; + return (
{errorAlert != null ? ( @@ -115,6 +219,14 @@ const ServerDashboard = (props) => { testid="admin-sort" /> + + Server{" "} + setSortMethod(() => method)} + testid="server-sort" + /> + Last Activity{" "} { - {user_data.map((e, i) => ( - - {e.name} - {e.admin ? "admin" : ""} - - {e.last_activity ? timeSince(e.last_activity) : "Never"} - - - {e.server != null ? ( - // Stop Single-user server - - ) : ( - // Start Single-user server - - )} - - - {/* Edit User */} - - - - ))} + {user_data.flatMap((e, i) => { + let userServers = Object.values({ + "": e.server, + ...(e.servers || {}), + }); + return userServers.map((server) => { + server = { name: "", ...server }; + return ( + + {!server.name && ( + + {e.name} + + )} + {!server.name && ( + + {e.admin ? "admin" : ""} + + )} + + + {server.name ? ( +

{server.name}

+ ) : ( +

[MAIN]

+ )} + + + {server.last_activity + ? timeSince(server.last_activity) + : "Never"} + + + {server.started ? ( + // Stop Single-user server + <> + + + + ) : ( + // Start Single-user server + <> + + + + + + )} + + + + ); + }); + })} ({ (data) => data.json() ), shutdownHub: () => jhapiRequest("/shutdown", "POST"), - startServer: (name) => jhapiRequest("/users/" + name + "/server", "POST"), - stopServer: (name) => jhapiRequest("/users/" + name + "/server", "DELETE"), + startServer: (name, serverName = "") => + jhapiRequest("/users/" + name + "/servers/" + (serverName || ""), "POST"), + stopServer: (name, serverName = "") => + jhapiRequest("/users/" + name + "/servers/" + (serverName || ""), "DELETE"), startAll: (names) => names.map((e) => jhapiRequest("/users/" + e + "/server", "POST")), stopAll: (names) => diff --git a/jsx/yarn.lock b/jsx/yarn.lock index ae18c53b..85d2da54 100644 --- a/jsx/yarn.lock +++ b/jsx/yarn.lock @@ -3664,9 +3664,9 @@ flatted@^3.1.0: integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== follow-redirects@^1.0.0: - version "1.14.7" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" - integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + version "1.14.8" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" + integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== for-in@^1.0.2: version "1.0.2" diff --git a/jupyterhub/auth.py b/jupyterhub/auth.py index 1ac38988..585d5242 100644 --- a/jupyterhub/auth.py +++ b/jupyterhub/auth.py @@ -975,16 +975,24 @@ class PAMAuthenticator(LocalAuthenticator): ).tag(config=True) open_sessions = Bool( - True, + False, help=""" Whether to open a new PAM session when spawners are started. - This may trigger things like mounting shared filsystems, - loading credentials, etc. depending on system configuration, - but it does not always work. + This may trigger things like mounting shared filesystems, + loading credentials, etc. depending on system configuration. + + The lifecycle of PAM sessions is not correct, + so many PAM session configurations will not work. If any errors are encountered when opening/closing PAM sessions, this is automatically set to False. + + .. versionchanged:: 2.2 + + Due to longstanding problems in the session lifecycle, + this is now disabled by default. + You may opt-in to opening sessions by setting this to True. """, ).tag(config=True) diff --git a/jupyterhub/handlers/pages.py b/jupyterhub/handlers/pages.py index e7921ff1..3a063f62 100644 --- a/jupyterhub/handlers/pages.py +++ b/jupyterhub/handlers/pages.py @@ -255,7 +255,7 @@ class SpawnHandler(BaseHandler): self.log.debug( "Triggering spawn with supplied form options for %s", spawner._log_name ) - options = await maybe_future(spawner.options_from_form(form_options)) + options = await maybe_future(spawner.run_options_from_form(form_options)) pending_url = self._get_pending_url(user, server_name) return await self._wrap_spawn_single_user( user, server_name, spawner, pending_url, options diff --git a/jupyterhub/roles.py b/jupyterhub/roles.py index 75545a9a..5bba4d45 100644 --- a/jupyterhub/roles.py +++ b/jupyterhub/roles.py @@ -403,6 +403,10 @@ def _token_allowed_role(db, token, role): if owner is None: raise ValueError(f"Owner not found for {token}") + if role in owner.roles: + # shortcut: token is assigned an exact role the owner has + return True + expanded_scopes = _get_subscopes(role, owner=owner) implicit_permissions = {'inherit', 'read:inherit'} diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 0fd7785f..5f445096 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -11,6 +11,7 @@ import shutil import signal import sys import warnings +from inspect import signature from subprocess import Popen from tempfile import mkdtemp from urllib.parse import urlparse @@ -424,6 +425,13 @@ class Spawner(LoggingConfigurable): def _default_options_from_form(self, form_data): return form_data + def run_options_from_form(self, form_data): + sig = signature(self.options_from_form) + if 'spawner' in sig.parameters: + return self.options_from_form(form_data, spawner=self) + else: + return self.options_from_form(form_data) + def options_from_query(self, query_data): """Interpret query arguments passed to /spawn diff --git a/jupyterhub/tests/test_spawner.py b/jupyterhub/tests/test_spawner.py index 1cbf398c..bf59a4eb 100644 --- a/jupyterhub/tests/test_spawner.py +++ b/jupyterhub/tests/test_spawner.py @@ -459,3 +459,27 @@ async def test_spawner_oauth_roles_bad(app, user): # raises ValueError if we try to assign a role that doesn't exist with pytest.raises(ValueError): await spawner.user.spawn() + + +async def test_spawner_options_from_form(db): + def options_from_form(form_data): + return form_data + + spawner = new_spawner(db, options_from_form=options_from_form) + form_data = {"key": ["value"]} + result = spawner.run_options_from_form(form_data) + for key, value in form_data.items(): + assert key in result + assert result[key] == value + + +async def test_spawner_options_from_form_with_spawner(db): + def options_from_form(form_data, spawner): + return form_data + + spawner = new_spawner(db, options_from_form=options_from_form) + form_data = {"key": ["value"]} + result = spawner.run_options_from_form(form_data) + for key, value in form_data.items(): + assert key in result + assert result[key] == value diff --git a/share/jupyterhub/static/js/admin-react.js b/share/jupyterhub/static/js/admin-react.js index acbfd48e..68c3f22c 100644 --- a/share/jupyterhub/static/js/admin-react.js +++ b/share/jupyterhub/static/js/admin-react.js @@ -1,2 +1,2 @@ /*! For license information please see admin-react.js.LICENSE.txt */ -(()=>{var e,t,n={733:(e,t,n)=>{"use strict";var r=n(294),a=n(935),o=n(697),l=n.n(o),i=r.createContext(null),u=function(e){e()},c=function(){return u},s={notify:function(){}},f=function(){function e(e,t){this.store=e,this.parentSub=t,this.unsubscribe=null,this.listeners=s,this.handleChangeWrapper=this.handleChangeWrapper.bind(this)}var t=e.prototype;return t.addNestedSub=function(e){return this.trySubscribe(),this.listeners.subscribe(e)},t.notifyNestedSubs=function(){this.listeners.notify()},t.handleChangeWrapper=function(){this.onStateChange&&this.onStateChange()},t.isSubscribed=function(){return Boolean(this.unsubscribe)},t.trySubscribe=function(){this.unsubscribe||(this.unsubscribe=this.parentSub?this.parentSub.addNestedSub(this.handleChangeWrapper):this.store.subscribe(this.handleChangeWrapper),this.listeners=function(){var e=c(),t=null,n=null;return{clear:function(){t=null,n=null},notify:function(){e((function(){for(var e=t;e;)e.callback(),e=e.next}))},get:function(){for(var e=[],n=t;n;)e.push(n),n=n.next;return e},subscribe:function(e){var r=!0,a=n={callback:e,next:null,prev:n};return a.prev?a.prev.next=a:t=a,function(){r&&null!==t&&(r=!1,a.next?a.next.prev=a.prev:n=a.prev,a.prev?a.prev.next=a.next:t=a.next)}}}}())},t.tryUnsubscribe=function(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null,this.listeners.clear(),this.listeners=s)},e}();const d=function(e){var t=e.store,n=e.context,a=e.children,o=(0,r.useMemo)((function(){var e=new f(t);return e.onStateChange=e.notifyNestedSubs,{store:t,subscription:e}}),[t]),l=(0,r.useMemo)((function(){return t.getState()}),[t]);(0,r.useEffect)((function(){var e=o.subscription;return e.trySubscribe(),l!==t.getState()&&e.notifyNestedSubs(),function(){e.tryUnsubscribe(),e.onStateChange=null}}),[o,l]);var u=n||i;return r.createElement(u.Provider,{value:o},a)};n(679),n(864);var p="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?r.useLayoutEffect:r.useEffect,h=n(121),m=function(){return Math.random().toString(36).substring(7).split("").join(".")},v={INIT:"@@redux/INIT"+m(),REPLACE:"@@redux/REPLACE"+m(),PROBE_UNKNOWN_ACTION:function(){return"@@redux/PROBE_UNKNOWN_ACTION"+m()}};function y(e){if("object"!=typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}function g(){return(g=Object.assign||function(e){for(var t=1;t200)}))},failRegexEvent:function(){return null},noChangeEvent:function(){return null},refreshGroupsData:function(){return R("/groups","GET").then((function(e){return e.json()}))},refreshUserData:function(){return R("/users","GET").then((function(e){return e.json()}))}}},j=function(e){return g({},e,O())},function(e){var t=(0,r.createFactory)(e);return function(e){return t(j(e))}});var j;function z(e){return"/"===e.charAt(0)}function I(e,t){for(var n=t,r=n+1,a=e.length;r=0;s--){var f=a[s];"."===f?I(a,s):".."===f?(I(a,s),c++):c&&(I(a,s),c--)}if(!i)for(;c--;c)a.unshift("..");!i||""===a[0]||a[0]&&z(a[0])||a.unshift("");var d=a.join("/");return n&&"/"!==d.substr(-1)&&(d+="/"),d};"undefined"==typeof window||!window.document||window.document.createElement;var F=1073741823,U="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:void 0!==n.g?n.g:{};function $(e){var t=[];return{on:function(e){t.push(e)},off:function(e){t=t.filter((function(t){return t!==e}))},get:function(){return e},set:function(n,r){e=n,t.forEach((function(t){return t(e,r)}))}}}const B=r.createContext||function(e,t){var n,a,o,i="__create-react-context-"+((U[o="__global_unique_id__"]=(U[o]||0)+1)+"__"),u=function(e){function n(){var t;return(t=e.apply(this,arguments)||this).emitter=$(t.props.value),t}P(n,e);var r=n.prototype;return r.getChildContext=function(){var e;return(e={})[i]=this.emitter,e},r.componentWillReceiveProps=function(e){if(this.props.value!==e.value){var n,r=this.props.value,a=e.value;((o=r)===(l=a)?0!==o||1/o==1/l:o!=o&&l!=l)?n=0:(n="function"==typeof t?t(r,a):F,0!=(n|=0)&&this.emitter.set(e.value,n))}var o,l},r.render=function(){return this.props.children},n}(r.Component);u.childContextTypes=((n={})[i]=l().object.isRequired,n);var c=function(t){function n(){var e;return(e=t.apply(this,arguments)||this).state={value:e.getValue()},e.onUpdate=function(t,n){0!=((0|e.observedBits)&n)&&e.setState({value:e.getValue()})},e}P(n,t);var r=n.prototype;return r.componentWillReceiveProps=function(e){var t=e.observedBits;this.observedBits=null==t?F:t},r.componentDidMount=function(){this.context[i]&&this.context[i].on(this.onUpdate);var e=this.props.observedBits;this.observedBits=null==e?F:e},r.componentWillUnmount=function(){this.context[i]&&this.context[i].off(this.onUpdate)},r.getValue=function(){return this.context[i]?this.context[i].get():e},r.render=function(){return(e=this.props.children,Array.isArray(e)?e[0]:e)(this.state.value);var e},n}(r.Component);return c.contextTypes=((a={})[i]=l().object,a),{Provider:u,Consumer:c}};const H=function(e,t){if(!e)throw new Error("Invariant failed")};var V=n(779),W=n.n(V);function G(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r=0||(a[n]=e[n]);return a}var Q=function(e){var t=B();return t.displayName="Router-History",t}(),q=function(e){var t=B();return t.displayName="Router",t}(),K=function(e){function t(t){var n;return(n=e.call(this,t)||this).state={location:t.history.location},n._isMounted=!1,n._pendingLocation=null,t.staticContext||(n.unlisten=t.history.listen((function(e){n._isMounted?n.setState({location:e}):n._pendingLocation=e}))),n}P(t,e),t.computeRootMatch=function(e){return{path:"/",url:"/",params:{},isExact:"/"===e}};var n=t.prototype;return n.componentDidMount=function(){this._isMounted=!0,this._pendingLocation&&this.setState({location:this._pendingLocation})},n.componentWillUnmount=function(){this.unlisten&&this.unlisten()},n.render=function(){return r.createElement(q.Provider,{value:{history:this.props.history,location:this.state.location,match:t.computeRootMatch(this.state.location.pathname),staticContext:this.props.staticContext}},r.createElement(Q.Provider,{children:this.props.children||null,value:this.props.history}))},t}(r.Component);r.Component,r.Component;var Y={},X=0;function J(e,t){void 0===t&&(t={}),("string"==typeof t||Array.isArray(t))&&(t={path:t});var n=t,r=n.path,a=n.exact,o=void 0!==a&&a,l=n.strict,i=void 0!==l&&l,u=n.sensitive,c=void 0!==u&&u;return[].concat(r).reduce((function(t,n){if(!n&&""!==n)return null;if(t)return t;var r=function(e,t){var n=""+t.end+t.strict+t.sensitive,r=Y[n]||(Y[n]={});if(r[e])return r[e];var a=[],o={regexp:W()(e,a,t),keys:a};return X<1e4&&(r[e]=o,X++),o}(n,{end:o,strict:i,sensitive:c}),a=r.regexp,l=r.keys,u=a.exec(e);if(!u)return null;var s=u[0],f=u.slice(1),d=e===s;return o&&!d?null:{path:n,url:"/"===n&&""===s?"/":s,isExact:d,params:l.reduce((function(e,t,n){return e[t.name]=f[n],e}),{})}}),null)}var Z=function(e){function t(){return e.apply(this,arguments)||this}return P(t,e),t.prototype.render=function(){var e=this;return r.createElement(q.Consumer,null,(function(t){t||H(!1);var n=e.props.location||t.location,a=g({},t,{location:n,match:e.props.computedMatch?e.props.computedMatch:e.props.path?J(n.pathname,e.props):t.match}),o=e.props,l=o.children,i=o.component,u=o.render;return Array.isArray(l)&&0===l.length&&(l=null),r.createElement(q.Provider,{value:a},a.match?l?"function"==typeof l?l(a):l:i?r.createElement(i,a):u?u(a):null:"function"==typeof l?l(a):null)}))},t}(r.Component);r.Component;var ee=function(e){function t(){return e.apply(this,arguments)||this}return P(t,e),t.prototype.render=function(){var e=this;return r.createElement(q.Consumer,null,(function(t){t||H(!1);var n,a,o=e.props.location||t.location;return r.Children.forEach(e.props.children,(function(e){if(null==a&&r.isValidElement(e)){n=e;var l=e.props.path||e.props.from;a=l?J(o.pathname,g({},e.props,{path:l})):t.match}})),a?r.cloneElement(n,{location:o,computedMatch:a}):null}))},t}(r.Component);function te(e){return"/"===e.charAt(0)?e:"/"+e}function ne(e){return"/"===e.charAt(0)?e.substr(1):e}function re(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function ae(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function oe(e){var t=e.pathname,n=e.search,r=e.hash,a=t||"/";return n&&"?"!==n&&(a+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(a+="#"===r.charAt(0)?r:"#"+r),a}function le(e,t,n,r){var a;"string"==typeof e?(a=function(e){var t=e||"/",n="",r="",a=t.indexOf("#");-1!==a&&(r=t.substr(a),t=t.substr(0,a));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e)).state=t:(void 0===(a=g({},e)).pathname&&(a.pathname=""),a.search?"?"!==a.search.charAt(0)&&(a.search="?"+a.search):a.search="",a.hash?"#"!==a.hash.charAt(0)&&(a.hash="#"+a.hash):a.hash="",void 0!==t&&void 0===a.state&&(a.state=t));try{a.pathname=decodeURI(a.pathname)}catch(e){throw e instanceof URIError?new URIError('Pathname "'+a.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):e}return n&&(a.key=n),r?a.pathname?"/"!==a.pathname.charAt(0)&&(a.pathname=D(a.pathname,r.pathname)):a.pathname=r.pathname:a.pathname||(a.pathname="/"),a}function ie(){var e=null,t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,a){if(null!=e){var o="function"==typeof e?e(t,n):e;"string"==typeof o?"function"==typeof r?r(o,a):a(!0):a(!1!==o)}else a(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;r=1?r.createElement("button",{className:"btn btn-sm btn-light spaced"},r.createElement(ke,{to:"".concat(t,"?page=").concat(n-1)},r.createElement("span",{className:"active-pagination"},"Previous"))):r.createElement("button",{className:"btn btn-sm btn-light spaced"},r.createElement("span",{className:"inactive-pagination"},"Previous")),l>=a?r.createElement("button",{className:"btn btn-sm btn-light spaced"},r.createElement(ke,{to:"".concat(t,"?page=").concat(n+1)},r.createElement("span",{className:"active-pagination"},"Next"))):r.createElement("button",{className:"btn btn-sm btn-light spaced"},r.createElement("span",{className:"inactive-pagination"},"Next"))))};He.propTypes={endpoint:l().string,page:l().number,limit:l().number,numOffset:l().number,numElements:l().number};const Ve=He;function We(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(e)){var n=[],r=!0,a=!1,o=void 0;try{for(var l,i=e[Symbol.iterator]();!(r=(l=i.next()).done)&&(n.push(l.value),!t||n.length!==t);r=!0);}catch(e){a=!0,o=e}finally{try{r||null==i.return||i.return()}finally{if(a)throw o}}return n}}(e,t)||function(e,t){if(e){if("string"==typeof e)return Ge(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Ge(e,t):void 0}}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function Ge(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n Manage Groups")),r.createElement("div",{className:"server-dashboard-container"},r.createElement("table",{className:"table table-striped table-bordered table-hover"},r.createElement("thead",{className:"admin-table-head"},r.createElement("tr",null,r.createElement("th",{id:"user-header"},"User"," ",r.createElement(qe,{sorts:{asc:function(e){return e.sort((function(e,t){return e.namet.name?1:-1}))}},callback:function(e){return i((function(){return e}))},testid:"user-sort"})),r.createElement("th",{id:"admin-header"},"Admin"," ",r.createElement(qe,{sorts:{asc:function(e){return e.sort((function(e){return e.admin?1:-1}))},desc:function(e){return e.sort((function(e){return e.admin?-1:1}))}},callback:function(e){return i((function(){return e}))},testid:"admin-sort"})),r.createElement("th",{id:"last-activity-header"},"Last Activity"," ",r.createElement(qe,{sorts:{asc:function(e){return e.sort((function(e,t){return new Date(e.last_activity)-new Date(t.last_activity)>0?1:-1}))},desc:function(e){return e.sort((function(e,t){return new Date(e.last_activity)-new Date(t.last_activity)>0?-1:1}))}},callback:function(e){return i((function(){return e}))},testid:"last-activity-sort"})),r.createElement("th",{id:"running-status-header"},"Running"," ",r.createElement(qe,{sorts:{asc:function(e){return e.sort((function(e){return null==e.server?-1:1}))},desc:function(e){return e.sort((function(e){return null==e.server?1:-1}))}},callback:function(e){return i((function(){return e}))},testid:"running-status-sort"})),r.createElement("th",{id:"actions-header"},"Actions"))),r.createElement("tbody",null,r.createElement("tr",{className:"noborder"},r.createElement("td",null,r.createElement(Re,{variant:"light",className:"add-users-button"},r.createElement(ke,{to:"/add-users"},"Add Users"))),r.createElement("td",null),r.createElement("td",null),r.createElement("td",null,r.createElement(Re,{variant:"primary",className:"start-all","data-testid":"start-all",onClick:function(){Promise.all(g(u.map((function(e){return e.name})))).then((function(e){var t=e.filter((function(e){return!e.ok}));return t.length>0&&a("Failed to start ".concat(t.length," ").concat(t.length>1?"servers":"server",". ").concat(t.length>1?"Are they ":"Is it "," already running?")),e})).then((function(e){return h.apply(void 0,d).then((function(e){E(e,f)})).catch((function(){return a("Failed to update users list.")})),e})).catch((function(){return a("Failed to start servers.")}))}},"Start All"),r.createElement("span",null," "),r.createElement(Re,{variant:"danger",className:"stop-all","data-testid":"stop-all",onClick:function(){Promise.all(b(u.map((function(e){return e.name})))).then((function(e){var t=e.filter((function(e){return!e.ok}));return t.length>0&&a("Failed to stop ".concat(t.length," ").concat(t.length>1?"servers":"server",". ").concat(t.length>1?"Are they ":"Is it "," already stopped?")),e})).then((function(e){return h.apply(void 0,d).then((function(e){E(e,f)})).catch((function(){return a("Failed to update users list.")})),e})).catch((function(){return a("Failed to stop servers.")}))}},"Stop All")),r.createElement("td",null,r.createElement(Re,{variant:"danger",id:"shutdown-button",onClick:m},"Shutdown Hub"))),u.map((function(e,t){return r.createElement("tr",{key:t+"row",className:"user-row"},r.createElement("td",{"data-testid":"user-row-name"},e.name),r.createElement("td",{"data-testid":"user-row-admin"},e.admin?"admin":""),r.createElement("td",{"data-testid":"user-row-last-activity"},e.last_activity?(n=e.last_activity,6e4,o=36e5,l=864e5,i=2592e6,u=31536e6,(c=Date.now()-Date.parse(n))<6e4?Math.round(c/1e3)+" seconds ago":c0?n.map((function(e,n){return r.createElement("li",{className:"list-group-item",key:"group-item"+n},r.createElement("span",{className:"badge badge-pill badge-success"},e.users.length+" users"),r.createElement(ke,{to:{pathname:"/group-edit",state:{group_data:e,user_data:t}}},e.name))})):r.createElement("div",null,r.createElement("h4",null,"no groups created..."))),r.createElement(Ve,{endpoint:"/groups",page:i,limit:o,numOffset:u[0],numElements:n.length})),r.createElement("div",{className:"panel-footer"},r.createElement("button",{className:"btn btn-light adjacent-span-spacing"},r.createElement(ke,{to:"/"},"Back")),r.createElement("button",{className:"btn btn-primary adjacent-span-spacing",onClick:function(){s.push("/create-group")}},"New Group"))))))):r.createElement("div",{"data-testid":"no-show"})};Ye.propTypes={user_data:l().array,groups_data:l().array,updateUsers:l().func,updateGroups:l().func,history:l().shape({push:l().func}),location:l().shape({search:l().string})};const Xe=Ye;function Je(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(e)){var n=[],r=!0,a=!1,o=void 0;try{for(var l,i=e[Symbol.iterator]();!(r=(l=i.next()).done)&&(n.push(l.value),!t||n.length!==t);r=!0);}catch(e){a=!0,o=e}finally{try{r||null==i.return||i.return()}finally{if(a)throw o}}return n}}(e,t)||function(e,t){if(e){if("string"==typeof e)return Ze(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Ze(e,t):void 0}}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function Ze(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&r.push(h(e,E.name)),t.length>0&&r.push(m(t,E.name)),Promise.all(r).then((function(e){0==e.map((function(e){return e.status})).filter((function(e){return e>=300})).length?y(0,f).then((function(e){return p(e,0)})).then((function(){return b.push("/groups")})):s("Failed to edit group.")})).catch((function(){console.log("outer"),s("Failed to edit group.")}))}else b.push("/groups")}},"Apply"),r.createElement("button",{id:"delete-group","data-testid":"delete-group",className:"btn btn-danger",style:{float:"right"},onClick:function(){var e=E.name;v(e).then((function(e){e.status<300?y(0,f).then((function(e){return p(e,0)})).then((function(){return b.push("/groups")})):s("Failed to delete group.")})).catch((function(){return s("Failed to delete group.")}))}},"Delete Group"),r.createElement("br",null),r.createElement("br",null)))):r.createElement("div",null)};at.propTypes={location:l().shape({state:l().shape({group_data:l().object,callback:l().func})}),history:l().shape({push:l().func}),addToGroup:l().func,removeFromGroup:l().func,deleteGroup:l().func,updateGroups:l().func,validateUser:l().func};const ot=at;function lt(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(e)){var n=[],r=!0,a=!1,o=void 0;try{for(var l,i=e[Symbol.iterator]();!(r=(l=i.next()).done)&&(n.push(l.value),!t||n.length!==t);r=!0);}catch(e){a=!0,o=e}finally{try{r||null==i.return||i.return()}finally{if(a)throw o}}return n}}(e,t)||function(e,t){if(e){if("string"==typeof e)return it(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?it(e,t):void 0}}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function it(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n2&&0==/[!@#$%^&*(),.?":{}|<>]/g.test(e)}));e.lengthe.length)&&(t=e.length);for(var n=0,r=new Array(t);n2&&0==/[!@#$%^&*(),.?":{}|<>]/g.test(y)?u(h,""!=y?y:h,w).then((function(e){e.status<300?f(0,t).then((function(e){return i(e,0)})).then((function(){return d.push("/")})).catch((function(){return o("Could not update users list.")})):o("Failed to edit user.")})).catch((function(){o("Failed to edit user.")})):o("Failed to edit user. Make sure the username does not contain special characters."):u(h,h,w).then((function(e){e.status<300?f(0,t).then((function(e){return i(e,0)})).then((function(){return d.push("/")})).catch((function(){return o("Could not update users list.")})):o("Failed to edit user.")})).catch((function(){o("Failed to edit user.")})):s()}},"Apply")))))))};vt.propTypes={location:l().shape({state:l().shape({username:l().string,has_admin:l().bool})}),history:l().shape({push:l().func}),editUser:l().func,deleteUser:l().func,failRegexEvent:l().func,noChangeEvent:l().func,updateUsers:l().func};const yt=vt;n(137);var gt=function e(t,n,r){var a;if("function"==typeof n&&"function"==typeof r||"function"==typeof r&&"function"==typeof arguments[3])throw new Error("It looks like you are passing several store enhancers to createStore(). This is not supported. Instead, compose them together to a single function.");if("function"==typeof n&&void 0===r&&(r=n,n=void 0),void 0!==r){if("function"!=typeof r)throw new Error("Expected the enhancer to be a function.");return r(e)(t,n)}if("function"!=typeof t)throw new Error("Expected the reducer to be a function.");var o=t,l=n,i=[],u=i,c=!1;function s(){u===i&&(u=i.slice())}function f(){if(c)throw new Error("You may not call store.getState() while the reducer is executing. The reducer has already received the state as an argument. Pass it down from the top reducer instead of reading it from the store.");return l}function d(e){if("function"!=typeof e)throw new Error("Expected the listener to be a function.");if(c)throw new Error("You may not call store.subscribe() while the reducer is executing. If you would like to be notified after the store has been updated, subscribe from a component and invoke store.getState() in the callback to access the latest state. See https://redux.js.org/api-reference/store#subscribelistener for more details.");var t=!0;return s(),u.push(e),function(){if(t){if(c)throw new Error("You may not unsubscribe from a store listener while the reducer is executing. See https://redux.js.org/api-reference/store#subscribelistener for more details.");t=!1,s();var n=u.indexOf(e);u.splice(n,1),i=null}}}function p(e){if(!y(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(c)throw new Error("Reducers may not dispatch actions.");try{c=!0,l=o(l,e)}finally{c=!1}for(var t=i=u,n=0;n0&&void 0!==arguments[0]?arguments[0]:A,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case"USER_PAGE":return Object.assign({},e,{user_page:t.value.page,user_data:t.value.data});case"GROUPS_PAGE":return Object.assign({},e,{groups_page:t.value.page,groups_data:t.value.data});default:return e}}),A),bt=function(){return(0,r.useEffect)((function(){var e=A.limit,t=A.groups_page;R("/users?offset=".concat(A.user_page*e,"&limit=").concat(e),"GET").then((function(e){return e.json()})).then((function(e){return gt.dispatch({type:"USER_PAGE",value:{data:e,page:0}})})).catch((function(e){return console.log(e)})),R("/groups?offset=".concat(t*e,"&limit=").concat(e),"GET").then((function(e){return e.json()})).then((function(e){return gt.dispatch({type:"GROUPS_PAGE",value:{data:e,page:0}})})).catch((function(e){return console.log(e)}))})),r.createElement("div",{className:"resets"},r.createElement(d,{store:gt},r.createElement(ve,null,r.createElement(ee,null,r.createElement(Z,{exact:!0,path:"/",component:T(M)(Ke)}),r.createElement(Z,{exact:!0,path:"/groups",component:T(M)(Xe)}),r.createElement(Z,{exact:!0,path:"/group-edit",component:T(M)(ot)}),r.createElement(Z,{exact:!0,path:"/create-group",component:T(M)(ct)}),r.createElement(Z,{exact:!0,path:"/add-users",component:T(M)(pt)}),r.createElement(Z,{exact:!0,path:"/edit-user",component:T(M)(yt)})))))};a.render(r.createElement(bt,null),document.getElementById("react-admin-hook"))},790:(e,t)=>{"use strict";t.E=function(){var e=[],t=e;function n(){t===e&&(t=e.slice())}return{listen:function(e){if("function"!=typeof e)throw new Error("Expected listener to be a function.");var r=!0;return n(),t.push(e),function(){if(r){r=!1,n();var a=t.indexOf(e);t.splice(a,1)}}},emit:function(){for(var n=e=t,r=0;r{var n;!function(){"use strict";var r={}.hasOwnProperty;function a(){for(var e=[],t=0;t{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(645),a=n.n(r),o=n(223),l=a()((function(e){return e[1]}));l.i(o.default),l.push([e.id,".users-container {\n width: 100%;\n position: relative;\n padding: 5px;\n overflow-x: scroll;\n}\n\n.users-container div {\n display: inline-block;\n}\n\n.users-container .item {\n padding: 3px;\n padding-left: 6px;\n padding-right: 6px;\n border-radius: 3px;\n font-size: 14px;\n margin-left: 4px;\n margin-right: 4px;\n transition: 30ms ease-in all;\n cursor: pointer;\n user-select: none;\n border: solid 1px #dfdfdf;\n}\n\n.users-container .item.unselected {\n background-color: #f7f7f7;\n color: #777;\n}\n\n.users-container .item.selected {\n background-color: orange;\n color: white;\n}\n\n.users-container .item:hover {\n opacity: 0.7;\n}\n",""]);const i=l},457:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(645),a=n.n(r),o=n(223),l=a()((function(e){return e[1]}));l.i(o.default),l.push([e.id,".pagination-footer * button {\n margin-right: 10px;\n}\n\n.pagination-footer * .inactive-pagination {\n color: gray;\n cursor: not-allowed;\n}\n\n.pagination-footer * button.spaced {\n color: var(--blue);\n}\n",""]);const i=l},642:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(645),a=n.n(r),o=n(223),l=a()((function(e){return e[1]}));l.i(o.default),l.push([e.id,".server-dashboard-container {\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n.server-dashboard-container .add-users-button {\n border: 1px solid #ddd;\n}\n\n.server-dashboard-container tbody {\n color: #626262;\n}\n\n.admin-table-head {\n user-select: none;\n}\n\n.sort-icon {\n display: inline-block;\n top: 0.125em;\n position: relative;\n user-select: none;\n cursor: pointer;\n}\n\ntr.noborder > td {\n border: none !important;\n}\n",""]);const i=l},223:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(645),a=n.n(r)()((function(e){return e[1]}));a.push([e.id,":root {\n --red: #d7191e;\n --orange: #f1ad4e;\n --blue: #2e7ab6;\n --white: #ffffff;\n --gray: #f7f7f7;\n}\n\n/* Color Classes */\n.red {\n background-color: var(--red);\n}\n.orange {\n background-color: var(--orange);\n}\n.blue {\n background-color: var(--blue);\n}\n.white {\n background-color: var(--white);\n}\n\n/* Resets */\n\n.resets .modal {\n display: block;\n visibility: visible;\n z-index: 2000;\n}\n\n/* Global Util Classes */\n.adjacent-span-spacing {\n margin-right: 5px;\n margin-left: 5px;\n}\n",""]);const o=a},645:e=>{"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n=e(t);return t[2]?"@media ".concat(t[2]," {").concat(n,"}"):n})).join("")},t.i=function(e,n,r){"string"==typeof e&&(e=[[null,e,""]]);var a={};if(r)for(var o=0;o{"use strict";var t=Object.prototype.hasOwnProperty;function n(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}e.exports=function(e,r){if(n(e,r))return!0;if("object"!=typeof e||null===e||"object"!=typeof r||null===r)return!1;var a=Object.keys(e),o=Object.keys(r);if(a.length!==o.length)return!1;for(var l=0;l{"use strict";var r=n(864),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},l={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},i={};function u(e){return r.isMemo(e)?l:i[e.$$typeof]||a}i[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},i[r.Memo]=l;var c=Object.defineProperty,s=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(h){var a=p(n);a&&a!==h&&e(t,a,r)}var l=s(n);f&&(l=l.concat(f(n)));for(var i=u(t),m=u(n),v=0;v{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,o){for(var l,i,u=a(e),c=1;c{var r=n(173);e.exports=function e(t,n,a){return r(n)||(a=n||a,n=[]),a=a||{},t instanceof RegExp?function(e,t){var n=e.source.match(/\((?!\?)/g);if(n)for(var r=0;r{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},703:(e,t,n)=>{"use strict";var r=n(414);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,l){if(l!==r){var i=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw i.name="Invariant Violation",i}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},697:(e,t,n)=>{e.exports=n(703)()},414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},448:(e,t,n)=>{"use strict";var r=n(294),a=n(418),o=n(840);function l(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n