diff options
Diffstat (limited to 'captain')
-rw-r--r-- | captain/api.f90 | 127 | ||||
-rw-r--r-- | captain/queryutils.f90 | 72 | ||||
-rw-r--r-- | captain/response.f90 | 37 | ||||
-rw-r--r-- | captain/security.f90 | 39 |
4 files changed, 184 insertions, 91 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 |