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
Show 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
from
portal
import
db
from
portal.database.exceptions
import
PortalDbError
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.time
import
normalize_time
...
...
@@ -580,10 +580,10 @@ class ProjectConfig(db.Model, EntityBase):
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
file_whitelist
=
db
.
Column
(
db
.
Text
)
pre_submit_script
=
db
.
Column
(
db
.
Tex
t
)
post_submit_script
=
db
.
Column
(
db
.
Tex
t
)
submission_parameters
=
db
.
Column
(
db
.
Tex
t
)
submission_scheduler_config
=
db
.
Column
(
db
.
Tex
t
)
pre_submit_script
=
db
.
Column
(
YAMLEncodedDic
t
)
post_submit_script
=
db
.
Column
(
YAMLEncodedDic
t
)
submission_parameters
=
db
.
Column
(
YAMLEncodedDic
t
)
submission_scheduler_config
=
db
.
Column
(
YAMLEncodedDic
t
)
_submissions_allowed_from
=
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
sqlalchemy
import
yaml
from
sqlalchemy
import
TypeDecorator
...
...
@@ -18,3 +19,19 @@ class JSONEncodedDict(TypeDecorator):
if
value
is
not
None
:
value
=
json
.
loads
(
value
)
return
value
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):
'gitlab_token_type'
:
token_type
,
'gitlab_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
)
...
...
portal/facade/projects_facade.py
View file @
9a32bcd4
...
...
@@ -9,6 +9,25 @@ from portal.service.projects import ProjectService
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
):
def
__init__
(
self
):
super
().
__init__
(
ProjectService
,
'Project'
)
...
...
@@ -67,6 +86,8 @@ class ProjectsFacade(GeneralCRUDFacade):
"""
client
=
client
or
self
.
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
def
get_test_files
(
self
,
project
:
Project
):
...
...
@@ -100,3 +121,11 @@ class ProjectsFacade(GeneralCRUDFacade):
f
"
{
self
.
client_name
}
"
)
result
=
self
.
_services
.
is_api
(
project
.
course
).
create_notepad
(
project
=
project
)
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_jwt_extended
import
jwt_required
from
flask_restplus
import
Namespace
...
...
@@ -209,6 +210,37 @@ class ProjectSubmissions(CustomResource):
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
.
param
(
'cid'
,
'Course id'
)
@
projects_namespace
.
param
(
'pid'
,
'Project id'
)
...
...
portal/service/errors.py
View file @
9a32bcd4
...
...
@@ -2,11 +2,14 @@
Errors in the service layer
"""
import
json
import
logging
from
typing
import
Union
from
portal.database
import
Course
,
Project
,
User
from
portal.tools
import
time
log
=
logging
.
getLogger
(
__name__
)
class
PortalError
(
Exception
):
"""Base exception class
...
...
@@ -37,6 +40,7 @@ class PortalAPIError(PortalError):
super
().
__init__
()
self
.
_code
=
code
self
.
_message
=
message
log
.
error
(
f
"[ERR] API ERROR[
{
code
}
]:
{
message
}
"
)
@
property
def
code
(
self
)
->
int
:
...
...
portal/service/gitlab.py
View file @
9a32bcd4
import
logging
from
typing
import
List
,
Optional
from
urllib.parse
import
urlparse
import
gitlab
from
gitlab.v4
import
objects
from
portal
import
tools
from
portal.database
import
Submission
from
portal.database
import
Submission
,
models
from
portal.service
import
errors
from
portal.service.general
import
GeneralService
...
...
@@ -34,8 +35,7 @@ class GitlabService(GeneralService):
@
property
def
gl_client
(
self
):
if
self
.
_client
is
None
:
log
.
debug
(
f
"[GL_CLIENT] Creating GL client for "
f
"token[
{
self
.
token_type
}
]:
{
self
.
token
}
"
)
log
.
debug
(
f
"[GL_CLIENT] Creating GL client for token type - [
{
self
.
token_type
}
]"
)
self
.
_client
=
self
.
get_client
(
token
=
self
.
_token
,
token_type
=
self
.
_token_type
)
return
self
.
_client
...
...
@@ -53,7 +53,8 @@ class GitlabService(GeneralService):
params
[
f
'
{
token_type
}
_token'
]
=
token
gitlab_url
=
self
.
_config
.
get
(
'GITLAB_URL'
)
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
:
if
token
:
...
...
@@ -149,6 +150,16 @@ class GitlabService(GeneralService):
return
None
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
):
def
__init__
(
self
):
...
...
portal/service/projects.py
View file @
9a32bcd4
...
...
@@ -137,7 +137,8 @@ class ProjectService(GeneralService):
if
not
ProjectService
(
self
.
project
).
can_create_submission
(
client
):
log
.
warning
(
f
"[WARN] Already active submission for:
{
self
.
project
.
log_name
}
"
)
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
)
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):
response
=
rest_tools
.
make_request
(
client
,
f
'/gitlab/service_token'
)
assert_response
(
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
[
'gitlab_username'
]
==
gl_cookies
[
'gitlab_username'
]
assert
res_proj
[
'gitlab_token'
]
==
gl_cookies
[
'gitlab_token'
]
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