Compare commits

...

6 Commits

Author SHA1 Message Date
Daniel Valdivia
27fd91ed37 Release v0.22.2 (#2495)
Signed-off-by: Daniel Valdivia
<18384552+dvaldivia@users.noreply.github.com>
2022-12-09 16:42:57 -08:00
Alex
7df0560104 Updated mds & fixed blank pages on login (#2494)
- Updates mds to v0.0.7
- Fixed an issue with blank pages during login
- Fixes an issue during logout where path was redirected to
'logout/login'

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
2022-12-09 14:24:58 -08:00
Javier Adriel
2368199e03 IDP management UI (#2487)
Adds UI to interact with IDP Configurations (CRUD)
2022-12-09 14:13:10 -08:00
Daniel Valdivia
bee98e1ba0 fix: race Condition on Object Browser via Websocket (#2492)
Signed-off-by: Daniel Valdivia
<18384552+dvaldivia@users.noreply.github.com>
2022-12-09 11:18:19 -08:00
Harshavardhana
8abbbb4625 avoid printing random logs (#2489) 2022-12-08 07:54:21 -08:00
Javier Adriel
ef182fe75e Increase threshold to 54.4 (Current coverage 54.5) (#2488) 2022-12-07 20:01:09 -06:00
67 changed files with 1710 additions and 78 deletions

View File

@@ -2517,7 +2517,7 @@ jobs:
go tool cover -func=all.out | grep total > tmp2
result=`cat tmp2 | awk 'END {print $3}'`
result=${result%\%}
threshold=53.40
threshold=54.40
echo "Result:"
echo "$result%"
if (( $(echo "$result >= $threshold" |bc -l) )); then

View File

@@ -15,7 +15,7 @@ spec:
serviceAccountName: console-sa
containers:
- name: console
image: 'minio/console:v0.22.1'
image: 'minio/console:v0.22.2'
imagePullPolicy: "IfNotPresent"
env:
- name: CONSOLE_OPERATOR_MODE

View File

@@ -32,7 +32,7 @@ spec:
spec:
containers:
- name: console
image: 'minio/console:v0.22.1'
image: 'minio/console:v0.22.2'
imagePullPolicy: "IfNotPresent"
env:
- name: CONSOLE_MINIO_SERVER

View File

@@ -1,8 +1,8 @@
{
"files": {
"main.css": "./static/css/main.b20a708b.css",
"main.js": "./static/js/main.ed30ed16.js",
"static/js/1260.4d240571.chunk.js": "./static/js/1260.4d240571.chunk.js",
"main.js": "./static/js/main.2b781eaf.js",
"static/js/1260.ded6e17b.chunk.js": "./static/js/1260.ded6e17b.chunk.js",
"static/js/6914.1699f207.chunk.js": "./static/js/6914.1699f207.chunk.js",
"static/js/4209.227aa3b5.chunk.js": "./static/js/4209.227aa3b5.chunk.js",
"static/js/1829.be0f91b9.chunk.js": "./static/js/1829.be0f91b9.chunk.js",
@@ -40,6 +40,12 @@
"static/css/5517.9cb5f34b.chunk.css": "./static/css/5517.9cb5f34b.chunk.css",
"static/js/5517.730e0aeb.chunk.js": "./static/js/5517.730e0aeb.chunk.js",
"static/js/2555.47860755.chunk.js": "./static/js/2555.47860755.chunk.js",
"static/js/7486.83e0d248.chunk.js": "./static/js/7486.83e0d248.chunk.js",
"static/js/1377.6fbc40f3.chunk.js": "./static/js/1377.6fbc40f3.chunk.js",
"static/js/4672.7e50d90b.chunk.js": "./static/js/4672.7e50d90b.chunk.js",
"static/js/2516.5d118ef6.chunk.js": "./static/js/2516.5d118ef6.chunk.js",
"static/js/2759.3ab5d8ec.chunk.js": "./static/js/2759.3ab5d8ec.chunk.js",
"static/js/7295.c85034bf.chunk.js": "./static/js/7295.c85034bf.chunk.js",
"static/js/7585.cd3ad44f.chunk.js": "./static/js/7585.cd3ad44f.chunk.js",
"static/js/4902.b5c5ff07.chunk.js": "./static/js/4902.b5c5ff07.chunk.js",
"static/js/7847.659fc617.chunk.js": "./static/js/7847.659fc617.chunk.js",
@@ -61,9 +67,9 @@
"static/js/8833.9dccd2d5.chunk.js": "./static/js/8833.9dccd2d5.chunk.js",
"static/js/6526.e3612299.chunk.js": "./static/js/6526.e3612299.chunk.js",
"static/js/483.5b997456.chunk.js": "./static/js/483.5b997456.chunk.js",
"static/js/9467.5b40a136.chunk.js": "./static/js/9467.5b40a136.chunk.js",
"static/js/9467.8e1b707f.chunk.js": "./static/js/9467.8e1b707f.chunk.js",
"static/js/6895.488a4025.chunk.js": "./static/js/6895.488a4025.chunk.js",
"static/js/5882.277a7242.chunk.js": "./static/js/5882.277a7242.chunk.js",
"static/js/5882.41668ace.chunk.js": "./static/js/5882.41668ace.chunk.js",
"static/js/8277.e772e8bd.chunk.js": "./static/js/8277.e772e8bd.chunk.js",
"static/js/4133.2386eedc.chunk.js": "./static/js/4133.2386eedc.chunk.js",
"static/css/1367.9cb5f34b.chunk.css": "./static/css/1367.9cb5f34b.chunk.css",
@@ -104,7 +110,7 @@
"static/js/9179.73844de6.chunk.js": "./static/js/9179.73844de6.chunk.js",
"static/js/51.f63429fd.chunk.js": "./static/js/51.f63429fd.chunk.js",
"static/js/711.7013b9d7.chunk.js": "./static/js/711.7013b9d7.chunk.js",
"static/js/6901.e2472ebd.chunk.js": "./static/js/6901.e2472ebd.chunk.js",
"static/js/6901.cda3f1f9.chunk.js": "./static/js/6901.cda3f1f9.chunk.js",
"static/js/2185.4baca582.chunk.js": "./static/js/2185.4baca582.chunk.js",
"static/js/312.770148c8.chunk.js": "./static/js/312.770148c8.chunk.js",
"static/js/2112.c85537ec.chunk.js": "./static/js/2112.c85537ec.chunk.js",
@@ -151,8 +157,10 @@
"static/js/9515.a4e964be.chunk.js": "./static/js/9515.a4e964be.chunk.js",
"static/js/2983.e248775f.chunk.js": "./static/js/2983.e248775f.chunk.js",
"static/js/5861.65847210.chunk.js": "./static/js/5861.65847210.chunk.js",
"static/js/537.85347210.chunk.js": "./static/js/537.85347210.chunk.js",
"static/js/2763.08c6e1fd.chunk.js": "./static/js/2763.08c6e1fd.chunk.js",
"static/media/LoginBG.png": "./static/media/LoginBG.143407455857f28ba809.png",
"static/media/videoBG.mp4": "./static/media/videoBG.17363418b3c2246a0e27.mp4",
"static/media/loginAnimationPoster.png": "./static/media/loginAnimationPoster.9aa924bfe619e71d5d29.png",
"static/media/Inter-BoldItalic.woff": "./static/media/Inter-BoldItalic.b376885042f6c961a541.woff",
"static/media/Inter-LightItalic.woff": "./static/media/Inter-LightItalic.ef9f65d91d2b0ba9b2e4.woff",
"static/media/Inter-BlackItalic.woff": "./static/media/Inter-BlackItalic.ca1e738e4f349f27514d.woff",
@@ -173,8 +181,8 @@
"static/media/Inter-Regular.woff2": "./static/media/Inter-Regular.c8ba52b05a9ef10f4758.woff2",
"index.html": "./index.html",
"main.b20a708b.css.map": "./static/css/main.b20a708b.css.map",
"main.ed30ed16.js.map": "./static/js/main.ed30ed16.js.map",
"1260.4d240571.chunk.js.map": "./static/js/1260.4d240571.chunk.js.map",
"main.2b781eaf.js.map": "./static/js/main.2b781eaf.js.map",
"1260.ded6e17b.chunk.js.map": "./static/js/1260.ded6e17b.chunk.js.map",
"6914.1699f207.chunk.js.map": "./static/js/6914.1699f207.chunk.js.map",
"4209.227aa3b5.chunk.js.map": "./static/js/4209.227aa3b5.chunk.js.map",
"1829.be0f91b9.chunk.js.map": "./static/js/1829.be0f91b9.chunk.js.map",
@@ -212,6 +220,12 @@
"5517.9cb5f34b.chunk.css.map": "./static/css/5517.9cb5f34b.chunk.css.map",
"5517.730e0aeb.chunk.js.map": "./static/js/5517.730e0aeb.chunk.js.map",
"2555.47860755.chunk.js.map": "./static/js/2555.47860755.chunk.js.map",
"7486.83e0d248.chunk.js.map": "./static/js/7486.83e0d248.chunk.js.map",
"1377.6fbc40f3.chunk.js.map": "./static/js/1377.6fbc40f3.chunk.js.map",
"4672.7e50d90b.chunk.js.map": "./static/js/4672.7e50d90b.chunk.js.map",
"2516.5d118ef6.chunk.js.map": "./static/js/2516.5d118ef6.chunk.js.map",
"2759.3ab5d8ec.chunk.js.map": "./static/js/2759.3ab5d8ec.chunk.js.map",
"7295.c85034bf.chunk.js.map": "./static/js/7295.c85034bf.chunk.js.map",
"7585.cd3ad44f.chunk.js.map": "./static/js/7585.cd3ad44f.chunk.js.map",
"4902.b5c5ff07.chunk.js.map": "./static/js/4902.b5c5ff07.chunk.js.map",
"7847.659fc617.chunk.js.map": "./static/js/7847.659fc617.chunk.js.map",
@@ -233,9 +247,9 @@
"8833.9dccd2d5.chunk.js.map": "./static/js/8833.9dccd2d5.chunk.js.map",
"6526.e3612299.chunk.js.map": "./static/js/6526.e3612299.chunk.js.map",
"483.5b997456.chunk.js.map": "./static/js/483.5b997456.chunk.js.map",
"9467.5b40a136.chunk.js.map": "./static/js/9467.5b40a136.chunk.js.map",
"9467.8e1b707f.chunk.js.map": "./static/js/9467.8e1b707f.chunk.js.map",
"6895.488a4025.chunk.js.map": "./static/js/6895.488a4025.chunk.js.map",
"5882.277a7242.chunk.js.map": "./static/js/5882.277a7242.chunk.js.map",
"5882.41668ace.chunk.js.map": "./static/js/5882.41668ace.chunk.js.map",
"8277.e772e8bd.chunk.js.map": "./static/js/8277.e772e8bd.chunk.js.map",
"4133.2386eedc.chunk.js.map": "./static/js/4133.2386eedc.chunk.js.map",
"1367.9cb5f34b.chunk.css.map": "./static/css/1367.9cb5f34b.chunk.css.map",
@@ -276,7 +290,7 @@
"9179.73844de6.chunk.js.map": "./static/js/9179.73844de6.chunk.js.map",
"51.f63429fd.chunk.js.map": "./static/js/51.f63429fd.chunk.js.map",
"711.7013b9d7.chunk.js.map": "./static/js/711.7013b9d7.chunk.js.map",
"6901.e2472ebd.chunk.js.map": "./static/js/6901.e2472ebd.chunk.js.map",
"6901.cda3f1f9.chunk.js.map": "./static/js/6901.cda3f1f9.chunk.js.map",
"2185.4baca582.chunk.js.map": "./static/js/2185.4baca582.chunk.js.map",
"312.770148c8.chunk.js.map": "./static/js/312.770148c8.chunk.js.map",
"2112.c85537ec.chunk.js.map": "./static/js/2112.c85537ec.chunk.js.map",
@@ -323,10 +337,11 @@
"9515.a4e964be.chunk.js.map": "./static/js/9515.a4e964be.chunk.js.map",
"2983.e248775f.chunk.js.map": "./static/js/2983.e248775f.chunk.js.map",
"5861.65847210.chunk.js.map": "./static/js/5861.65847210.chunk.js.map",
"537.85347210.chunk.js.map": "./static/js/537.85347210.chunk.js.map",
"2763.08c6e1fd.chunk.js.map": "./static/js/2763.08c6e1fd.chunk.js.map"
},
"entrypoints": [
"static/css/main.b20a708b.css",
"static/js/main.ed30ed16.js"
"static/js/main.2b781eaf.js"
]
}

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="/"/><meta content="width=device-width,initial-scale=1" name="viewport"/><meta content="#081C42" media="(prefers-color-scheme: light)" name="theme-color"/><meta content="#081C42" media="(prefers-color-scheme: dark)" name="theme-color"/><meta content="MinIO Console" name="description"/><link href="./styles/root-styles.css" rel="stylesheet"/><link href="./apple-icon-180x180.png" rel="apple-touch-icon" sizes="180x180"/><link href="./favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"/><link href="./favicon-96x96.png" rel="icon" sizes="96x96" type="image/png"/><link href="./favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"/><link href="./manifest.json" rel="manifest"/><link color="#3a4e54" href="./safari-pinned-tab.svg" rel="mask-icon"/><title>MinIO Console</title><script defer="defer" src="./static/js/main.ed30ed16.js"></script><link href="./static/css/main.b20a708b.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="preload"><img src="./images/background.svg"/> <img src="./images/background-wave-orig2.svg"/></div><div id="loader-block"><img src="./Loader.svg"/></div></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="/"/><meta content="width=device-width,initial-scale=1" name="viewport"/><meta content="#081C42" media="(prefers-color-scheme: light)" name="theme-color"/><meta content="#081C42" media="(prefers-color-scheme: dark)" name="theme-color"/><meta content="MinIO Console" name="description"/><link href="./styles/root-styles.css" rel="stylesheet"/><link href="./apple-icon-180x180.png" rel="apple-touch-icon" sizes="180x180"/><link href="./favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"/><link href="./favicon-96x96.png" rel="icon" sizes="96x96" type="image/png"/><link href="./favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"/><link href="./manifest.json" rel="manifest"/><link color="#3a4e54" href="./safari-pinned-tab.svg" rel="mask-icon"/><title>MinIO Console</title><script defer="defer" src="./static/js/main.2b781eaf.js"></script><link href="./static/css/main.b20a708b.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="preload"><img src="./images/background.svg"/> <img src="./images/background-wave-orig2.svg"/></div><div id="loader-block"><img src="./Loader.svg"/></div></div></body></html>

View File

@@ -1,2 +0,0 @@
"use strict";(self.webpackChunkportal_ui=self.webpackChunkportal_ui||[]).push([[1260],{1260:function(t,e,o){o.r(e);o(72791);var a=o(16871),n=o(25469),r=o(45248),u=o(81207),c=o(87995),l=o(46078),i=o(80184);e.default=function(){var t=(0,n.TL)(),e=(0,a.s0)();return function(){var o=function(){(0,r.Ov)(),t((0,c.wr)(!1)),localStorage.setItem("userLoggedIn",""),localStorage.setItem("redirect-path",""),t((0,l.lX)()),e("login")},a=localStorage.getItem("auth-state");u.Z.invoke("POST","/api/v1/logout",{state:a}).then((function(){o()})).catch((function(t){console.log(t),o()}))}(),(0,i.jsx)(i.Fragment,{})}}}]);
//# sourceMappingURL=1260.4d240571.chunk.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"static/js/1260.4d240571.chunk.js","mappings":"6MAoDA,UA3BmB,WACjB,IAAMA,GAAWC,EAAAA,EAAAA,MACXC,GAAWC,EAAAA,EAAAA,MAsBjB,OArBe,WACb,IAAMC,EAAgB,YACpBC,EAAAA,EAAAA,MACAL,GAASM,EAAAA,EAAAA,KAAW,IACpBC,aAAaC,QAAQ,eAAgB,IACrCD,aAAaC,QAAQ,gBAAiB,IACtCR,GAASS,EAAAA,EAAAA,OACTP,EAAS,QACV,EACKQ,EAAQH,aAAaI,QAAQ,cACnCC,EAAAA,EAAAA,OACU,OADV,iBACoC,CAAEF,MAAAA,IACnCG,MAAK,WACJT,GACD,IACAU,OAAM,SAACC,GACNC,QAAQC,IAAIF,GACZX,GACD,GACJ,CACDc,IACO,uBACR,C","sources":["screens/LogoutPage/LogoutPage.tsx"],"sourcesContent":["// This file is part of MinIO Console Server\n// Copyright (c) 2022 MinIO, Inc.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <http://www.gnu.org/licenses/>.\n\nimport React from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useAppDispatch } from \"../../store\";\nimport { ErrorResponseHandler } from \"../../common/types\";\nimport { clearSession } from \"../../common/utils\";\nimport api from \"../../common/api\";\nimport { userLogged } from \"../../systemSlice\";\nimport { resetSession } from \"../Console/consoleSlice\";\n\nconst LogoutPage = () => {\n const dispatch = useAppDispatch();\n const navigate = useNavigate();\n const logout = () => {\n const deleteSession = () => {\n clearSession();\n dispatch(userLogged(false));\n localStorage.setItem(\"userLoggedIn\", \"\");\n localStorage.setItem(\"redirect-path\", \"\");\n dispatch(resetSession());\n navigate(`login`);\n };\n const state = localStorage.getItem(\"auth-state\");\n api\n .invoke(\"POST\", `/api/v1/logout`, { state })\n .then(() => {\n deleteSession();\n })\n .catch((err: ErrorResponseHandler) => {\n console.log(err);\n deleteSession();\n });\n };\n logout();\n return <></>;\n};\n\nexport default LogoutPage;\n"],"names":["dispatch","useAppDispatch","navigate","useNavigate","deleteSession","clearSession","userLogged","localStorage","setItem","resetSession","state","getItem","api","then","catch","err","console","log","logout"],"sourceRoot":""}

View File

@@ -0,0 +1,2 @@
"use strict";(self.webpackChunkportal_ui=self.webpackChunkportal_ui||[]).push([[1260],{1260:function(t,e,o){o.r(e);o(72791);var a=o(16871),n=o(25469),u=o(45248),c=o(87995),l=o(46078),r=o(81207),i=o(7241),s=o(80184);e.default=function(){var t=(0,n.TL)(),e=(0,a.s0)();return function(){var o=function(){(0,u.Ov)(),t((0,c.wr)(!1)),localStorage.setItem("userLoggedIn",""),localStorage.setItem("redirect-path",""),t((0,l.lX)()),e("/login")},a=localStorage.getItem("auth-state");r.Z.invoke("POST","/api/v1/logout",{state:a}).then((function(){o()})).catch((function(t){console.log(t),o()}))}(),(0,s.jsx)(i.Z,{})}}}]);
//# sourceMappingURL=1260.ded6e17b.chunk.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"static/js/1260.ded6e17b.chunk.js","mappings":"uNAqDA,UA3BmB,WACjB,IAAMA,GAAWC,EAAAA,EAAAA,MACXC,GAAWC,EAAAA,EAAAA,MAsBjB,OArBe,WACb,IAAMC,EAAgB,YACpBC,EAAAA,EAAAA,MACAL,GAASM,EAAAA,EAAAA,KAAW,IACpBC,aAAaC,QAAQ,eAAgB,IACrCD,aAAaC,QAAQ,gBAAiB,IACtCR,GAASS,EAAAA,EAAAA,OACTP,EAAS,SACV,EACKQ,EAAQH,aAAaI,QAAQ,cACnCC,EAAAA,EAAAA,OACU,OADV,iBACoC,CAAEF,MAAAA,IACnCG,MAAK,WACJT,GACD,IACAU,OAAM,SAACC,GACNC,QAAQC,IAAIF,GACZX,GACD,GACJ,CACDc,IACO,SAAC,IAAD,GACR,C","sources":["screens/LogoutPage/LogoutPage.tsx"],"sourcesContent":["// This file is part of MinIO Console Server\n// Copyright (c) 2022 MinIO, Inc.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <http://www.gnu.org/licenses/>.\n\nimport React from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useAppDispatch } from \"../../store\";\nimport { ErrorResponseHandler } from \"../../common/types\";\nimport { clearSession } from \"../../common/utils\";\nimport { userLogged } from \"../../systemSlice\";\nimport { resetSession } from \"../Console/consoleSlice\";\nimport api from \"../../common/api\";\nimport LoadingComponent from \"../../common/LoadingComponent\";\n\nconst LogoutPage = () => {\n const dispatch = useAppDispatch();\n const navigate = useNavigate();\n const logout = () => {\n const deleteSession = () => {\n clearSession();\n dispatch(userLogged(false));\n localStorage.setItem(\"userLoggedIn\", \"\");\n localStorage.setItem(\"redirect-path\", \"\");\n dispatch(resetSession());\n navigate(`/login`);\n };\n const state = localStorage.getItem(\"auth-state\");\n api\n .invoke(\"POST\", `/api/v1/logout`, { state })\n .then(() => {\n deleteSession();\n })\n .catch((err: ErrorResponseHandler) => {\n console.log(err);\n deleteSession();\n });\n };\n logout();\n return <LoadingComponent />;\n};\n\nexport default LogoutPage;\n"],"names":["dispatch","useAppDispatch","navigate","useNavigate","deleteSession","clearSession","userLogged","localStorage","setItem","resetSession","state","getItem","api","then","catch","err","console","log","logout"],"sourceRoot":""}

View File

@@ -0,0 +1,2 @@
"use strict";(self.webpackChunkportal_ui=self.webpackChunkportal_ui||[]).push([[1377],{81377:function(u,e,n){n.r(e);n(72791);var t=n(11135),r=n(25787),s=n(44959),i=n(80184);e.default=(0,r.Z)((function(u){return(0,t.Z)({})}))((function(u){u.classes;return(0,i.jsx)(s.Z,{idpType:"openid"})}))}}]);
//# sourceMappingURL=1377.6fbc40f3.chunk.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"static/js/1377.6fbc40f3.chunk.js","mappings":"6KAiCA,WAAeA,EAAAA,EAAAA,IANA,SAACC,GAAD,OAAkBC,EAAAA,EAAAA,GAAa,CAAC,EAAhC,GAMf,EAJgC,SAAC,GAA8C,EAA5CC,QACjC,OAAO,SAAC,IAAD,CAAmBC,QAAS,UACpC,G","sources":["screens/Console/IDP/IDPOpenIDConfigurations.tsx"],"sourcesContent":["// This file is part of MinIO Console Server\n// Copyright (c) 2022 MinIO, Inc.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <http://www.gnu.org/licenses/>.\n\nimport React from \"react\";\n\nimport { Theme } from \"@mui/material/styles\";\nimport createStyles from \"@mui/styles/createStyles\";\nimport withStyles from \"@mui/styles/withStyles\";\nimport IDPConfigurations from \"./IDPConfigurations\";\n\ntype IDPOpenIDConfigurationsProps = {\n classes?: any;\n};\n\nconst styles = (theme: Theme) => createStyles({});\n\nconst IDPOpenIDConfigurations = ({ classes }: IDPOpenIDConfigurationsProps) => {\n return <IDPConfigurations idpType={\"openid\"} />;\n};\n\nexport default withStyles(styles)(IDPOpenIDConfigurations);\n"],"names":["withStyles","theme","createStyles","classes","idpType"],"sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self.webpackChunkportal_ui=self.webpackChunkportal_ui||[]).push([[7486],{17486:function(u,e,n){n.r(e);n(72791);var t=n(11135),r=n(25787),s=n(44959),a=n(80184);e.default=(0,r.Z)((function(u){return(0,t.Z)({})}))((function(u){u.classes;return(0,a.jsx)(s.Z,{idpType:"ldap"})}))}}]);
//# sourceMappingURL=7486.83e0d248.chunk.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"static/js/7486.83e0d248.chunk.js","mappings":"6KAiCA,WAAeA,EAAAA,EAAAA,IANA,SAACC,GAAD,OAAkBC,EAAAA,EAAAA,GAAa,CAAC,EAAhC,GAMf,EAJ8B,SAAC,GAA4C,EAA1CC,QAC/B,OAAO,SAAC,IAAD,CAAmBC,QAAS,QACpC,G","sources":["screens/Console/IDP/IDPLDAPConfigurations.tsx"],"sourcesContent":["// This file is part of MinIO Console Server\n// Copyright (c) 2022 MinIO, Inc.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <http://www.gnu.org/licenses/>.\n\nimport React from \"react\";\n\nimport { Theme } from \"@mui/material/styles\";\nimport createStyles from \"@mui/styles/createStyles\";\nimport withStyles from \"@mui/styles/withStyles\";\nimport IDPConfigurations from \"./IDPConfigurations\";\n\ntype IDPLDAPConfigurationsProps = {\n classes?: any;\n};\n\nconst styles = (theme: Theme) => createStyles({});\n\nconst IDPLDAPConfigurations = ({ classes }: IDPLDAPConfigurationsProps) => {\n return <IDPConfigurations idpType={\"ldap\"} />;\n};\n\nexport default withStyles(styles)(IDPLDAPConfigurations);\n"],"names":["withStyles","theme","createStyles","classes","idpType"],"sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

View File

@@ -30,7 +30,7 @@
"kbar": "^0.1.0-beta.34",
"local-storage-fallback": "^4.1.1",
"lodash": "^4.17.21",
"mds": "https://github.com/minio/mds.git#v0.0.5",
"mds": "https://github.com/minio/mds.git#v0.0.7",
"minio": "^7.0.28",
"moment": "^2.29.4",
"react": "^18.1.0",

View File

@@ -39,7 +39,14 @@ const MainRouter = () => {
</Suspense>
}
/>
<Route path="/logout" element={<Logout />} />
<Route
path="/logout"
element={
<Suspense fallback={<LoadingComponent />}>
<Logout />
</Suspense>
}
/>
<Route
path="/login"
element={

View File

@@ -35,6 +35,7 @@ import { SRInfoStateType } from "./types";
import { AppState, useAppDispatch } from "./store";
import { saveSessionResponse } from "./screens/Console/consoleSlice";
import { getOverrideColorVariants } from "./utils/stylesUtils";
import LoadingComponent from "./common/LoadingComponent";
interface ProtectedRouteProps {
Component: any;
@@ -119,7 +120,7 @@ const ProtectedRoute = ({ Component }: ProtectedRouteProps) => {
// if we're still trying to retrieve user session render nothing
if (sessionLoading) {
return null;
return <LoadingComponent />;
}
// redirect user to the right page based on session status
return userLoggedIn ? <Component /> : <StorePathAndRedirect />;

View File

@@ -30,8 +30,6 @@ const LoadingComponent = () => {
>
<Grid item xs={3} style={{ textAlign: "center" }}>
<Loader style={{ width: 35, height: 35 }} />
<br />
Loading...
</Grid>
</Grid>
);

View File

@@ -130,6 +130,15 @@ export const IAM_PAGES = {
ACCOUNT_ADD: "/access-keys/new-account",
USER_SA_ACCOUNT_ADD: "/identity/users/new-user-sa/:userName",
/* IDP */
IDP_LDAP_CONFIGURATIONS: "/idp/ldap/configurations",
IDP_LDAP_CONFIGURATIONS_VIEW: "/idp/ldap/configurations/:idpName",
IDP_LDAP_CONFIGURATIONS_ADD: "/idp/ldap/configurations/add-idp",
IDP_OPENID_CONFIGURATIONS: "/idp/openid/configurations",
IDP_OPENID_CONFIGURATIONS_VIEW: "/idp/openid/configurations/:idpName",
IDP_OPENID_CONFIGURATIONS_ADD: "/idp/openid/configurations/add-idp",
POLICIES: "/identity/policies",
POLICY_ADD: "/identity/add-policy",
POLICIES_VIEW: "/identity/policies/*",
@@ -430,6 +439,30 @@ export const IAM_PAGES_PERMISSIONS = {
IAM_SCOPES.ADMIN_SERVER_INFO,
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
],
[IAM_PAGES.IDP_LDAP_CONFIGURATIONS]: [
IAM_SCOPES.ADMIN_ALL_ACTIONS,
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
],
[IAM_PAGES.IDP_LDAP_CONFIGURATIONS_ADD]: [
IAM_SCOPES.ADMIN_ALL_ACTIONS,
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
],
[IAM_PAGES.IDP_LDAP_CONFIGURATIONS_VIEW]: [
IAM_SCOPES.ADMIN_ALL_ACTIONS,
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
],
[IAM_PAGES.IDP_OPENID_CONFIGURATIONS]: [
IAM_SCOPES.ADMIN_ALL_ACTIONS,
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
],
[IAM_PAGES.IDP_OPENID_CONFIGURATIONS_ADD]: [
IAM_SCOPES.ADMIN_ALL_ACTIONS,
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
],
[IAM_PAGES.IDP_OPENID_CONFIGURATIONS_VIEW]: [
IAM_SCOPES.ADMIN_ALL_ACTIONS,
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
],
};
export const S3_ALL_RESOURCES = "arn:aws:s3:::*";

View File

@@ -42,7 +42,7 @@ import {
setIsVersioned,
setLoadingLocking,
setLoadingObjectInfo,
setLoadingObjectsList,
setLoadingObjects,
setLoadingRecords,
setLoadingVersioning,
setLoadingVersions,
@@ -82,12 +82,16 @@ const styles = (theme: Theme) =>
let objectsWS: WebSocket;
let currentRequestID: number = 0;
let errorCounter: number = 0;
let wsInFlight: boolean = false;
const initWSConnection = (
onMessageCallback: (message: IMessageEvent) => void,
openCallback?: () => void,
notAvailableCallback?: () => void
onMessageCallback?: (message: IMessageEvent) => void
) => {
if (wsInFlight) {
return;
}
wsInFlight = true;
const url = new URL(window.location.toString());
const isDev = process.env.NODE_ENV === "development";
const port = isDev ? "9090" : url.port;
@@ -103,25 +107,28 @@ const initWSConnection = (
);
objectsWS.onopen = () => {
wsInFlight = false;
if (openCallback) {
openCallback();
}
errorCounter = 0;
};
if (onMessageCallback) {
objectsWS.onmessage = onMessageCallback;
}
const reconnectFn = () => {
if (errorCounter <= 5) {
initWSConnection(onMessageCallback, openCallback);
initWSConnection(openCallback, onMessageCallback);
errorCounter += 1;
} else {
console.error("Websocket not available.");
if (notAvailableCallback) {
notAvailableCallback();
}
}
};
objectsWS.onclose = () => {
wsInFlight = false;
console.warn("Websocket Disconnected. Attempting Reconnection...");
// We reconnect after 3 seconds
@@ -129,6 +136,7 @@ const initWSConnection = (
};
objectsWS.onerror = () => {
wsInFlight = false;
console.error("Error in websocket connection. Attempting reconnection...");
// We reconnect after 3 seconds
@@ -136,8 +144,6 @@ const initWSConnection = (
};
};
initWSConnection(() => {});
const BrowserHandler = () => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
@@ -201,10 +207,10 @@ const BrowserHandler = () => {
const obOnly = !!features?.includes("object-browser-only");
/*WS Request Handlers*/
objectsWS.onmessage = useCallback(
const onMessageCallBack = useCallback(
(message: IMessageEvent) => {
// reset start status
dispatch(setLoadingObjectsList(false));
dispatch(setLoadingObjects(false));
const response: WebsocketResponse = JSON.parse(message.data.toString());
if (currentRequestID === response.request_id) {
@@ -250,7 +256,7 @@ const BrowserHandler = () => {
// This indicates final messages is received.
if (response.request_end) {
dispatch(setLoadingObjectsList(false));
dispatch(setLoadingObjects(false));
dispatch(setLoadingRecords(false));
return;
}
@@ -283,7 +289,7 @@ const BrowserHandler = () => {
// We store the new ID for the requestID
currentRequestID = newRequestID;
} catch (e) {
console.log(e);
console.error(e);
}
} else {
// Socket is disconnected, we request reconnection but will need to recreate call
@@ -291,10 +297,10 @@ const BrowserHandler = () => {
initWSRequest(path, date);
};
initWSConnection(dupRequest);
initWSConnection(dupRequest, onMessageCallBack);
}
},
[bucketName, rewindEnabled, showDeleted, dispatch]
[bucketName, rewindEnabled, showDeleted, dispatch, onMessageCallBack]
);
useEffect(() => {
@@ -394,7 +400,7 @@ const BrowserHandler = () => {
initWSRequest(pathPrefix, requestDate);
} else {
dispatch(setLoadingObjectsList(false));
dispatch(setLoadingObjects(false));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [

View File

@@ -64,7 +64,7 @@ import { selFeatures } from "../../consoleSlice";
import AutoColorIcon from "../../Common/Components/AutoColorIcon";
import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
import AButton from "../../Common/AButton/AButton";
import { setLoadingObjectsList } from "../../ObjectBrowser/objectBrowserSlice";
import { setLoadingObjects } from "../../ObjectBrowser/objectBrowserSlice";
const styles = (theme: Theme) =>
createStyles({
@@ -124,7 +124,7 @@ const ListBuckets = ({ classes }: IListBucketsProps) => {
.then((res: BucketList) => {
setLoading(false);
setRecords(res.buckets || []);
dispatch(setLoadingObjectsList(true));
dispatch(setLoadingObjects(true));
})
.catch((err: ErrorResponseHandler) => {
setLoading(false);

View File

@@ -96,7 +96,7 @@ import {
resetMessages,
resetRewind,
setDownloadRenameModal,
setLoadingObjectsList,
setLoadingObjects,
setLoadingRecords,
setLoadingVersions,
setNewObject,
@@ -317,7 +317,7 @@ const ListObjects = () => {
useEffect(() => {
dispatch(setSearchObjects(""));
dispatch(setLoadingObjectsList(true));
dispatch(setLoadingObjects(true));
dispatch(setSelectedObjects([]));
}, [simplePath, dispatch]);
@@ -424,7 +424,7 @@ const ListObjects = () => {
if (refresh) {
dispatch(setSnackBarMessage(`Objects deleted successfully.`));
dispatch(setSelectedObjects([]));
dispatch(setLoadingObjectsList(true));
dispatch(setLoadingObjects(true));
}
};
@@ -595,7 +595,7 @@ const ListObjects = () => {
};
xhr.onloadend = () => {
if (files.length === 0) {
dispatch(setLoadingObjectsList(true));
dispatch(setLoadingObjects(true));
}
};
xhr.onabort = () => {
@@ -650,7 +650,7 @@ const ListObjects = () => {
dispatch(setErrorSnackMessage(err));
}
// We force objects list reload after all promises were handled
dispatch(setLoadingObjectsList(true));
dispatch(setLoadingObjects(true));
dispatch(setSelectedObjects([]));
});
};
@@ -736,7 +736,7 @@ const ListObjects = () => {
dispatch(setSelectedObjects([]));
if (forceRefresh) {
dispatch(setLoadingObjectsList(true));
dispatch(setLoadingObjects(true));
}
};
@@ -949,7 +949,7 @@ const ListObjects = () => {
} else {
dispatch(resetMessages());
dispatch(setLoadingRecords(true));
dispatch(setLoadingObjectsList(true));
dispatch(setLoadingObjects(true));
}
}}
disabled={

View File

@@ -27,7 +27,7 @@ import { AppState, useAppDispatch } from "../../../../../../store";
import { selFeatures } from "../../../../consoleSlice";
import { encodeURLString } from "../../../../../../common/utils";
import {
setLoadingObjectsList,
setLoadingObjects,
setLoadingVersions,
setObjectDetailsView,
setSelectedObjects,
@@ -168,7 +168,7 @@ const ListObjectsTable = () => {
const newSortDirection = get(sortData, "sortDirection", "DESC");
setCurrentSortField(sortData.sortBy);
setSortDirection(newSortDirection);
dispatch(setLoadingObjectsList(true));
dispatch(setLoadingObjects(true));
};
const selectAllItems = () => {

View File

@@ -25,7 +25,7 @@ import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapp
import { AppState, useAppDispatch } from "../../../../../../store";
import {
resetRewind,
setLoadingObjectsList,
setLoadingObjects,
setRewindEnable,
} from "../../../../ObjectBrowser/objectBrowserSlice";
@@ -73,7 +73,7 @@ const RewindEnable = ({
})
);
}
dispatch(setLoadingObjectsList(true));
dispatch(setLoadingObjects(true));
closeModalAndRefresh();
};

View File

@@ -122,6 +122,24 @@ const AccountCreate = React.lazy(
const Users = React.lazy(() => import("./Users/Users"));
const Groups = React.lazy(() => import("./Groups/Groups"));
const IDPLDAPConfigurations = React.lazy(
() => import("./IDP/IDPLDAPConfigurations")
);
const IDPOpenIDConfigurations = React.lazy(
() => import("./IDP/IDPOpenIDConfigurations")
);
const AddIDPLDAPConfiguration = React.lazy(
() => import("./IDP/AddIDPLDAPConfiguration")
);
const AddIDPOpenIDConfiguration = React.lazy(
() => import("./IDP/AddIDPOpenIDConfiguration")
);
const IDPLDAPConfigurationDetails = React.lazy(
() => import("./IDP/IDPLDAPConfigurationDetails")
);
const IDPOpenIDConfigurationDetails = React.lazy(
() => import("./IDP/IDPOpenIDConfigurationDetails")
);
const TenantDetails = React.lazy(
() => import("./Tenants/TenantDetails/TenantDetails")
@@ -343,6 +361,30 @@ const Console = ({ classes }: IConsoleProps) => {
component: Policies,
path: IAM_PAGES.POLICIES,
},
{
component: IDPLDAPConfigurations,
path: IAM_PAGES.IDP_LDAP_CONFIGURATIONS,
},
{
component: IDPOpenIDConfigurations,
path: IAM_PAGES.IDP_OPENID_CONFIGURATIONS,
},
{
component: AddIDPLDAPConfiguration,
path: IAM_PAGES.IDP_LDAP_CONFIGURATIONS_ADD,
},
{
component: AddIDPOpenIDConfiguration,
path: IAM_PAGES.IDP_OPENID_CONFIGURATIONS_ADD,
},
{
component: IDPLDAPConfigurationDetails,
path: IAM_PAGES.IDP_LDAP_CONFIGURATIONS_VIEW,
},
{
component: IDPOpenIDConfigurationDetails,
path: IAM_PAGES.IDP_OPENID_CONFIGURATIONS_VIEW,
},
{
component: Heal,
path: IAM_PAGES.TOOLS_HEAL,

View File

@@ -0,0 +1,219 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useState } from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Box, Grid } from "@mui/material";
import {
formFieldStyles,
modalBasic,
} from "../Common/FormComponents/common/styleLibrary";
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { Button } from "mds";
import { useNavigate } from "react-router-dom";
import { ErrorResponseHandler } from "../../../common/types";
import { useAppDispatch } from "../../../store";
import {
setErrorSnackMessage,
setServerNeedsRestart,
} from "../../../systemSlice";
import useApi from "../Common/Hooks/useApi";
import PageHeader from "../Common/PageHeader/PageHeader";
import BackLink from "../../../common/BackLink";
import PageLayout from "../Common/Layout/PageLayout";
import SectionTitle from "../Common/SectionTitle";
type AddIDPConfigurationProps = {
classes?: any;
icon: React.ReactNode;
helpBox: React.ReactNode;
header: string;
title: string;
backLink: string;
formFields: object;
endpoint: string;
};
const styles = (theme: Theme) =>
createStyles({
...formFieldStyles,
...modalBasic,
});
const AddIDPConfiguration = ({
classes,
icon,
helpBox,
header,
backLink,
title,
formFields,
endpoint,
}: AddIDPConfigurationProps) => {
const extraFormFields = {
name: {
required: true,
hasError: (s: string, editMode: boolean) => {
return !s && editMode ? "Config Name is required" : "";
},
label: "Name",
tooltip: "Name for identity provider configuration",
placeholder: "Name",
type: "text",
},
...formFields,
};
const navigate = useNavigate();
const dispatch = useAppDispatch();
const [fields, setFields] = useState<any>({});
const onSuccess = (res: any) => {
navigate(backLink);
dispatch(setServerNeedsRestart(res.restart === true));
};
const onError = (err: ErrorResponseHandler) =>
dispatch(setErrorSnackMessage(err));
const [loading, invokeApi] = useApi(onSuccess, onError);
const validSave = () => {
for (const [key, value] of Object.entries(extraFormFields)) {
if (
value.required &&
!(
fields[key] !== undefined &&
fields[key] !== null &&
fields[key] !== ""
)
) {
return false;
}
}
return true;
};
const resetForm = () => {
setFields({});
};
const addRecord = (event: React.FormEvent) => {
event.preventDefault();
const name = fields["name"];
let input = "";
for (const key of Object.keys(formFields)) {
if (fields[key]) {
input += `${key}=${fields[key]} `;
}
}
invokeApi("POST", endpoint, { name, input });
};
return (
<Grid item xs={12}>
<PageHeader label={<BackLink to={backLink} label={header} />} />
<PageLayout>
<Box
sx={{
display: "grid",
padding: "25px",
gap: "25px",
gridTemplateColumns: {
md: "2fr 1.2fr",
xs: "1fr",
},
border: "1px solid #eaeaea",
}}
>
<Box>
<SectionTitle icon={icon}>{title}</SectionTitle>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
addRecord(e);
}}
>
<Grid container item spacing="20" sx={{ marginTop: 1 }}>
<Grid xs={12} item>
{Object.entries(extraFormFields).map(([key, value]) => (
<Grid
item
xs={12}
className={classes.formFieldRow}
key={key}
>
<InputBoxWrapper
id={key}
required={value.required}
name={key}
label={value.label}
tooltip={value.tooltip}
error={value.hasError(fields[key], true)}
value={fields[key] ? fields[key] : ""}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setFields({ ...fields, [key]: e.target.value })
}
placeholder={value.placeholder}
type={value.type}
/>
</Grid>
))}
<Grid item xs={12} textAlign={"right"}>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
marginTop: "20px",
gap: "15px",
}}
>
<Button
id={"clear"}
type="button"
variant="regular"
onClick={resetForm}
label={"Clear"}
/>
<Button
id={"save-key"}
type="submit"
variant="callAction"
color="primary"
disabled={loading || !validSave()}
label={"Save"}
/>
</Box>
</Grid>
</Grid>
</Grid>
</form>
</Box>
{helpBox}
</Box>
</PageLayout>
</Grid>
);
};
export default withStyles(styles)(AddIDPConfiguration);

View File

@@ -0,0 +1,106 @@
import React, { Fragment } from "react";
import { Box } from "@mui/material";
import { HelpIconFilled } from "../../../icons";
interface IContent {
icon: React.ReactNode;
text: string;
iconDescription: string;
}
interface IAddIDPConfigurationHelpBoxProps {
helpText: string;
docLink: string;
docText: string;
contents: IContent[];
}
const FeatureItem = ({
icon,
description,
}: {
icon: any;
description: string;
}) => {
return (
<Box
sx={{
display: "flex",
"& .min-icon": {
marginRight: "10px",
height: "23px",
width: "23px",
marginBottom: "10px",
},
}}
>
{icon}{" "}
<div style={{ fontSize: "14px", fontStyle: "italic", color: "#5E5E5E" }}>
{description}
</div>
</Box>
);
};
const AddIDPConfigurationHelpBox = ({
helpText,
docLink,
docText,
contents,
}: IAddIDPConfigurationHelpBoxProps) => {
return (
<Box
sx={{
flex: 1,
border: "1px solid #eaeaea",
borderRadius: "2px",
display: "flex",
flexFlow: "column",
padding: "20px",
}}
>
<Box
sx={{
fontSize: "16px",
fontWeight: 600,
display: "flex",
alignItems: "center",
marginBottom: "16px",
paddingBottom: "20px",
"& .min-icon": {
height: "21px",
width: "21px",
marginRight: "15px",
},
}}
>
<HelpIconFilled />
<div>{helpText}</div>
</Box>
<Box sx={{ fontSize: "14px", marginBottom: "15px" }}>
{contents.map((content) => (
<Fragment>
{content.icon && (
<Box sx={{ paddingBottom: "20px" }}>
<FeatureItem
icon={content.icon}
description={content.iconDescription}
/>
</Box>
)}
<Box sx={{ paddingBottom: "20px" }}>{content.text}</Box>
</Fragment>
))}
<Box sx={{ paddingBottom: "20px" }}>
<a href={docLink} target="_blank" rel="noreferrer">
{docText}
</a>
</Box>
</Box>
</Box>
);
};
export default AddIDPConfigurationHelpBox;

View File

@@ -0,0 +1,86 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import LoginIcon from "@mui/icons-material/Login";
import {
formFieldStyles,
modalBasic,
} from "../Common/FormComponents/common/styleLibrary";
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
import AddIDPConfiguration from "./AddIDPConfiguration";
import { ldapFormFields } from "./utils";
import AddIDPConfigurationHelpBox from "./AddIDPConfigurationHelpbox";
type AddIDPLDAPConfigurationProps = {
classes?: any;
};
const styles = (theme: Theme) =>
createStyles({
...formFieldStyles,
formFieldRow: {
...formFieldStyles.formFieldRow,
},
...modalBasic,
});
const AddIDPLDAPConfiguration = ({ classes }: AddIDPLDAPConfigurationProps) => {
const helpBoxContents = [
{
text: "MinIO supports using an Active Directory or LDAP (AD/LDAP) service for external management of user identities. Configuring an external IDentity Provider (IDP) enables Single-Sign On (SSO) workflows, where applications authenticate against the external IDP before accessing MinIO.",
icon: <LoginIcon />,
iconDescription: "Create Configurations",
},
{
text: "MinIO queries the configured Active Directory / LDAP server to verify the credentials specified by the application and optionally return a list of groups in which the user has membership. MinIO supports two modes (Lookup-Bind Mode and Username-Bind Mode) for performing these queries",
icon: null,
iconDescription: "",
},
{
text: "MinIO recommends using Lookup-Bind mode as the preferred method for verifying AD/LDAP credentials. Username-Bind mode is a legacy method retained for backwards compatibility only.",
icon: null,
iconDescription: "",
},
];
return (
<AddIDPConfiguration
icon={<LoginIcon />}
helpBox={
<AddIDPConfigurationHelpBox
helpText={"Learn more about LDAP Configurations"}
contents={helpBoxContents}
docLink={
"https://min.io/docs/minio/linux/operations/external-iam.html?ref=con#minio-external-iam-ad-ldap"
}
docText={"Learn more about LDAP Configurations"}
/>
}
header={"LDAP Configurations"}
backLink={IAM_PAGES.IDP_LDAP_CONFIGURATIONS}
title={"Create LDAP Configuration"}
endpoint={"/api/v1/idp/ldap/"}
formFields={ldapFormFields}
/>
);
};
export default withStyles(styles)(AddIDPLDAPConfiguration);

View File

@@ -0,0 +1,71 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
import { LockIcon } from "../../../icons";
import AddIDPConfiguration from "./AddIDPConfiguration";
import { openIDFormFields } from "./utils";
import AddIDPConfigurationHelpBox from "./AddIDPConfigurationHelpbox";
type AddIDPOpenIDConfigurationProps = {
classes?: any;
};
const styles = (theme: Theme) => createStyles({});
const AddIDPOpenIDConfiguration = ({
classes,
}: AddIDPOpenIDConfigurationProps) => {
const helpBoxContents = [
{
text: "MinIO supports using an OpenID Connect (OIDC) compatible IDentity Provider (IDP) such as Okta, KeyCloak, Dex, Google, or Facebook for external management of user identities.",
icon: <LockIcon />,
iconDescription: "Create Configurations",
},
{
text: "Configuring an external IDP enables Single-Sign On workflows, where applications authenticate against the external IDP before accessing MinIO.",
icon: null,
iconDescription: "",
},
];
return (
<AddIDPConfiguration
icon={<LockIcon />}
helpBox={
<AddIDPConfigurationHelpBox
helpText={"Learn more about OpenID Connect Configurations"}
contents={helpBoxContents}
docLink={
"https://min.io/docs/minio/linux/operations/external-iam.html?ref=con#minio-external-iam-oidc"
}
docText={"Learn more about OpenID Connect Configurations"}
/>
}
header={"OpenID Configurations"}
backLink={IAM_PAGES.IDP_OPENID_CONFIGURATIONS}
title={"Create OpenID Configuration"}
endpoint={"/api/v1/idp/openid/"}
formFields={openIDFormFields}
/>
);
};
export default withStyles(styles)(AddIDPOpenIDConfiguration);

View File

@@ -0,0 +1,86 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { DialogContentText } from "@mui/material";
import { ErrorResponseHandler } from "../../../common/types";
import useApi from "../Common/Hooks/useApi";
import ConfirmDialog from "../Common/ModalWrapper/ConfirmDialog";
import { ConfirmDeleteIcon } from "../../../icons";
import {
setErrorSnackMessage,
setServerNeedsRestart,
} from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
interface IDeleteIDPConfigurationModalProps {
closeDeleteModalAndRefresh: (refresh: boolean) => void;
deleteOpen: boolean;
idp: string;
idpType: string;
}
const DeleteIDPConfigurationModal = ({
closeDeleteModalAndRefresh,
deleteOpen,
idp,
idpType,
}: IDeleteIDPConfigurationModalProps) => {
const dispatch = useAppDispatch();
const onDelSuccess = (res: any) => {
closeDeleteModalAndRefresh(true);
dispatch(setServerNeedsRestart(res.restart === true));
};
const onDelError = (err: ErrorResponseHandler) =>
dispatch(setErrorSnackMessage(err));
const onClose = () => closeDeleteModalAndRefresh(false);
const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
if (!idp) {
return null;
}
const onConfirmDelete = () => {
invokeDeleteApi("DELETE", `/api/v1/idp/${idpType}/${idp}`);
};
const displayName = idp === "_" ? "Default" : idp;
return (
<ConfirmDialog
title={`Delete ${displayName}`}
confirmText={"Delete"}
isOpen={deleteOpen}
titleIcon={<ConfirmDeleteIcon />}
isLoading={deleteLoading}
onConfirm={onConfirmDelete}
onClose={onClose}
confirmButtonProps={{
disabled: deleteLoading,
}}
confirmationContent={
<DialogContentText>
Are you sure you want to delete IDP <b>{displayName}</b>{" "}
configuration? <br />
</DialogContentText>
}
/>
);
};
export default DeleteIDPConfigurationModal;

View File

@@ -0,0 +1,364 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Box, Grid } from "@mui/material";
import {
buttonsStyles,
containerForHeader,
formFieldStyles,
hrClass,
modalBasic,
pageContentStyles,
searchField,
} from "../Common/FormComponents/common/styleLibrary";
import { RefreshIcon, TrashIcon } from "../../../icons";
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { Button } from "mds";
import { useNavigate, useParams } from "react-router-dom";
import { ErrorResponseHandler } from "../../../common/types";
import { useAppDispatch } from "../../../store";
import {
setErrorSnackMessage,
setServerNeedsRestart,
} from "../../../systemSlice";
import useApi from "../Common/Hooks/useApi";
import api from "../../../common/api";
import PageLayout from "../Common/Layout/PageLayout";
import PageHeader from "../Common/PageHeader/PageHeader";
import BackLink from "../../../common/BackLink";
import ScreenTitle from "../Common/ScreenTitle/ScreenTitle";
import DeleteIDPConfigurationModal from "./DeleteIDPConfigurationModal";
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
type IDPConfigurationDetailsProps = {
classes?: any;
formFields: object;
endpoint: string;
backLink: string;
header: string;
idpType: string;
icon: React.ReactNode;
};
const styles = (theme: Theme) =>
createStyles({
...formFieldStyles,
formFieldRow: {
...formFieldStyles.formFieldRow,
},
...modalBasic,
pageContainer: {
height: "100%",
},
screenTitle: {
border: 0,
paddingTop: 0,
},
...pageContentStyles,
...searchField,
capitalize: {
textTransform: "capitalize",
},
...hrClass,
...buttonsStyles,
...containerForHeader(theme.spacing(4)),
});
const IDPConfigurationDetails = ({
classes,
formFields,
endpoint,
backLink,
header,
idpType,
icon,
}: IDPConfigurationDetailsProps) => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const params = useParams();
const configurationName = params.idpName;
const [loading, setLoading] = useState<boolean>(true);
const [isEnabled, setIsEnabled] = useState<boolean>(false);
const [fields, setFields] = useState<any>({});
const [originalFields, setOriginalFields] = useState<any>({});
const [record, setRecord] = useState<any>({});
const [editMode, setEditMode] = useState<boolean>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const onSuccess = (res: any) => {
dispatch(setServerNeedsRestart(res.restart === true));
};
const onError = (err: ErrorResponseHandler) =>
dispatch(setErrorSnackMessage(err));
const [loadingSave, invokeApi] = useApi(onSuccess, onError);
const onEnabledSuccess = (res: any) => {
setIsEnabled(!isEnabled);
dispatch(setServerNeedsRestart(res.restart === true));
};
const onEnabledError = (err: ErrorResponseHandler) => {
dispatch(setErrorSnackMessage(err));
};
const [loadingEnabledSave, invokeEnabledApi] = useApi(
onEnabledSuccess,
onEnabledError
);
const toggleEditMode = () => {
if (editMode) {
parseFields(record);
}
setEditMode(!editMode);
};
const parseFields = (record: any) => {
let fields: any = {};
if (record.info) {
record.info.forEach((item: any) => {
if (item.key === "enable") {
setIsEnabled(item.value === "on");
}
fields[item.key] = item.value;
});
}
setFields(fields);
};
const parseOriginalFields = (record: any) => {
let fields: any = {};
if (record.info) {
record.info.forEach((item: any) => {
fields[item.key] = item.value;
});
}
setOriginalFields(fields);
};
useEffect(() => {
setLoading(true);
}, []);
useEffect(() => {
const loadRecord = () => {
api
.invoke("GET", `${endpoint}${configurationName}`)
.then((result: any) => {
if (result) {
setRecord(result);
parseFields(result);
parseOriginalFields(result);
}
setLoading(false);
})
.catch((err: ErrorResponseHandler) => {
dispatch(setErrorSnackMessage(err));
setLoading(false);
});
};
if (loading) {
loadRecord();
}
}, [dispatch, loading, configurationName, endpoint]);
const validSave = () => {
for (const [key, value] of Object.entries(formFields)) {
if (
value.required &&
!(
fields[key] !== undefined &&
fields[key] !== null &&
fields[key] !== ""
)
) {
return false;
}
}
return true;
};
const resetForm = () => {
setFields({});
};
const saveRecord = (event: React.FormEvent) => {
event.preventDefault();
let input = "";
for (const key of Object.keys(formFields)) {
if (fields[key] || fields[key] !== originalFields[key]) {
input += `${key}=${fields[key]} `;
}
}
invokeApi("PUT", `${endpoint}${configurationName}`, { input });
setEditMode(false);
};
const closeDeleteModalAndRefresh = async (refresh: boolean) => {
setDeleteOpen(false);
if (refresh) {
navigate(backLink);
}
};
const toggleConfiguration = (value: boolean) => {
const input = `enable=${value ? "on" : "off"}`;
invokeEnabledApi("PUT", `${endpoint}${configurationName}`, { input });
};
return (
<Grid item xs={12}>
{deleteOpen && configurationName && (
<DeleteIDPConfigurationModal
deleteOpen={deleteOpen}
idp={configurationName}
idpType={idpType}
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
/>
)}
<PageHeader
label={<BackLink to={backLink} label={header} />}
actions={
<FormSwitchWrapper
label={""}
indicatorLabels={["Enabled", "Disabled"]}
checked={isEnabled}
value={"is-configuration-enabled"}
id={"is-configuration-enabled"}
name={"is-configuration-enabled"}
onChange={(e) => toggleConfiguration(e.target.checked)}
description=""
disabled={loadingEnabledSave}
/>
}
/>
<PageLayout className={classes.pageContainer}>
<Grid item xs={12}>
<ScreenTitle
classes={{
screenTitle: classes.screenTitle,
}}
icon={icon}
title={configurationName === "_" ? "Default" : configurationName}
actions={
<Fragment>
{configurationName !== "_" && (
<Button
id={"delete-idp-config"}
onClick={() => {
setDeleteOpen(true);
}}
label={"Delete Configuration"}
icon={<TrashIcon />}
variant={"secondary"}
/>
)}
<Button
id={"refresh-idp-config"}
onClick={() => setLoading(true)}
label={"Refresh"}
icon={<RefreshIcon />}
/>
</Fragment>
}
/>
</Grid>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
saveRecord(e);
}}
>
<Grid container item spacing="20" sx={{ marginTop: 1 }}>
<Grid xs={12} item className={classes.fieldBox}>
{Object.entries(formFields).map(([key, value]) => (
<Grid item xs={12} className={classes.formFieldRow} key={key}>
<InputBoxWrapper
id={key}
required={value.required}
name={key}
label={value.label}
tooltip={value.tooltip}
error={value.hasError(fields[key], editMode)}
value={fields[key] ? fields[key] : ""}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setFields({ ...fields, [key]: e.target.value })
}
placeholder={value.placeholder}
disabled={!editMode}
type={value.type}
/>
</Grid>
))}
<Grid item xs={12} textAlign={"right"}>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
marginTop: "20px",
gap: "15px",
}}
>
<Button
id={"edit"}
type="button"
variant={editMode ? "regular" : "callAction"}
onClick={toggleEditMode}
label={editMode ? "Cancel" : "Edit"}
/>
{editMode && (
<Button
id={"clear"}
type="button"
variant="regular"
onClick={resetForm}
label={"Clear"}
/>
)}
{editMode && (
<Button
id={"save-key"}
type="submit"
variant="callAction"
color="primary"
disabled={loading || loadingSave || !validSave()}
label={"Save"}
/>
)}
</Box>
</Grid>
</Grid>
</Grid>
</form>
</PageLayout>
</Grid>
);
};
export default withStyles(styles)(IDPConfigurationDetails);

View File

@@ -0,0 +1,225 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { useAppDispatch } from "../../../store";
import { useNavigate } from "react-router-dom";
import {
CONSOLE_UI_RESOURCE,
IAM_SCOPES,
} from "../../../common/SecureComponent/permissions";
import {
hasPermission,
SecureComponent,
} from "../../../common/SecureComponent";
import api from "../../../common/api";
import { ErrorResponseHandler } from "../../../common/types";
import { setErrorSnackMessage } from "../../../systemSlice";
import PageHeader from "../Common/PageHeader/PageHeader";
import PageLayout from "../Common/Layout/PageLayout";
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
import { Grid } from "@mui/material";
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
import { Button } from "mds";
import { AddIcon, RefreshIcon } from "../../../icons";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import DeleteIDPConfigurationModal from "./DeleteIDPConfigurationModal";
type IDPConfigurationsProps = {
classes?: any;
idpType: string;
};
const styles = (theme: Theme) =>
createStyles({
...containerForHeader(theme.spacing(4)),
});
const IDPConfigurations = ({ classes, idpType }: IDPConfigurationsProps) => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [selectedIDP, setSelectedIDP] = useState<string>("");
const [loading, setLoading] = useState<boolean>(false);
const [records, setRecords] = useState<[]>([]);
const deleteIDP = hasPermission(CONSOLE_UI_RESOURCE, [
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
]);
const viewIDP = hasPermission(CONSOLE_UI_RESOURCE, [
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
]);
const displayIDPs = hasPermission(CONSOLE_UI_RESOURCE, [
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
]);
useEffect(() => {
fetchRecords();
}, []);
useEffect(() => {
if (loading) {
if (displayIDPs) {
api
.invoke("GET", `/api/v1/idp/${idpType}`)
.then((res) => {
setLoading(false);
setRecords(
res.results.map((r: any) => {
r.name = r.name === "_" ? "Default" : r.name;
r.enabled = r.enabled === true ? "Enabled" : "Disabled";
return r;
})
);
})
.catch((err: ErrorResponseHandler) => {
setLoading(false);
dispatch(setErrorSnackMessage(err));
});
} else {
setLoading(false);
}
}
}, [loading, setLoading, setRecords, dispatch, displayIDPs, idpType]);
const fetchRecords = () => {
setLoading(true);
};
const confirmDeleteIDP = (idp: string) => {
setDeleteOpen(true);
idp = idp === "Default" ? "_" : idp;
setSelectedIDP(idp);
};
const viewAction = (idp: any) => {
let name = idp.name === "Default" ? "_" : idp.name;
navigate(`/idp/${idpType}/configurations/${name}`);
};
const closeDeleteModalAndRefresh = async (refresh: boolean) => {
setDeleteOpen(false);
if (refresh) {
fetchRecords();
}
};
const tableActions = [
{
type: "view",
onClick: viewAction,
disableButtonFunction: () => !viewIDP,
},
{
type: "delete",
onClick: confirmDeleteIDP,
sendOnlyId: true,
disableButtonFunction: (idp: string) => !deleteIDP || idp === "Default",
},
];
return (
<Fragment>
{deleteOpen && (
<DeleteIDPConfigurationModal
deleteOpen={deleteOpen}
idp={selectedIDP}
idpType={idpType}
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
/>
)}
<PageHeader label={`${idpType.toUpperCase()} Configurations`} />
<PageLayout className={classes.pageContainer}>
<Grid container spacing={1}>
<Grid
item
xs={12}
display={"flex"}
alignItems={"center"}
justifyContent={"flex-end"}
sx={{
"& button": {
marginLeft: "8px",
},
}}
>
<SecureComponent
scopes={[IAM_SCOPES.ADMIN_CONFIG_UPDATE]}
resource={CONSOLE_UI_RESOURCE}
errorProps={{ disabled: true }}
>
<TooltipWrapper tooltip={"Refresh"}>
<Button
id={"refresh-keys"}
variant="regular"
icon={<RefreshIcon />}
onClick={() => setLoading(true)}
/>
</TooltipWrapper>
</SecureComponent>
<SecureComponent
scopes={[IAM_SCOPES.ADMIN_CONFIG_UPDATE]}
resource={CONSOLE_UI_RESOURCE}
errorProps={{ disabled: true }}
>
<TooltipWrapper tooltip={`Create ${idpType} configuration`}>
<Button
id={"create-idp"}
label={"Create Configuration"}
variant={"callAction"}
icon={<AddIcon />}
onClick={() =>
navigate(`/idp/${idpType}/configurations/add-idp`)
}
/>
</TooltipWrapper>
</SecureComponent>
</Grid>
<Grid item xs={12} className={classes.tableBlock}>
<SecureComponent
scopes={[IAM_SCOPES.ADMIN_CONFIG_UPDATE]}
resource={CONSOLE_UI_RESOURCE}
errorProps={{ disabled: true }}
>
<TableWrapper
itemActions={tableActions}
columns={[
{ label: "Name", elementKey: "name" },
{ label: "Type", elementKey: "type" },
{ label: "Enabled", elementKey: "enabled" },
]}
isLoading={loading}
records={records}
entityName="Keys"
idField="name"
/>
</SecureComponent>
</Grid>
</Grid>
</PageLayout>
</Fragment>
);
};
export default withStyles(styles)(IDPConfigurations);

View File

@@ -0,0 +1,48 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { ldapFormFields } from "./utils";
import LoginIcon from "@mui/icons-material/Login";
import IDPConfigurationDetails from "./IDPConfigurationDetails";
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
type IDPLDAPConfigurationDetailsProps = {
classes?: any;
};
const styles = (theme: Theme) => createStyles({});
const IDPLDAPConfigurationDetails = ({
classes,
}: IDPLDAPConfigurationDetailsProps) => {
return (
<IDPConfigurationDetails
backLink={IAM_PAGES.IDP_LDAP_CONFIGURATIONS}
header={"LDAP Configurations"}
endpoint={"/api/v1/idp/ldap/"}
idpType={"ldap"}
formFields={ldapFormFields}
icon={<LoginIcon width={40} />}
/>
);
};
export default withStyles(styles)(IDPLDAPConfigurationDetails);

View File

@@ -0,0 +1,34 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import IDPConfigurations from "./IDPConfigurations";
type IDPLDAPConfigurationsProps = {
classes?: any;
};
const styles = (theme: Theme) => createStyles({});
const IDPLDAPConfigurations = ({ classes }: IDPLDAPConfigurationsProps) => {
return <IDPConfigurations idpType={"ldap"} />;
};
export default withStyles(styles)(IDPLDAPConfigurations);

View File

@@ -0,0 +1,48 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
import { LockIcon } from "../../../icons";
import { openIDFormFields } from "./utils";
import IDPConfigurationDetails from "./IDPConfigurationDetails";
type IDPOpenIDConfigurationDetailsProps = {
classes?: any;
};
const styles = (theme: Theme) => createStyles({});
const IDPOpenIDConfigurationDetails = ({
classes,
}: IDPOpenIDConfigurationDetailsProps) => {
return (
<IDPConfigurationDetails
backLink={IAM_PAGES.IDP_OPENID_CONFIGURATIONS}
header={"OpenID Configurations"}
endpoint={"/api/v1/idp/openid/"}
idpType={"openid"}
formFields={openIDFormFields}
icon={<LockIcon width={40} />}
/>
);
};
export default withStyles(styles)(IDPOpenIDConfigurationDetails);

View File

@@ -0,0 +1,34 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import IDPConfigurations from "./IDPConfigurations";
type IDPOpenIDConfigurationsProps = {
classes?: any;
};
const styles = (theme: Theme) => createStyles({});
const IDPOpenIDConfigurations = ({ classes }: IDPOpenIDConfigurationsProps) => {
return <IDPConfigurations idpType={"openid"} />;
};
export default withStyles(styles)(IDPOpenIDConfigurations);

View File

@@ -0,0 +1,176 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
export const openIDFormFields = {
config_url: {
required: true,
hasError: (s: string, editMode: boolean) => {
return !s && editMode ? "Config URL is required" : "";
},
label: "Config URL",
tooltip: "Config URL for identity provider configuration",
placeholder:
"https://identity-provider-url/.well-known/openid-configuration",
type: "text",
},
client_id: {
required: true,
hasError: (s: string, editMode: boolean) => {
return !s && editMode ? "Client ID is required" : "";
},
label: "Client ID",
tooltip: "Identity provider Client ID",
placeholder: "Enter Client ID",
type: "text",
},
client_secret: {
required: true,
hasError: (s: string, editMode: boolean) => {
return !s && editMode ? "Client Secret is required" : "";
},
label: "Client Secret",
tooltip: "Identity provider Client Secret",
placeholder: "Enter Client Secret",
type: "password",
},
display_name: {
required: false,
label: "Display Name",
tooltip: "Display Name",
placeholder: "Enter Display Name",
type: "text",
hasError: (s: string, editMode: boolean) => "",
},
claim_name: {
required: false,
label: "Claim Name",
tooltip: "Claim from which MinIO will read the policy or role to use",
placeholder: "Enter Claim Name",
type: "text",
hasError: (s: string, editMode: boolean) => "",
},
claim_prefix: {
required: false,
label: "Claim Prefix",
tooltip: "Claim Prefix",
placeholder: "Enter Claim Prefix",
type: "text",
hasError: (s: string, editMode: boolean) => "",
},
scopes: {
required: false,
label: "Scopes",
tooltip: "Scopes",
placeholder: "openid,profile,email",
type: "text",
hasError: (s: string, editMode: boolean) => "",
},
redirect_uri: {
required: false,
label: "Redirect URI",
tooltip: "Redirect URI",
placeholder: "https://console-endpoint-url/oauth_callback",
type: "text",
hasError: (s: string, editMode: boolean) => "",
},
role_policy: {
required: false,
label: "Role Policy",
tooltip: "Role Policy",
placeholder: "readonly",
type: "text",
hasError: (s: string, editMode: boolean) => "",
},
};
export const ldapFormFields = {
server_addr: {
required: true,
hasError: (s: string, editMode: boolean) => {
return !s && editMode ? "Server Address is required" : "";
},
label: "Server Address",
tooltip: 'AD/LDAP server address e.g. "myldapserver.com:636"',
placeholder: "myldapserver.com:636",
type: "text",
},
lookup_bind_dn: {
required: true,
hasError: (s: string, editMode: boolean) => {
return !s && editMode ? "Lookup Bind DN is required" : "";
},
label: "Lookup Bind DN",
tooltip:
"DN for LDAP read-only service account used to perform DN and group lookups",
placeholder: "cn=admin,dc=min,dc=io",
type: "text",
},
lookup_bind_password: {
required: true,
hasError: (s: string, editMode: boolean) => {
return !s && editMode ? "Lookup Bind Password is required" : "";
},
label: "Lookup Bind Password",
tooltip:
"Password for LDAP read-only service account used to perform DN and group lookups",
placeholder: "admin",
type: "password",
},
user_dn_search_base_dn: {
required: true,
hasError: (s: string, editMode: boolean) => {
return !s && editMode ? "User DN Search Base DN is required" : "";
},
label: "User DN Search Base",
tooltip: "Base LDAP DN to search for user DN",
placeholder: "DC=example,DC=net",
type: "text",
},
user_dn_search_filter: {
required: true,
hasError: (s: string, editMode: boolean) => {
return !s && editMode ? "User DN Search Filter is required" : "";
},
label: "User DN Search Filter",
tooltip: "Search filter to lookup user DN",
placeholder: "(sAMAcountName=%s)",
type: "text",
},
display_name: {
required: false,
label: "Display Name",
tooltip: "Display Name",
placeholder: "Enter Display Name",
type: "text",
hasError: (s: string, editMode: boolean) => "",
},
group_search_base_dn: {
required: false,
hasError: (s: string, editMode: boolean) => "",
label: "Group Search Base DN",
tooltip: "Group Search Base DN",
placeholder: "ou=swengg,dc=min,dc=io",
type: "text",
},
group_search_filter: {
required: false,
hasError: (s: string, editMode: boolean) => "",
label: "Group Search Filter",
tooltip: "Group Search Filter",
placeholder: "(&(objectclass=groupofnames)(member=%d))",
type: "text",
},
};

View File

@@ -233,7 +233,7 @@ export const objectBrowserSlice = createSlice({
setSearchObjects: (state, action: PayloadAction<string>) => {
state.searchObjects = action.payload;
},
setLoadingObjectsList: (state, action: PayloadAction<boolean>) => {
setLoadingObjects: (state, action: PayloadAction<boolean>) => {
state.loadingObjects = action.payload;
},
setSearchVersions: (state, action: PayloadAction<string>) => {
@@ -352,7 +352,7 @@ export const {
openList,
closeList,
setSearchObjects,
setLoadingObjectsList,
setLoadingObjects,
cancelObjectInList,
setSearchVersions,
setSelectedVersion,

View File

@@ -59,6 +59,7 @@ import {
import SettingsIcon from "../../icons/SettingsIcon";
import React from "react";
import LicenseBadge from "./Menu/LicenseBadge";
import { LockOpen, Login } from "@mui/icons-material";
export const validRoutes = (
features: string[] | null | undefined,
@@ -141,6 +142,20 @@ export const validRoutes = (
to: IAM_PAGES.POLICIES,
icon: AccessMenuIcon,
},
{
name: "OpenID",
component: NavLink,
id: "openID",
to: IAM_PAGES.IDP_OPENID_CONFIGURATIONS,
icon: LockOpen,
},
{
name: "LDAP",
component: NavLink,
id: "ldap",
to: IAM_PAGES.IDP_LDAP_CONFIGURATIONS,
icon: Login,
},
],
},

View File

@@ -19,9 +19,10 @@ import { useNavigate } from "react-router-dom";
import { useAppDispatch } from "../../store";
import { ErrorResponseHandler } from "../../common/types";
import { clearSession } from "../../common/utils";
import api from "../../common/api";
import { userLogged } from "../../systemSlice";
import { resetSession } from "../Console/consoleSlice";
import api from "../../common/api";
import LoadingComponent from "../../common/LoadingComponent";
const LogoutPage = () => {
const dispatch = useAppDispatch();
@@ -33,7 +34,7 @@ const LogoutPage = () => {
localStorage.setItem("userLoggedIn", "");
localStorage.setItem("redirect-path", "");
dispatch(resetSession());
navigate(`login`);
navigate(`/login`);
};
const state = localStorage.getItem("auth-state");
api
@@ -47,7 +48,7 @@ const LogoutPage = () => {
});
};
logout();
return <></>;
return <LoadingComponent />;
};
export default LogoutPage;

View File

@@ -7969,9 +7969,9 @@ mdn-data@2.0.4:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
"mds@https://github.com/minio/mds.git#v0.0.5":
version "0.0.5"
resolved "https://github.com/minio/mds.git#b9183841f178b7cc3ef320554f762c1d5bf6d7bd"
"mds@https://github.com/minio/mds.git#v0.0.7":
version "0.0.7"
resolved "https://github.com/minio/mds.git#dd51b9d694550e7737e6cf2462195b8ad70fbfe6"
dependencies:
"@types/styled-components" "^5.1.25"
styled-components "^5.3.6"

View File

@@ -20,7 +20,6 @@ import (
"context"
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"strconv"
@@ -604,9 +603,6 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
// start listing and writing to web socket
go func() {
defer func() {
log.Println("Closing listing goroutine:", messageRequest.RequestID)
}()
objectRqConfigs, err := getObjectsOptionsFromReq(messageRequest)
if err != nil {
LogInfo(fmt.Sprintf("Error during Objects OptionsParse %s", err.Error()))