Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
nomad-lab
nomad-FAIR
Commits
678e2f45
Commit
678e2f45
authored
Aug 27, 2019
by
Markus Scheidgen
Browse files
Allow API to verify GUI token. Adopted GUI to API changes.
parent
ded39fb5
Changes
10
Hide whitespace changes
Inline
Side-by-side
gui/src/components/App.js
View file @
678e2f45
...
...
@@ -161,9 +161,6 @@ class NavigationUnstyled extends React.Component {
barButton
:
{
borderColor
:
theme
.
palette
.
getContrastText
(
theme
.
palette
.
primary
.
main
),
marginRight
:
0
},
barButtonDisabled
:
{
marginRight
:
0
}
})
...
...
@@ -230,7 +227,7 @@ class NavigationUnstyled extends React.Component {
{
help
?
<
HelpDialog
color
=
"
inherit
"
maxWidth
=
"
md
"
classes
=
{{
root
:
classes
.
helpButton
}}
{...
help
}
/> : ''
}
<
/div
>
<
div
className
=
{
classes
.
barActions
}
>
<
LoginLogout
variant
=
"
outlined
"
color
=
"
inherit
"
classes
=
{{
button
:
classes
.
barButton
,
buttonDisabled
:
classes
.
barButtonDisabled
}}
/
>
<
LoginLogout
variant
=
"
outlined
"
color
=
"
inherit
"
classes
=
{{
button
:
classes
.
barButton
}}
/
>
<
/div
>
<
/Toolbar
>
{
loading
?
<
LinearProgress
color
=
"
primary
"
/>
:
''
}
...
...
gui/src/components/LoginLogout.js
View file @
678e2f45
...
...
@@ -3,23 +3,15 @@ import PropTypes from 'prop-types'
import
{
withStyles
}
from
'
@material-ui/core/styles
'
import
Typography
from
'
@material-ui/core/Typography
'
import
{
compose
}
from
'
recompose
'
import
{
Button
,
DialogTitle
,
DialogContent
,
DialogContentText
,
TextField
,
DialogActions
,
Dialog
,
FormGroup
}
from
'
@material-ui/core
'
import
{
Button
}
from
'
@material-ui/core
'
import
{
withApi
}
from
'
./api
'
import
{
withKeycloak
}
from
'
react-keycloak
'
class
LoginLogout
extends
React
.
Component
{
static
propTypes
=
{
classes
:
PropTypes
.
object
.
isRequired
,
api
:
PropTypes
.
object
.
isRequired
,
isLoggingIn
:
PropTypes
.
bool
,
user
:
PropTypes
.
object
,
login
:
PropTypes
.
func
.
isRequired
,
logout
:
PropTypes
.
func
.
isRequired
,
variant
:
PropTypes
.
string
,
color
:
PropTypes
.
string
,
onLoggedIn
:
PropTypes
.
func
,
onLoggedOut
:
PropTypes
.
func
,
user
:
PropTypes
.
object
,
keycloak
:
PropTypes
.
object
.
isRequired
}
...
...
@@ -31,92 +23,17 @@ class LoginLogout extends React.Component {
marginRight
:
theme
.
spacing
.
unit
*
2
}
},
button
:
{},
// to allow overrides
buttonDisabled
:
{},
errorText
:
{
marginTop
:
theme
.
spacing
.
unit
,
marginBottom
:
theme
.
spacing
.
unit
}
button
:
{}
// to allow overrides
})
constructor
(
props
)
{
super
(
props
)
this
.
handleLogout
=
this
.
handleLogout
.
bind
(
this
)
this
.
handleChange
=
this
.
handleChange
.
bind
(
this
)
this
.
handleKeyPress
=
this
.
handleKeyPress
.
bind
(
this
)
}
state
=
{
loginDialogOpen
:
false
,
userName
:
''
,
password
:
''
,
failure
:
false
}
componentDidMount
()
{
this
.
_ismounted
=
true
}
componentWillUnmount
()
{
this
.
_ismounted
=
false
}
handleLoginDialogClosed
(
withLogin
)
{
if
(
withLogin
)
{
this
.
props
.
login
(
this
.
state
.
userName
,
this
.
state
.
password
,
(
success
)
=>
{
if
(
success
&&
this
.
props
.
onLoggedIn
)
{
this
.
props
.
onLoggedIn
()
}
if
(
this
.
_ismounted
)
{
if
(
success
)
{
this
.
setState
({
loginDialogOpen
:
false
,
failure
:
false
})
}
else
{
this
.
setState
({
failure
:
true
,
loginDialogOpen
:
true
})
}
}
})
}
else
{
if
(
this
.
_ismounted
)
{
this
.
setState
({
failure
:
false
,
userName
:
''
,
password
:
''
,
loginDialogOpen
:
false
})
}
}
}
handleChange
=
name
=>
event
=>
{
this
.
setState
({
[
name
]:
event
.
target
.
value
,
failure
:
false
})
}
handleLogout
()
{
this
.
props
.
logout
()
if
(
this
.
props
.
onLoggedOut
)
{
this
.
props
.
onLoggedOut
()
}
}
handleKeyPress
(
ev
)
{
if
(
ev
.
key
===
'
Enter
'
)
{
ev
.
preventDefault
()
this
.
handleLoginDialogClosed
(
true
)
}
}
render
()
{
const
{
classes
,
variant
,
color
,
isLoggingIn
,
keycloak
}
=
this
.
props
const
{
classes
,
variant
,
color
,
keycloak
,
user
}
=
this
.
props
let
user
=
null
if
(
keycloak
.
authenticated
)
{
user
=
{}
}
const
{
failure
}
=
this
.
state
if
(
user
)
{
return
(
<
div
className
=
{
classes
.
root
}
>
<
Typography
color
=
"
inherit
"
variant
=
"
body1
"
>
Welcome
{
user
.
first_name
}
{
user
.
last_name
}
Welcome
{
user
?
user
.
name
:
'
...
'
}
<
/Typography
>
<
Button
className
=
{
classes
.
button
}
...
...
@@ -131,64 +48,10 @@ class LoginLogout extends React.Component {
<
Button
className
=
{
classes
.
button
}
variant
=
{
variant
}
color
=
{
color
}
onClick
=
{()
=>
keycloak
.
login
()}
>
Login
<
/Button
>
<
Dialog
disableBackdropClick
disableEscapeKeyDown
open
=
{
this
.
state
.
loginDialogOpen
}
onClose
=
{()
=>
this
.
handleLoginDialogClosed
(
false
)}
>
<
DialogTitle
>
Login
<
/DialogTitle
>
<
DialogContent
>
<
DialogContentText
>
To
login
,
please
enter
your
email
address
and
password
.
If
you
do
not
have
an
account
,
please
go
to
the
NOMAD
Repository
and
create
one
.
<
/DialogContentText
>
{
failure
?
<
DialogContentText
className
=
{
classes
.
errorText
}
color
=
"
error
"
>
Wrong
username
or
password
!<
/DialogContentText> : ''
}
<
form
>
<
FormGroup
>
<
TextField
autoComplete
=
"
username
"
disabled
=
{
isLoggingIn
}
autoFocus
margin
=
"
dense
"
id
=
"
uaseName
"
label
=
"
Email Address
"
type
=
"
email
"
fullWidth
value
=
{
this
.
state
.
userName
}
onChange
=
{
this
.
handleChange
(
'
userName
'
)}
onKeyPress
=
{
this
.
handleKeyPress
}
/
>
<
TextField
autoComplete
=
"
current-password
"
disabled
=
{
isLoggingIn
}
margin
=
"
dense
"
id
=
"
password
"
label
=
"
Password
"
type
=
"
password
"
fullWidth
value
=
{
this
.
state
.
password
}
onChange
=
{
this
.
handleChange
(
'
password
'
)}
onKeyPress
=
{
this
.
handleKeyPress
}
/
>
<
/FormGroup
>
<
/form
>
<
/DialogContent
>
<
DialogActions
>
<
Button
onClick
=
{()
=>
this
.
handleLoginDialogClosed
(
false
)}
color
=
"
primary
"
>
Cancel
<
/Button
>
<
Button
onClick
=
{()
=>
this
.
handleLoginDialogClosed
(
true
)}
color
=
"
primary
"
disabled
=
{
this
.
state
.
userName
===
''
||
this
.
state
.
password
===
''
}
>
Login
<
/Button
>
<
/DialogActions
>
<
/Dialog
>
<
/div
>
)
}
}
}
export
default
compose
(
withKeycloak
,
withApi
(
false
),
withStyles
(
LoginLogout
.
styles
))(
LoginLogout
)
export
default
compose
(
withApi
(
false
),
withStyles
(
LoginLogout
.
styles
))(
LoginLogout
)
gui/src/components/api.js
View file @
678e2f45
import
React
from
'
react
'
import
PropTypes
,
{
instanceOf
}
from
'
prop-types
'
import
PropTypes
from
'
prop-types
'
import
{
withErrors
}
from
'
./errors
'
import
{
UploadRequest
}
from
'
@navjobs/upload
'
import
Swagger
from
'
swagger-client
'
import
{
apiBase
}
from
'
../config
'
import
{
Typography
,
withStyles
,
Link
}
from
'
@material-ui/core
'
import
LoginLogout
from
'
./LoginLogout
'
import
{
Cookies
,
withCookies
}
from
'
react-cookie
'
import
{
compose
}
from
'
recompose
'
import
MetaInfoRepository
from
'
./MetaInfoRepository
'
import
{
withKeycloak
}
from
'
react-keycloak
'
const
ApiContext
=
React
.
createContext
()
...
...
@@ -66,7 +66,7 @@ class Upload {
method
:
'
PUT
'
,
headers
:
{
'
Content-Type
'
:
'
application/gzip
'
,
...
this
.
api
.
auth
_h
eaders
...
this
.
api
.
auth
H
eaders
}
},
files
:
[
file
],
...
...
@@ -143,20 +143,13 @@ function handleApiError(e) {
}
class
Api
{
static
async
createSwaggerClient
(
userNameToken
,
password
)
{
static
async
createSwaggerClient
(
accessToken
)
{
let
data
if
(
userName
Token
)
{
if
(
access
Token
)
{
let
auth
=
{
'
X-Token
'
:
userNameToken
}
if
(
password
)
{
auth
=
{
'
HTTP Basic
'
:
{
username
:
userNameToken
,
password
:
password
}
}
'
OpenIDConnect Bearer Token
'
:
`Bearer
${
accessToken
}
`
}
data
=
{
authorizations
:
auth
}
}
...
...
@@ -167,15 +160,14 @@ class Api {
}
}
constructor
(
user
)
{
constructor
(
accessToken
)
{
this
.
onStartLoading
=
()
=>
null
this
.
onFinishLoading
=
()
=>
null
user
=
user
||
{}
this
.
auth_headers
=
{
'
X-Token
'
:
user
.
token
this
.
authHeaders
=
{
'
Authentication
'
:
`Bearer
${
accessToken
}
`
}
this
.
swaggerPromise
=
Api
.
createSwaggerClient
(
user
.
t
oken
).
catch
(
handleApiError
)
this
.
swaggerPromise
=
Api
.
createSwaggerClient
(
accessT
oken
).
catch
(
handleApiError
)
// keep a list of localUploads, these are uploads that are currently uploaded through
// the browser and that therefore not yet returned by the backend
...
...
@@ -395,21 +387,37 @@ export class ApiProviderComponent extends React.Component {
PropTypes
.
arrayOf
(
PropTypes
.
node
),
PropTypes
.
node
]).
isRequired
,
cookies
:
instanceOf
(
Cookies
).
isRequired
,
raiseError
:
PropTypes
.
func
.
isRequired
raiseError
:
PropTypes
.
func
.
isRequired
,
keycloak
:
PropTypes
.
object
.
isRequired
,
keycloakInitialized
:
PropTypes
.
bool
}
componentDidMount
(
props
)
{
const
token
=
this
.
props
.
cookies
.
get
(
'
token
'
)
if
(
token
&&
token
!==
'
undefined
'
)
{
this
.
state
.
login
(
token
)
}
else
{
this
.
setState
({
api
:
this
.
createApi
()})
update
()
{
const
{
keycloak
}
=
this
.
props
this
.
setState
({
api
:
this
.
createApi
(
keycloak
.
token
)})
if
(
keycloak
.
token
)
{
keycloak
.
loadUserInfo
()
.
success
(
user
=>
{
this
.
setState
({
user
:
user
})
})
.
error
(
error
=>
{
this
.
props
.
raiseError
(
error
)
})
}
}
componentDidMount
()
{
this
.
update
()
}
componentDidUpdate
(
prevProps
)
{
if
(
this
.
props
.
keycloakInitialized
!==
prevProps
.
keycloakInitialized
)
{
this
.
update
()
}
}
createApi
(
user
)
{
const
api
=
new
Api
(
user
)
createApi
(
accessToken
)
{
const
api
=
new
Api
(
accessToken
)
api
.
onStartLoading
=
(
name
)
=>
{
this
.
setState
(
state
=>
({
loading
:
state
.
loading
+
1
}))
}
...
...
@@ -428,50 +436,8 @@ export class ApiProviderComponent extends React.Component {
state
=
{
api
:
null
,
user
:
null
,
info
:
null
,
isLoggingIn
:
false
,
loading
:
0
,
login
:
(
userNameToken
,
password
,
successCallback
)
=>
{
this
.
setState
({
isLoggingIn
:
true
})
successCallback
=
successCallback
||
(()
=>
true
)
Api
.
createSwaggerClient
(
userNameToken
,
password
)
.
then
(
client
=>
{
client
.
apis
.
auth
.
get_user
()
.
catch
(
error
=>
{
if
(
error
.
response
&&
error
.
response
.
status
!==
401
)
{
throw
error
}
})
.
then
(
response
=>
{
if
(
response
)
{
const
user
=
response
.
body
this
.
setState
({
api
:
this
.
createApi
(
user
),
isLoggingIn
:
false
,
user
:
user
})
this
.
props
.
cookies
.
set
(
'
token
'
,
user
.
token
)
successCallback
(
true
)
}
else
{
this
.
setState
({
api
:
this
.
createApi
(),
isLoggingIn
:
false
,
user
:
null
})
successCallback
(
false
)
}
})
.
catch
(
error
=>
{
try
{
this
.
props
.
raiseError
(
error
)
}
catch
(
e
)
{
this
.
setState
({
api
:
this
.
createApi
(),
isLoggingIn
:
false
,
user
:
null
})
this
.
props
.
raiseError
(
error
)
}
})
})
.
catch
(
error
=>
{
this
.
setState
({
api
:
this
.
createApi
(),
isLoggingIn
:
false
,
user
:
null
})
this
.
props
.
raiseError
(
error
)
})
},
logout
:
()
=>
{
this
.
setState
({
api
:
this
.
createApi
(),
user
:
null
})
this
.
props
.
cookies
.
set
(
'
token
'
,
undefined
)
}
loading
:
0
}
render
()
{
...
...
@@ -487,9 +453,7 @@ export class ApiProviderComponent extends React.Component {
class
LoginRequiredUnstyled
extends
React
.
Component
{
static
propTypes
=
{
classes
:
PropTypes
.
object
.
isRequired
,
message
:
PropTypes
.
string
,
isLoggingIn
:
PropTypes
.
bool
,
onLoggedIn
:
PropTypes
.
func
message
:
PropTypes
.
string
}
static
styles
=
theme
=>
({
...
...
@@ -504,7 +468,7 @@ class LoginRequiredUnstyled extends React.Component {
})
render
()
{
const
{
classes
,
isLoggingIn
,
onLoggedIn
,
message
}
=
this
.
props
const
{
classes
,
message
}
=
this
.
props
let
loginMessage
=
''
if
(
message
)
{
...
...
@@ -516,7 +480,7 @@ class LoginRequiredUnstyled extends React.Component {
return
(
<
div
className
=
{
classes
.
root
}
>
{
loginMessage
}
<
LoginLogout
onLoggedIn
=
{
onLoggedIn
}
variant
=
"
outlined
"
color
=
"
primary
"
isLoggingIn
=
{
isLoggingIn
}
/
>
<
LoginLogout
variant
=
"
outlined
"
color
=
"
primary
"
/>
<
/div
>
)
}
...
...
@@ -537,7 +501,7 @@ DisableOnLoading.propTypes = {
children
:
PropTypes
.
any
.
isRequired
}
export
const
ApiProvider
=
compose
(
with
Cookies
,
withErrors
)(
ApiProviderComponent
)
export
const
ApiProvider
=
compose
(
with
Keycloak
,
withErrors
)(
ApiProviderComponent
)
const
LoginRequired
=
withStyles
(
LoginRequiredUnstyled
.
styles
)(
LoginRequiredUnstyled
)
...
...
@@ -549,7 +513,6 @@ class WithApiComponent extends React.Component {
loginMessage
:
PropTypes
.
string
,
api
:
PropTypes
.
object
,
user
:
PropTypes
.
object
,
isLoggingIn
:
PropTypes
.
bool
,
Component
:
PropTypes
.
any
}
...
...
@@ -586,10 +549,10 @@ class WithApiComponent extends React.Component {
render
()
{
const
{
raiseError
,
loginRequired
,
loginMessage
,
Component
,
...
rest
}
=
this
.
props
const
{
api
,
user
,
isLoggingIn
}
=
rest
const
{
api
,
keycloak
}
=
rest
const
{
notAuthorized
}
=
this
.
state
if
(
notAuthorized
)
{
if
(
user
)
{
if
(
keycloak
.
authenticated
)
{
return
(
<
div
>
<
Typography
variant
=
"
h6
"
>
Not
Authorized
<
/Typography
>
...
...
@@ -602,19 +565,15 @@ class WithApiComponent extends React.Component {
)
}
else
{
return
(
<
LoginRequired
message
=
"
You need to be logged in to access this information.
"
isLoggingIn
=
{
isLoggingIn
}
onLoggedIn
=
{()
=>
this
.
setState
({
notAuthorized
:
false
})}
/
>
<
LoginRequired
message
=
"
You need to be logged in to access this information.
"
/>
)
}
}
else
{
if
(
api
)
{
if
(
user
||
!
loginRequired
)
{
if
(
keycloak
.
authenticated
||
!
loginRequired
)
{
return
<
Component
{...
rest
}
raiseError
=
{
this
.
raiseError
}
/
>
}
else
{
return
<
LoginRequired
message
=
{
loginMessage
}
isLoggingIn
=
{
isLoggingIn
}
/
>
return
<
LoginRequired
message
=
{
loginMessage
}
/
>
}
}
else
{
return
''
...
...
@@ -623,12 +582,14 @@ class WithApiComponent extends React.Component {
}
}
const
WithKeycloakWithApiCompnent
=
withKeycloak
(
WithApiComponent
)
export
function
withApi
(
loginRequired
,
showErrorPage
,
loginMessage
)
{
return
function
(
Component
)
{
return
withErrors
(
props
=>
(
<
ApiContext
.
Consumer
>
{
apiContext
=>
(
<
WithApiComp
o
nent
<
WithKeycloak
WithApiCompnent
loginRequired
=
{
loginRequired
}
loginMessage
=
{
loginMessage
}
showErrorPage
=
{
showErrorPage
}
...
...
gui/src/index.js
View file @
678e2f45
...
...
@@ -24,7 +24,7 @@ const keycloak = Keycloak({
})
ReactDOM
.
render
(
<
KeycloakProvider
keycloak
=
{
keycloak
}
initConfig
=
{{
onLoad
:
'
check-sso
'
}}
>
<
KeycloakProvider
keycloak
=
{
keycloak
}
initConfig
=
{{
onLoad
:
'
check-sso
'
}}
LoadingComponent
=
{
<
div
/>
}
>
<
Router
history
=
{
sendTrackingData
?
matomo
.
connectToHistory
(
history
)
:
history
}
>
<
App
/>
<
/Router
>
...
...
nomad/api/app.py
View file @
678e2f45
...
...
@@ -27,7 +27,7 @@ from datetime import datetime
import
pytz
import
random
from
nomad
import
config
,
utils
,
infrastructure
from
nomad
import
config
,
utils
base_path
=
config
.
services
.
api_base_path
""" Provides the root path of the nomad APIs. """
...
...
@@ -70,8 +70,6 @@ def api_base_path_response(env, resp):
app
.
wsgi_app
=
DispatcherMiddleware
(
# type: ignore
api_base_path_response
,
{
config
.
services
.
api_base_path
:
app
.
wsgi_app
})
infrastructure
.
keycloak
.
configure_flask
(
app
)
CORS
(
app
)
api
=
Api
(
...
...
nomad/api/auth.py
View file @
678e2f45
...
...
@@ -229,12 +229,16 @@ class AuthResource(Resource):
dict
(
user
=
g
.
user
.
user_id
,
exp
=
expires_at
),
config
.
services
.
api_secret
,
'HS256'
).
decode
(
'utf-8'
)
return
{
'user'
:
g
.
user
,
'upload_token'
:
generate_upload_token
(
g
.
user
),
'signature_token'
:
signature_token
(),
'access_token'
:
infrastructure
.
keycloak
.
access_token
}
try
:
return
{
'user'
:
infrastructure
.
keycloak
.
get_user
(
g
.
user
.
user_id
),
'upload_token'
:
generate_upload_token
(
g
.
user
),
'signature_token'
:
signature_token
(),
'access_token'
:
infrastructure
.
keycloak
.
access_token
}
except
KeyError
:
abort
(
401
,
'The authenticated user does not exist'
)
def
with_signature_token
(
func
):
...
...
nomad/config.py
View file @
678e2f45
...
...
@@ -113,11 +113,12 @@ elastic = NomadConfig(
keycloak
=
NomadConfig
(
server_url
=
'http://localhost:8002/auth/'
,
issuer_url
=
'http://localhost:8002/auth/realms/fairdi_nomad_test'
,
realm_name
=
'fairdi_nomad_test'
,
username
=
'admin'
,
password
=
'password'
,
client_id
=
'nomad_api_dev'
,
client_secret_key
=
'
0f9ec82f-a1dc-4405-a80e-593160aeea4
2'
client_secret_key
=
'
ae9bb323-3793-4243-9e4b-f380c54e54e
2'
)
mongo
=
NomadConfig
(
...
...
nomad/infrastructure.py
View file @
678e2f45
...
...
@@ -19,7 +19,6 @@ is run once for each *api* and *worker* process. Individual functions for partia
exist to facilitate testing, :py:mod:`nomad.migration`, aspects of :py:mod:`nomad.cli`, etc.
"""
from
typing
import
Union
import
os.path
import
shutil
from
elasticsearch.exceptions
import
RequestError
...
...
@@ -28,10 +27,11 @@ from mongoengine import connect
import
smtplib
from
email.mime.text
import
MIMEText
from
keycloak
import
KeycloakOpenID
,
KeycloakAdmin
from
flask_oidc
import
OpenIDConnect
import
json
import
jwt
from
flask
import
g
,
request
import
basicauth
from
datetime
import
datetime
from
nomad
import
config
,
utils
...
...
@@ -108,32 +108,9 @@ class Keycloak():
configuration
"""
def
__init__
(
self
):
self
.
_flask_oidc
=
None
self
.
__oidc_client
=
None
self
.
__admin_client
=
None
def
configure_flask
(
self
,
app
):
oidc_issuer_url
=
'%s/realms/%s'
%
(
config
.
keycloak
.
server_url
.
rstrip
(
'/'
),
config
.
keycloak
.
realm_name
)
oidc_client_secrets
=
dict
(
client_id
=
config
.
keycloak
.
client_id
,
client_secret
=
config
.
keycloak
.
client_secret_key
,
issuer
=
oidc_issuer_url
,
auth_uri
=
'%s/protocol/openid-connect/auth'
%
oidc_issuer_url
,
token_uri
=
'%s/protocol/openid-connect/token'
%
oidc_issuer_url
,
userinfo_uri
=
'%s/protocol/openid-connect/userinfo'
%
oidc_issuer_url
,
token_introspection_uri
=
'%s/protocol/openid-connect/token/introspect'
%
oidc_issuer_url
,
redirect_uris
=
[
'http://localhost/fairdi/nomad/latest'
])
oidc_client_secrets_file
=
os
.
path
.
join
(
config
.
fs
.
tmp
,
'oidc_client_secrets'
)
with
open
(
oidc_client_secrets_file
,
'wt'
)
as
f
:
json
.
dump
(
dict
(
web
=
oidc_client_secrets
),
f
)
app
.
config
.
update
(
dict
(
SECRET_KEY
=
config
.
services
.
api_secret
,
OIDC_RESOURCE_SERVER_ONLY
=
True
,
OIDC_USER_INFO_ENABLED
=
False
,
OIDC_CLIENT_SECRETS
=
oidc_client_secrets_file
,
OIDC_OPENID_REALM
=
config
.
keycloak
.
realm_name
))
self
.
_flask_oidc
=
OpenIDConnect
(
app
)
self
.
__public_keys
=
None
@
property
def
_oidc_client
(
self
):
...
...
@@ -146,10 +123,36 @@ class Keycloak():
return
self
.
__oidc_client
def
authorize_flask
(
self
,
basic
:
bool
=
True
)
->
Union
[
str
,
object
]:
token
=
None
@
property
def
_public_keys
(
self
):
if
self
.
__public_keys
is
None
:
try
:
jwks
=
self
.
_oidc_client
.
certs
()
self
.
__public_keys
=
{}
for
jwk
in
jwks
[
'keys'
]:
kid
=
jwk
[
'kid'
]
self
.
__public_keys
[
kid
]
=
jwt
.
algorithms
.
RSAAlgorithm
.
from_jwk
(
json
.
dumps
(
jwk
))