Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Kontr 2.0
Portal API Backend
Commits
9a32bcd4
Verified
Commit
9a32bcd4
authored
Apr 08, 2019
by
Peter Stanko
Browse files
Add gitlab_url as cookie
parent
a866d4e9
Changes
9
Hide whitespace changes
Inline
Side-by-side
portal/database/models.py
View file @
9a32bcd4
...
@@ -16,7 +16,7 @@ from werkzeug.security import check_password_hash, generate_password_hash
...
@@ -16,7 +16,7 @@ from werkzeug.security import check_password_hash, generate_password_hash
from
portal
import
db
from
portal
import
db
from
portal.database.exceptions
import
PortalDbError
from
portal.database.exceptions
import
PortalDbError
from
portal.database.mixins
import
EntityBase
,
NamedMixin
from
portal.database.mixins
import
EntityBase
,
NamedMixin
from
portal.database.types
import
JSONEncodedDict
from
portal.database.types
import
JSONEncodedDict
,
YAMLEncodedDict
from
portal.tools
import
time
from
portal.tools
import
time
from
portal.tools.time
import
normalize_time
from
portal.tools.time
import
normalize_time
...
@@ -580,10 +580,10 @@ class ProjectConfig(db.Model, EntityBase):
...
@@ -580,10 +580,10 @@ class ProjectConfig(db.Model, EntityBase):
test_files_subdir
=
db
.
Column
(
db
.
String
(
50
))
# git subdir
test_files_subdir
=
db
.
Column
(
db
.
String
(
50
))
# git subdir
test_files_commit_hash
=
db
.
Column
(
db
.
String
(
128
))
# hash of the latest revision of test_files
test_files_commit_hash
=
db
.
Column
(
db
.
String
(
128
))
# hash of the latest revision of test_files
file_whitelist
=
db
.
Column
(
db
.
Text
)
file_whitelist
=
db
.
Column
(
db
.
Text
)
pre_submit_script
=
db
.
Column
(
db
.
Tex
t
)
pre_submit_script
=
db
.
Column
(
YAMLEncodedDic
t
)
post_submit_script
=
db
.
Column
(
db
.
Tex
t
)
post_submit_script
=
db
.
Column
(
YAMLEncodedDic
t
)
submission_parameters
=
db
.
Column
(
db
.
Tex
t
)
submission_parameters
=
db
.
Column
(
YAMLEncodedDic
t
)
submission_scheduler_config
=
db
.
Column
(
db
.
Tex
t
)
submission_scheduler_config
=
db
.
Column
(
YAMLEncodedDic
t
)
_submissions_allowed_from
=
db
.
Column
(
db
.
TIMESTAMP
(
timezone
=
True
))
_submissions_allowed_from
=
db
.
Column
(
db
.
TIMESTAMP
(
timezone
=
True
))
_submissions_allowed_to
=
db
.
Column
(
db
.
TIMESTAMP
(
timezone
=
True
))
_submissions_allowed_to
=
db
.
Column
(
db
.
TIMESTAMP
(
timezone
=
True
))
...
...
portal/database/types.py
View file @
9a32bcd4
import
json
import
json
import
sqlalchemy
import
sqlalchemy
import
yaml
from
sqlalchemy
import
TypeDecorator
from
sqlalchemy
import
TypeDecorator
...
@@ -17,4 +18,20 @@ class JSONEncodedDict(TypeDecorator):
...
@@ -17,4 +18,20 @@ class JSONEncodedDict(TypeDecorator):
def
process_result_value
(
self
,
value
,
dialect
):
def
process_result_value
(
self
,
value
,
dialect
):
if
value
is
not
None
:
if
value
is
not
None
:
value
=
json
.
loads
(
value
)
value
=
json
.
loads
(
value
)
return
value
return
value
\ No newline at end of file
class
YAMLEncodedDict
(
TypeDecorator
):
"Represents an immutable structure as a yaml-encoded string."
impl
=
sqlalchemy
.
Text
def
process_bind_param
(
self
,
value
,
dialect
):
if
value
is
not
None
:
value
=
yaml
.
safe_dump
(
value
)
return
value
def
process_result_value
(
self
,
value
,
dialect
):
if
value
is
not
None
:
value
=
yaml
.
safe_load
(
value
)
return
value
portal/facade/gitlab_facade.py
View file @
9a32bcd4
...
@@ -106,6 +106,7 @@ class GitlabFacade(GeneralFacade):
...
@@ -106,6 +106,7 @@ class GitlabFacade(GeneralFacade):
'gitlab_token_type'
:
token_type
,
'gitlab_token_type'
:
token_type
,
'gitlab_username'
:
user_info
.
username
,
'gitlab_username'
:
user_info
.
username
,
'username'
:
user_info
.
username
,
'username'
:
user_info
.
username
,
'gitlab_url'
:
self
.
_config
.
get
(
'GITLAB_URL'
)
}
}
return
flask
.
jsonify
(
params
)
if
not
redir
else
self
.
_make_login_response
(
params
)
return
flask
.
jsonify
(
params
)
if
not
redir
else
self
.
_make_login_response
(
params
)
...
...
portal/facade/projects_facade.py
View file @
9a32bcd4
...
@@ -9,6 +9,25 @@ from portal.service.projects import ProjectService
...
@@ -9,6 +9,25 @@ from portal.service.projects import ProjectService
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
def
_check_gl_repo_size
(
gl_project
,
project
):
max_repo_size
=
project
.
config
.
submission_scheduler_config
.
get
(
'repo_max_size'
)
if
max_repo_size
:
repo_size
=
gl_project
.
statistics
[
'repository_size'
]
if
repo_size
>
max_repo_size
:
raise
errors
.
SubmissionRefusedError
(
f
"Your repository is bigger than the limit:"
f
"
{
repo_size
}
>
{
max_repo_size
}
"
)
def
_check_gl_repo_visibility
(
gl_project
,
project
):
required_repo_visibility
=
project
.
config
.
submission_scheduler_config
.
get
(
'repo_visibility'
)
if
required_repo_visibility
:
repo_visibility
=
gl_project
.
visibility
if
required_repo_visibility
!=
repo_visibility
:
raise
errors
.
SubmissionRefusedError
(
f
"Your repository visibility "
f
"is not
{
required_repo_visibility
}
."
f
" Actual:
{
repo_visibility
}
"
)
class
ProjectsFacade
(
GeneralCRUDFacade
):
class
ProjectsFacade
(
GeneralCRUDFacade
):
def
__init__
(
self
):
def
__init__
(
self
):
super
().
__init__
(
ProjectService
,
'Project'
)
super
().
__init__
(
ProjectService
,
'Project'
)
...
@@ -67,6 +86,8 @@ class ProjectsFacade(GeneralCRUDFacade):
...
@@ -67,6 +86,8 @@ class ProjectsFacade(GeneralCRUDFacade):
"""
"""
client
=
client
or
self
.
client
client
=
client
or
self
.
client
result
=
self
.
_service
(
project
).
check_submission_create
(
client
)
result
=
self
.
_service
(
project
).
check_submission_create
(
client
)
if
self
.
_services
.
kontr_gitlab
.
is_authorized
and
not
project
.
submit_configurable
:
result
=
self
.
_check_gitlab_params
(
client
,
project
)
return
result
return
result
def
get_test_files
(
self
,
project
:
Project
):
def
get_test_files
(
self
,
project
:
Project
):
...
@@ -100,3 +121,11 @@ class ProjectsFacade(GeneralCRUDFacade):
...
@@ -100,3 +121,11 @@ class ProjectsFacade(GeneralCRUDFacade):
f
"
{
self
.
client_name
}
"
)
f
"
{
self
.
client_name
}
"
)
result
=
self
.
_services
.
is_api
(
project
.
course
).
create_notepad
(
project
=
project
)
result
=
self
.
_services
.
is_api
(
project
.
course
).
create_notepad
(
project
=
project
)
return
result
return
result
def
_check_gitlab_params
(
self
,
client
:
Client
,
project
:
Project
):
gl_repo
=
self
.
services
.
kontr_gitlab
.
build_repo_name
(
client
,
project
)
gl_project
=
self
.
_services
.
kontr_gitlab
.
get_project
(
gl_repo
)
_check_gl_repo_size
(
gl_project
,
project
)
_check_gl_repo_visibility
(
gl_project
,
project
)
return
True
portal/rest/projects.py
View file @
9a32bcd4
import
flask
from
flask
import
request
from
flask
import
request
from
flask_jwt_extended
import
jwt_required
from
flask_jwt_extended
import
jwt_required
from
flask_restplus
import
Namespace
from
flask_restplus
import
Namespace
...
@@ -209,6 +210,37 @@ class ProjectSubmissions(CustomResource):
...
@@ -209,6 +210,37 @@ class ProjectSubmissions(CustomResource):
self
.
facades
.
projects
.
check_submission_create
(
project
,
client
=
user
)
self
.
facades
.
projects
.
check_submission_create
(
project
,
client
=
user
)
@
projects_namespace
.
route
(
'/courses/<string:cid>/projects/<string:pid>/submissions/check'
)
@
projects_namespace
.
param
(
'cid'
,
'Course id'
)
@
projects_namespace
.
param
(
'pid'
,
'Project id'
)
@
projects_namespace
.
response
(
404
,
'Course not found'
)
@
projects_namespace
.
response
(
404
,
'Project not found'
)
class
ProjectSubmissionsCheck
(
CustomResource
):
@
jwt_required
@
access_log
# @projects_namespace.response(201, 'Project submission create', model=submission_schema)
def
get
(
self
,
cid
:
str
,
pid
:
str
):
course
=
self
.
find
.
course
(
cid
)
user
=
request
.
args
.
get
(
'user'
)
user
=
self
.
find
.
user
(
user
)
if
user
is
not
None
else
self
.
client
# authorization
self
.
permissions
(
course
=
course
).
require
.
create_submission
(
user
)
# check if a new submission can be created by this user in this project
project
=
self
.
find
.
project
(
course
,
pid
)
self
.
_check_user_can_create_submission
(
project
,
user
)
log
.
info
(
f
"[REST] Check Create submission in
{
project
.
log_name
}
"
f
"by
{
self
.
client
.
log_name
}
for user
{
user
.
log_name
}
"
)
return
flask
.
jsonify
({
'message'
:
'User is able to create submission'
}),
200
def
_check_user_can_create_submission
(
self
,
project
,
user
):
if
self
.
client
.
type
==
ClientType
.
USER
:
client_instance
=
self
.
find
.
user
(
self
.
client
.
id
)
if
not
client_instance
.
is_admin
:
self
.
facades
.
projects
.
check_submission_create
(
project
,
client
=
user
)
@
projects_namespace
.
route
(
'/courses/<string:cid>/projects/<string:pid>/files'
)
@
projects_namespace
.
route
(
'/courses/<string:cid>/projects/<string:pid>/files'
)
@
projects_namespace
.
param
(
'cid'
,
'Course id'
)
@
projects_namespace
.
param
(
'cid'
,
'Course id'
)
@
projects_namespace
.
param
(
'pid'
,
'Project id'
)
@
projects_namespace
.
param
(
'pid'
,
'Project id'
)
...
...
portal/service/errors.py
View file @
9a32bcd4
...
@@ -2,11 +2,14 @@
...
@@ -2,11 +2,14 @@
Errors in the service layer
Errors in the service layer
"""
"""
import
json
import
json
import
logging
from
typing
import
Union
from
typing
import
Union
from
portal.database
import
Course
,
Project
,
User
from
portal.database
import
Course
,
Project
,
User
from
portal.tools
import
time
from
portal.tools
import
time
log
=
logging
.
getLogger
(
__name__
)
class
PortalError
(
Exception
):
class
PortalError
(
Exception
):
"""Base exception class
"""Base exception class
...
@@ -37,6 +40,7 @@ class PortalAPIError(PortalError):
...
@@ -37,6 +40,7 @@ class PortalAPIError(PortalError):
super
().
__init__
()
super
().
__init__
()
self
.
_code
=
code
self
.
_code
=
code
self
.
_message
=
message
self
.
_message
=
message
log
.
error
(
f
"[ERR] API ERROR[
{
code
}
]:
{
message
}
"
)
@
property
@
property
def
code
(
self
)
->
int
:
def
code
(
self
)
->
int
:
...
...
portal/service/gitlab.py
View file @
9a32bcd4
import
logging
import
logging
from
typing
import
List
,
Optional
from
typing
import
List
,
Optional
from
urllib.parse
import
urlparse
import
gitlab
import
gitlab
from
gitlab.v4
import
objects
from
gitlab.v4
import
objects
from
portal
import
tools
from
portal
import
tools
from
portal.database
import
Submission
from
portal.database
import
Submission
,
models
from
portal.service
import
errors
from
portal.service
import
errors
from
portal.service.general
import
GeneralService
from
portal.service.general
import
GeneralService
...
@@ -34,8 +35,7 @@ class GitlabService(GeneralService):
...
@@ -34,8 +35,7 @@ class GitlabService(GeneralService):
@
property
@
property
def
gl_client
(
self
):
def
gl_client
(
self
):
if
self
.
_client
is
None
:
if
self
.
_client
is
None
:
log
.
debug
(
f
"[GL_CLIENT] Creating GL client for "
log
.
debug
(
f
"[GL_CLIENT] Creating GL client for token type - [
{
self
.
token_type
}
]"
)
f
"token[
{
self
.
token_type
}
]:
{
self
.
token
}
"
)
self
.
_client
=
self
.
get_client
(
token
=
self
.
_token
,
token_type
=
self
.
_token_type
)
self
.
_client
=
self
.
get_client
(
token
=
self
.
_token
,
token_type
=
self
.
_token_type
)
return
self
.
_client
return
self
.
_client
...
@@ -53,7 +53,8 @@ class GitlabService(GeneralService):
...
@@ -53,7 +53,8 @@ class GitlabService(GeneralService):
params
[
f
'
{
token_type
}
_token'
]
=
token
params
[
f
'
{
token_type
}
_token'
]
=
token
gitlab_url
=
self
.
_config
.
get
(
'GITLAB_URL'
)
gitlab_url
=
self
.
_config
.
get
(
'GITLAB_URL'
)
instance
:
gitlab
.
Gitlab
=
gitlab
.
Gitlab
(
gitlab_url
,
**
params
)
instance
:
gitlab
.
Gitlab
=
gitlab
.
Gitlab
(
gitlab_url
,
**
params
)
log
.
debug
(
f
"[CL_CLIENT] Instance
{
instance
.
url
}
-
{
params
}
"
)
reducted_params
=
{(
k
,
v
)
for
(
k
,
v
)
in
params
.
items
()
if
not
k
.
endswith
(
'_token'
)}
log
.
debug
(
f
"[CL_CLIENT] Instance
{
instance
.
url
}
-
{
reducted_params
}
"
)
try
:
try
:
if
token
:
if
token
:
...
@@ -149,6 +150,16 @@ class GitlabService(GeneralService):
...
@@ -149,6 +150,16 @@ class GitlabService(GeneralService):
return
None
return
None
return
full_name
return
full_name
@
property
def
git_domain
(
self
)
->
str
:
return
self
.
_config
.
get
(
'GITLAB_BASE_DOMAIN'
)
or
urlparse
(
self
.
gl_client
.
url
).
hostname
def
build_ssh_url
(
self
,
client
:
models
.
Client
,
project
:
models
.
Project
):
return
f
"git@
{
self
.
git_domain
}
:
{
self
.
build_repo_name
(
client
,
project
)
}
.git"
def
build_repo_name
(
self
,
client
:
models
.
Client
,
project
:
models
.
Project
):
return
f
"
{
client
.
codename
}
/
{
project
.
course
.
codename
}
"
class
KontrGitlabService
(
GitlabService
):
class
KontrGitlabService
(
GitlabService
):
def
__init__
(
self
):
def
__init__
(
self
):
...
...
portal/service/projects.py
View file @
9a32bcd4
...
@@ -137,7 +137,8 @@ class ProjectService(GeneralService):
...
@@ -137,7 +137,8 @@ class ProjectService(GeneralService):
if
not
ProjectService
(
self
.
project
).
can_create_submission
(
client
):
if
not
ProjectService
(
self
.
project
).
can_create_submission
(
client
):
log
.
warning
(
f
"[WARN] Already active submission for:
{
self
.
project
.
log_name
}
"
)
log
.
warning
(
f
"[WARN] Already active submission for:
{
self
.
project
.
log_name
}
"
)
raise
errors
.
SubmissionRefusedError
(
raise
errors
.
SubmissionRefusedError
(
f
"Submission for project
{
self
.
project
.
log_name
}
already active."
)
f
"User
{
user
.
log_name
}
has already active "
f
"submission for project
{
self
.
project
.
log_name
}
"
)
self
.
_check_latest_submission
(
user
)
self
.
_check_latest_submission
(
user
)
return
True
return
True
...
...
tests/rest/test_gitlab.py
View file @
9a32bcd4
...
@@ -231,8 +231,9 @@ def test_set_gitlab_service_cookies(client, gl_api_url, gl_user, gl_cookies):
...
@@ -231,8 +231,9 @@ def test_set_gitlab_service_cookies(client, gl_api_url, gl_user, gl_cookies):
response
=
rest_tools
.
make_request
(
client
,
f
'/gitlab/service_token'
)
response
=
rest_tools
.
make_request
(
client
,
f
'/gitlab/service_token'
)
assert_response
(
response
)
assert_response
(
response
)
res_proj
=
rest_tools
.
extract_data
(
response
)
res_proj
=
rest_tools
.
extract_data
(
response
)
assert
len
(
res_proj
)
==
4
assert
len
(
res_proj
)
==
5
assert
res_proj
[
'username'
]
==
gl_cookies
[
'username'
]
assert
res_proj
[
'username'
]
==
gl_cookies
[
'username'
]
assert
res_proj
[
'gitlab_username'
]
==
gl_cookies
[
'gitlab_username'
]
assert
res_proj
[
'gitlab_username'
]
==
gl_cookies
[
'gitlab_username'
]
assert
res_proj
[
'gitlab_token'
]
==
gl_cookies
[
'gitlab_token'
]
assert
res_proj
[
'gitlab_token'
]
==
gl_cookies
[
'gitlab_token'
]
assert
res_proj
[
'gitlab_token_type'
]
==
gl_cookies
[
'gitlab_token_type'
]
assert
res_proj
[
'gitlab_token_type'
]
==
gl_cookies
[
'gitlab_token_type'
]
assert
res_proj
[
'gitlab_url'
]
Write
Preview
Supports
Markdown
0%
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!
Cancel
Please
register
or
sign in
to comment