aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--captain/api.f90127
-rw-r--r--captain/queryutils.f9072
-rw-r--r--captain/response.f9037
-rw-r--r--captain/security.f9039
-rw-r--r--player/endpoints.f9030
5 files changed, 212 insertions, 93 deletions
diff --git a/captain/api.f90 b/captain/api.f90
index 665ff8d..c05b0ba 100644
--- a/captain/api.f90
+++ b/captain/api.f90
@@ -59,20 +59,24 @@ contains
class(request)::req
integer::job_i, task_i
- if(associated(req%query_string)) then
+ character(len=:), pointer::status
+
+ status => req%q%get_value("status")
+
+ if(associated(status)) then
job_i = req%path_component_int(5)
task_i = req%path_component_int(7)
- call write_log("Task Update Is "//trim(req%query_string), LOG_DEBUG)
- if(req%query_string == "starting") then
+ call write_log("Task Update Is "//trim(status), LOG_DEBUG)
+ if(status == "starting") then
call write_log("Inserting task", LOG_DEBUG)
call insert_task(job_i, task_i)
call update_job_status(job_i, JOB_STATUS_WORKING)
- else if(req%query_string == "inprogress") then
+ else if(status == "inprogress") then
call update_task_status(job_i, task_i, JOB_STATUS_WORKING)
call update_job_status(job_i, JOB_STATUS_WORKING)
- else if(req%query_string == "complete") then
+ else if(status == "complete") then
call update_task_status(job_i, task_i, JOB_STATUS_SUCCESS)
- else if(req%query_string == "failed") then
+ else if(status == "failed") then
call update_task_status(job_i, task_i, JOB_STATUS_FAILURE)
end if
end if
@@ -84,7 +88,7 @@ contains
use captain_db
use special_filenames
use logging
- use query_utilities
+ use security, only: validate_query_token
implicit none
type(request), intent(in)::req
@@ -94,73 +98,88 @@ contains
integer::job_i, player_i, qs_platform_index
character(len=:), pointer::checkin_work_json
- type(query)::q
! Complete - "/api/player/{name}/job/{jobid}/complete"
! Failed - "/api/player/{name}/job/{jobid}/failed"
! Task - "/api/player/{name}/job/{jobid}/task/{task num}"
if(trim(req%component(2)) == "player" .and. trim(req%component(4)) == "job") then
- job_i = req%path_component_int(5)
- call write_log("Job "//trim(req%component(5))//" update arrived", LOG_INFO)
- write(player, *) job_i
-
- if(.not. is_final_job_status(job_i)) then
- if(trim(req%component(6)) == "complete") then
- call update_job_status(job_i, JOB_STATUS_SUCCESS)
- else if(trim(req%component(6)) == "failure") then
- call update_job_status(job_i, JOB_STATUS_FAILURE)
+ if(validate_query_token(req%q%get_value("token"), req%component(3))) then
+
+ job_i = req%path_component_int(5)
+ call write_log("Job "//trim(req%component(5))//" update arrived", LOG_INFO)
+
+ write(player, *) job_i
+
+ if(.not. is_final_job_status(job_i)) then
+ if(trim(req%component(6)) == "complete") then
+ call update_job_status(job_i, JOB_STATUS_SUCCESS)
+ else if(trim(req%component(6)) == "failure") then
+ call update_job_status(job_i, JOB_STATUS_FAILURE)
+ end if
end if
- end if
+
+ if(trim(req%component(6)) == "task") then
+ call write_log("Task update encountered", LOG_INFO)
+ call handle_task_request(req)
+ end if
+
+ resp%code = GEMINI_CODE_SUCCESS
+ call resp%set_body_contents(RESPONSE_JSON_OKAY)
+ resp%body_mimetype = "text/plain"
+
+ else
- if(trim(req%component(6)) == "task") then
- call write_log("Task update encountered", LOG_INFO)
- call handle_task_request(req)
+ resp%code = GEMINI_CODE_BAD_REQUEST
+
end if
-
- resp%code = GEMINI_CODE_SUCCESS
- call resp%set_body_contents(RESPONSE_JSON_OKAY)
- resp%body_mimetype = "text/plain"
! Checkin - /api/player/{name}/checkin.json
else if(trim(req%component(2)) == "player" .and. trim(req%component(4)) == "checkin.json") then
- ! Check for pending jobs
- call req%path_component(3, player)
- player_i = get_player_id(player)
-
- ! If we have a checkin, but the worker should have a job in progress, mark
- ! the jobs as failed.
- call mark_working_jobs_as_failed(player_i)
+
+ if(validate_query_token(req%q%get_value("token"), req%component(3))) then
- ! Acknowledge the checkin in the database
- if(associated(req%query_string)) then
- call q%init(req%query_string)
- if(associated(q%get_value("platform"))) then
- call acknowledge_checkin(player_i, q%get_value("platform"))
+ ! Check for pending jobs
+ call req%path_component(3, player)
+ player_i = get_player_id(player)
+
+ ! If we have a checkin, but the worker should have a job in progress, mark
+ ! the jobs as failed.
+ call mark_working_jobs_as_failed(player_i)
+
+ ! Acknowledge the checkin in the database
+ if(associated(req%query_string)) then
+ if(associated(req%q%get_value("platform"))) then
+ call acknowledge_checkin(player_i, req%q%get_value("platform"))
+ else
+ call acknowledge_checkin(player_i)
+ end if
else
call acknowledge_checkin(player_i)
end if
- call q%destroy()
- else
- call acknowledge_checkin(player_i)
- end if
-
- job_i = get_pending_job_for_player(player_i)
- if(job_i < 0) then
- resp%code = GEMINI_CODE_SUCCESS
- call resp%set_body_contents(RESPONSE_JSON_IDLE)
- else
- checkin_work_json => build_job_available_json(job_i)
- if(associated(checkin_work_json)) then
+
+ job_i = get_pending_job_for_player(player_i)
+ if(job_i < 0) then
resp%code = GEMINI_CODE_SUCCESS
- call write_log("Sending: "//trim(checkin_work_json), LOG_DEBUG)
- call resp%set_body_contents(trim(checkin_work_json), "text/gemini")
- deallocate(checkin_work_json)
+ call resp%set_body_contents(RESPONSE_JSON_IDLE)
else
- resp%code = GEMINI_CODE_PERMFAIL
+ checkin_work_json => build_job_available_json(job_i)
+ if(associated(checkin_work_json)) then
+ resp%code = GEMINI_CODE_SUCCESS
+ call write_log("Sending: "//trim(checkin_work_json), LOG_DEBUG)
+ call resp%set_body_contents(trim(checkin_work_json), "text/gemini")
+ deallocate(checkin_work_json)
+ else
+ resp%code = GEMINI_CODE_PERMFAIL
+ end if
end if
- end if
+ else
+
+ resp%code = GEMINI_CODE_BAD_REQUEST
+
+ end if
+
! Instruction - /api/instructions/{name}
else if(trim(req%component(2)) == "instruction") then
diff --git a/captain/queryutils.f90 b/captain/queryutils.f90
index 00a914b..c6b612b 100644
--- a/captain/queryutils.f90
+++ b/captain/queryutils.f90
@@ -36,7 +36,7 @@ implicit none
end type query_component
type :: query
-
+
character(len=:), pointer::full
type(query_component), dimension(:), pointer::components
@@ -133,42 +133,54 @@ contains
implicit none
class(query), intent(out)::self
- character(len=*), intent(in)::str
+ character(len=*), intent(in), optional::str
character(64)::msg
integer::ampersands, i, i_end, i_comp, n
- n = len_trim(str)
- allocate(character(len=n) :: self%full)
- self%full = str
-
- ampersands = 0
- do i = 1, len(self%full)
- if(self%full(i:i) == '&') then
- ampersands = ampersands + 1
- end if
- end do
-
- allocate(self%components(ampersands + 1))
+ self%components => null()
+ self%full => null()
- ! Split and parse each component
- if(ampersands == 0) then
- call self%components(1)%parse(self%full)
+ if(present(str)) then
+ n = len_trim(str)
else
- i_comp = 1
- i = 1
- i_end = index(self%full, '&')
- do while(i_comp < ampersands + 1)
- call self%components(i_comp)%parse(self%full(i:i_end-1))
- i = i_end + 1
- do i_end = i, len_trim(self%full)
- if(self%full(i_end:i_end) == '&') then
- exit
- end if
- end do
- i_comp = i_comp + 1
+ n = 0
+ end if
+
+ if(n > 0) then
+
+ allocate(character(len=n) :: self%full)
+ self%full = str
+
+ ampersands = 0
+ do i = 1, len(self%full)
+ if(self%full(i:i) == '&') then
+ ampersands = ampersands + 1
+ end if
end do
- call self%components(i_comp)%parse(self%full(i:i_end-1))
+
+ allocate(self%components(ampersands + 1))
+
+ ! Split and parse each component
+ if(ampersands == 0) then
+ call self%components(1)%parse(self%full)
+ else
+ i_comp = 1
+ i = 1
+ i_end = index(self%full, '&')
+ do while(i_comp < ampersands + 1)
+ call self%components(i_comp)%parse(self%full(i:i_end-1))
+ i = i_end + 1
+ do i_end = i, len_trim(self%full)
+ if(self%full(i_end:i_end) == '&') then
+ exit
+ end if
+ end do
+ i_comp = i_comp + 1
+ end do
+ call self%components(i_comp)%parse(self%full(i:i_end-1))
+ end if
+
end if
end subroutine query_init
diff --git a/captain/response.f90 b/captain/response.f90
index 2659c31..3933eb8 100644
--- a/captain/response.f90
+++ b/captain/response.f90
@@ -22,13 +22,15 @@
module server_response
use iso_c_binding
+use query_utilities
implicit none
- integer, parameter::GEMINI_CODE_INPUT = 10
- integer, parameter::GEMINI_CODE_SUCCESS = 20
- integer, parameter::GEMINI_CODE_REDIRECT = 30
- integer, parameter::GEMINI_CODE_TEMPFAIL = 40
- integer, parameter::GEMINI_CODE_PERMFAIL = 50
+ integer, parameter::GEMINI_CODE_INPUT = 10
+ integer, parameter::GEMINI_CODE_SUCCESS = 20
+ integer, parameter::GEMINI_CODE_REDIRECT = 30
+ integer, parameter::GEMINI_CODE_TEMPFAIL = 40
+ integer, parameter::GEMINI_CODE_PERMFAIL = 50
+ integer, parameter::GEMINI_CODE_BAD_REQUEST = 59
character(*), parameter::RESPONSE_JSON_OKAY = '{"status": "okay"}'
@@ -64,7 +66,10 @@ implicit none
character(len=:), pointer::location => null()
character(len=:), pointer::page => null()
character(len=:), pointer::query_string => null()
+ character(len=:), pointer::token => null()
character(len=4)::method = "GET"
+
+ type(query)::q
contains
@@ -77,14 +82,14 @@ implicit none
procedure :: component => request_component_func
procedure :: is_get => request_is_get
procedure :: is_post => request_is_post
-
+ procedure :: has_query => request_has_query
+
end type request
type, extends(request) :: titan_request
integer(kind=8)::size
character(len=:), pointer::mimetype
- character(len=:), pointer::token
type(c_ptr)::ssl_connection
@@ -197,6 +202,12 @@ contains
call toupper(self%method)
end if
+ if(associated(self%query_string)) then
+ call self%q%init(self%query_string)
+ else
+ call self%q%init()
+ end if
+
end subroutine request_init
function request_component_start_location(self, i_component) result(res)
@@ -365,6 +376,16 @@ contains
end function request_is_post
+ function request_has_query(self)
+ implicit none
+
+ class(request)::self
+ logical::request_has_query
+
+ request_has_query = associated(self%query_string) .and. self%q%component_count() > 0
+
+ end function request_has_query
+
subroutine request_destroy(self)
implicit none
@@ -394,6 +415,8 @@ contains
deallocate(self%page)
end if
+ call self%q%destroy()
+
end subroutine request_destroy
subroutine response_destroy(resp)
diff --git a/captain/security.f90 b/captain/security.f90
index 2f5fa4c..44d40a6 100644
--- a/captain/security.f90
+++ b/captain/security.f90
@@ -104,4 +104,43 @@ contains
end function validate_titan_token
+ ! NOTE: A null() token can be passed, and it might even validate!
+ function validate_query_token(token, player)
+ use captain_db
+ implicit none
+
+ character(len=:), pointer::token
+ character(len=*), intent(in)::player
+
+ logical::validate_query_token
+
+ character(len=:), pointer::dbtoken
+
+ validate_query_token = .false.
+
+ if(associated(token)) then
+ allocate(character(len=len(token))::dbtoken)
+ else
+ allocate(character(len=64)::dbtoken)
+ end if
+
+ dbtoken = ' '
+
+ call get_player_token_db(player, dbtoken)
+
+ ! If no token is provided and none is in the db, then we're okay
+ if((.not. associated(token)) .and. len_trim(dbtoken) == 0) then
+
+ validate_query_token = .true.
+
+ else if(associated(token)) then
+
+ validate_query_token = (trim(token) == trim(dbtoken))
+
+ end if
+
+ deallocate(dbtoken)
+
+ end function validate_query_token
+
end module security \ No newline at end of file
diff --git a/player/endpoints.f90 b/player/endpoints.f90
index 09d3faa..8708592 100644
--- a/player/endpoints.f90
+++ b/player/endpoints.f90
@@ -57,6 +57,27 @@ contains
end subroutine base_url
+ subroutine append_query_token(url)
+ use config, only: token
+ implicit none
+
+ character(len=*), intent(inout)::url
+ character::prepend
+
+ if(len_trim(token) > 0) then
+
+ if(index(url, "?") > 0) then
+ prepend = "&"
+ else
+ prepend = "?"
+ end if
+
+ url = trim(url)//prepend//"token="//trim(token)
+
+ end if
+
+ end subroutine append_query_token
+
subroutine get_check_in_url(res)
use config
use utilities, only: replace_field
@@ -66,6 +87,7 @@ contains
call base_url(captain, LOCATION_CHECK_IN, .false., res)
call replace_field(res, "name", identity)
+ call append_query_token(res)
end subroutine get_check_in_url
@@ -92,9 +114,11 @@ contains
call replace_field(url, "step", step)
if(present(status)) then
- url = trim(url)//"?"//trim(status_text(status))
+ url = trim(url)//"?status="//trim(status_text(status))
end if
-
+
+ call append_query_token(url)
+
end subroutine get_status_url
subroutine get_job_report_url(job, success, res)
@@ -113,6 +137,8 @@ contains
end if
call replace_field(res, "name", identity)
call replace_field(res, "jobid", job)
+
+ call append_query_token(res)
end subroutine get_job_report_url