Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
csentry
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Anders Harrisson
csentry
Commits
f3488f75
Commit
f3488f75
authored
7 years ago
by
Benjamin Bertrand
Browse files
Options
Downloads
Patches
Plain Diff
Add decorators to protect routes
parent
062a0395
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
app/api/main.py
+7
-0
7 additions, 0 deletions
app/api/main.py
app/decorators.py
+70
-0
70 additions, 0 deletions
app/decorators.py
app/models.py
+23
-3
23 additions, 3 deletions
app/models.py
app/settings.py
+5
-2
5 additions, 2 deletions
app/settings.py
with
105 additions
and
5 deletions
app/api/main.py
+
7
−
0
View file @
f3488f75
...
...
@@ -16,6 +16,7 @@ from flask_ldap3_login import AuthenticationResponseStatus
from
..extensions
import
ldap_manager
,
db
from
..models
import
Item
,
Manufacturer
,
Model
,
Location
,
Status
from
..
import
utils
from
..decorators
import
jwt_groups_accepted
bp
=
Blueprint
(
'
api
'
,
__name__
)
...
...
@@ -82,12 +83,14 @@ def get_items():
@bp.route
(
'
/items
'
,
methods
=
[
'
POST
'
])
@jwt_required
@jwt_groups_accepted
(
'
admin
'
,
'
create
'
)
def
create_item
():
return
create_generic_model
(
Item
,
mandatory_field
=
'
serial_number
'
)
@bp.route
(
'
/items/<item_id>
'
,
methods
=
[
'
PATCH
'
])
@jwt_required
@jwt_groups_accepted
(
'
admin
'
,
'
create
'
)
def
patch_item
(
item_id
):
data
=
request
.
get_json
()
if
data
is
None
:
...
...
@@ -112,6 +115,7 @@ def get_manufacturers():
@bp.route
(
'
/manufacturers
'
,
methods
=
[
'
POST
'
])
@jwt_required
@jwt_groups_accepted
(
'
admin
'
,
'
create
'
)
def
create_manufacturer
():
return
create_generic_model
(
Manufacturer
)
...
...
@@ -124,6 +128,7 @@ def get_models():
@bp.route
(
'
/models
'
,
methods
=
[
'
POST
'
])
@jwt_required
@jwt_groups_accepted
(
'
admin
'
,
'
create
'
)
def
create_model
():
return
create_generic_model
(
Model
)
...
...
@@ -136,6 +141,7 @@ def get_locations():
@bp.route
(
'
/locations
'
,
methods
=
[
'
POST
'
])
@jwt_required
@jwt_groups_accepted
(
'
admin
'
,
'
create
'
)
def
create_locations
():
return
create_generic_model
(
Location
)
...
...
@@ -148,5 +154,6 @@ def get_status():
@bp.route
(
'
/status
'
,
methods
=
[
'
POST
'
])
@jwt_required
@jwt_groups_accepted
(
'
admin
'
,
'
create
'
)
def
create_status
():
return
create_generic_model
(
Status
)
This diff is collapsed.
Click to expand it.
app/decorators.py
0 → 100644
+
70
−
0
View file @
f3488f75
# -*- coding: utf-8 -*-
"""
app.decorators
~~~~~~~~~~~~~~
This module defines some useful decorators.
:copyright: (c) 2017 European Spallation Source ERIC
:license: BSD 2-Clause, see LICENSE for more details.
"""
from
functools
import
wraps
from
flask_jwt_extended
import
get_current_user
from
.utils
import
InventoryError
def
jwt_groups_required
(
*
groups
):
"""
Decorator which specifies that a user must have all the specified groups.
Example::
@bp.route(
'
/models
'
, methods=[
'
POST
'
])
@jwt_required
@jwt_groups_required(
'
admin
'
,
'
create
'
)
def create_model():
return create()
The current user must be in both
'
admin
'
and
'
create
'
groups
to access this route.
:param groups: required groups
"""
def
wrapper
(
fn
):
@wraps
(
fn
)
def
decorated_view
(
*
args
,
**
kwargs
):
user
=
get_current_user
()
if
user
is
None
:
raise
InventoryError
(
'
Invalid indentity
'
,
status_code
=
403
)
if
not
user
.
is_member_of_all_groups
(
groups
):
raise
InventoryError
(
"
User doesn
'
t have the required group(s)
"
,
status_code
=
403
)
return
fn
(
*
args
,
**
kwargs
)
return
decorated_view
return
wrapper
def
jwt_groups_accepted
(
*
groups
):
"""
Decorator which specifies that a user must have at least one of the specified groups.
Example::
@bp.route(
'
/models
'
, methods=[
'
POST
'
])
@jwt_required
@jwt_groups_accepted(
'
admin
'
,
'
create
'
)
def create_model():
return create()
The current user must be in either
'
admin
'
or
'
create
'
group
to access this route.
:param groups: accepted groups
"""
def
wrapper
(
fn
):
@wraps
(
fn
)
def
decorated_view
(
*
args
,
**
kwargs
):
user
=
get_current_user
()
if
user
is
None
:
raise
InventoryError
(
'
Invalid indentity
'
,
status_code
=
403
)
if
not
user
.
is_member_of_one_group
(
groups
):
raise
InventoryError
(
"
User doesn
'
t have the required group
"
,
status_code
=
403
)
return
fn
(
*
args
,
**
kwargs
)
return
decorated_view
return
wrapper
This diff is collapsed.
Click to expand it.
app/models.py
+
23
−
3
View file @
f3488f75
...
...
@@ -17,7 +17,7 @@ from sqlalchemy.ext.associationproxy import association_proxy
from
citext
import
CIText
from
flask
import
current_app
from
flask_login
import
UserMixin
from
.extensions
import
db
,
login_manager
,
ldap_manager
from
.extensions
import
db
,
login_manager
,
ldap_manager
,
jwt
from
.
import
utils
...
...
@@ -61,11 +61,21 @@ def load_user(user_id):
"""
User loader callback for flask-login
:param str user_id: unicode ID of a user
:returns: corresponding user object
:returns: corresponding user object
or None
"""
return
User
.
query
.
get
(
int
(
user_id
))
@jwt.user_loader_callback_loader
def
user_loader_callback
(
identity
):
"""
User loader callback for flask-jwt-extended
:param str identity: identity from the token (user_id)
:returns: corresponding user object or None
"""
return
User
.
query
.
get
(
int
(
identity
))
@ldap_manager.save_user
def
save_user
(
dn
,
username
,
data
,
memberships
):
"""
User saver for flask-ldap3-login
...
...
@@ -140,7 +150,17 @@ class User(db.Model, UserMixin):
@property
def
is_admin
(
self
):
return
current_app
.
config
[
'
INVENTORY_ADMIN_GROUP
'
]
in
self
.
groups
return
current_app
.
config
[
'
INVENTORY_LDAP_GROUPS
'
][
'
admin
'
]
in
self
.
groups
def
is_member_of_one_group
(
self
,
groups
):
"""
Return True if the user is at least member of one of the given groups
"""
names
=
[
current_app
.
config
[
'
INVENTORY_LDAP_GROUPS
'
].
get
(
group
)
for
group
in
groups
]
return
bool
(
set
(
self
.
groups
)
&
set
(
names
))
def
is_member_of_all_groups
(
self
,
groups
):
"""
Return True if the user is member of all the given groups
"""
names
=
[
current_app
.
config
[
'
INVENTORY_LDAP_GROUPS
'
].
get
(
group
)
for
group
in
groups
]
return
set
(
names
).
issubset
(
self
.
groups
)
def
__str__
(
self
):
return
self
.
name
...
...
This diff is collapsed.
Click to expand it.
app/settings.py
+
5
−
2
View file @
f3488f75
...
...
@@ -34,7 +34,10 @@ LDAP_GROUP_OBJECT_FILTER = ''
LDAP_USER_SEARCH_SCOPE
=
'
SUBTREE
'
LDAP_GROUP_SEARCH_SCOPE
=
'
SUBTREE
'
LDAP_GROUP_MEMBERS_ATTR
=
'
member
'
LDAP_GET_USER_ATTRIBUTES
=
[
'
cn
'
,
'
sAMAccountName
'
,
'
mail
'
,
'
memberOf
'
]
LDAP_GET_USER_ATTRIBUTES
=
[
'
cn
'
,
'
sAMAccountName
'
,
'
mail
'
]
LDAP_GET_GROUP_ATTRIBUTES
=
[
'
cn
'
]
INVENTORY_ADMIN_GROUP
=
'
ICS Control System Infrastructure group
'
INVENTORY_LDAP_GROUPS
=
{
'
admin
'
:
'
ICS Control System Infrastructure group
'
,
'
create
'
:
'
ICS Employees
'
,
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment