diff options
-rw-r--r-- | captain/config.f90 | 92 | ||||
-rw-r--r-- | captain/db.f90 | 3 | ||||
-rw-r--r-- | captain/example/levitating-permissions.conf | 14 | ||||
-rw-r--r-- | captain/example/levitating.conf | 27 | ||||
-rw-r--r-- | captain/http.f90 | 1 | ||||
-rw-r--r-- | captain/levitating-captain.prj | 3 | ||||
-rw-r--r-- | captain/requtils.f90 | 54 | ||||
-rw-r--r-- | captain/response.f90 | 12 | ||||
-rw-r--r-- | captain/web.f90 | 226 |
9 files changed, 321 insertions, 111 deletions
diff --git a/captain/config.f90 b/captain/config.f90 index 509c489..2d67adf 100644 --- a/captain/config.f90 +++ b/captain/config.f90 @@ -71,6 +71,24 @@ implicit none character(*), parameter::SALT_VARIABLE = "security-salt" character(1024)::app_salt + character(*), parameter::PERMISSIONS_FILE_VARIABLE = "permissions_file" + character(1024)::perm_filename + + integer, parameter::MAX_PERMISSIONS = 16 + + type::permissions + character(len=40), dimension(MAX_PERMISSIONS)::k + integer, dimension(MAX_PERMISSIONS)::v + + contains + + procedure :: load => load_permissions + procedure :: get => get_permission + + end type permissions + + type(permissions)::global_permissions + contains subroutine get_variable(str, v) @@ -162,6 +180,9 @@ contains else if(cvariable == SALT_VARIABLE) then app_salt = trim(cvalue) + + else if(cvariable == PERMISSIONS_FILE_VARIABLE) then + perm_filename = trim(cvalue) end if @@ -174,6 +195,7 @@ contains integer::unit_number, istatus character(1024)::line, cvalue character(64)::cvariable + integer::i open(newunit=unit_number, file=trim(filename), status='old', & action="read", iostat=istatus) @@ -195,6 +217,16 @@ contains close(unit_number) + if(len_trim(perm_filename) > 0) then + if(perm_filename(1:1) /= "/") then + i = index(filename, "/", back=.true.) + if(i > 1) then + perm_filename = filename(1:i)//trim(perm_filename) + end if + end if + call global_permissions%load(perm_filename) + end if + end subroutine load_configuration subroutine template_filepath(x, res) @@ -207,5 +239,65 @@ contains call combine_paths(template_directory, x, res) end subroutine template_filepath + + subroutine load_permissions(self, filename) + implicit none + + class(permissions), intent(out)::self + character(*), intent(in)::filename + + integer::unit_number, istatus + character(1024)::line, cvalue + character(64)::cvariable + integer::i + + self%k = " " + + open(newunit=unit_number, file=trim(filename), status='old', & + action="read", iostat=istatus) + + i = 1 + + read(unit_number, '(A)', iostat=istatus) line + do while(istatus == 0 .and. i <= MAX_PERMISSIONS) + + if(len_trim(line) > 0 .and. line(1:1) /= '#') then + + call get_variable(line, cvariable) + call get_value(line, cvalue) + + self%k(i) = cvariable + read(cvalue, *, iostat=istatus) self%v(i) + + i = i + 1 + end if + + read(unit_number, '(A)', iostat=istatus) line + end do + + close(unit_number) + + end subroutine load_permissions + + pure function get_permission(self, key) + use auth_levels, only: AUTH_ADMIN_USER + implicit none + + class(permissions), intent(in)::self + character(len=*), intent(in)::key + integer::get_permission + + integer::i + + get_permission = AUTH_ADMIN_USER + + do i = 1, MAX_PERMISSIONS + if(trim(self%k(i)) == trim(key)) then + get_permission = self%v(i) + exit + end if + end do + + end function get_permission end module config diff --git a/captain/db.f90 b/captain/db.f90 index 97c397a..44bc473 100644 --- a/captain/db.f90 +++ b/captain/db.f90 @@ -1541,6 +1541,7 @@ contains end function get_user_auth_db function get_session_auth_db(session) + use auth_levels, only: AUTH_NONE implicit none character(len=*), intent(in)::session @@ -1548,7 +1549,7 @@ contains type(sqlite3_stmt)::stmt - get_session_auth_db = -1 + get_session_auth_db = AUTH_NONE if(stmt%prepare(db, "SELECT level FROM session_auth WHERE session=? LIMIT 1") == SQLITE_OK) then if(stmt%bind_text(1, session) == SQLITE_OK) then diff --git a/captain/example/levitating-permissions.conf b/captain/example/levitating-permissions.conf new file mode 100644 index 0000000..a487872 --- /dev/null +++ b/captain/example/levitating-permissions.conf @@ -0,0 +1,14 @@ + +access-releases = 0 +access-logs = 0 + +add-players = 10 +modify-players = 5 + +scan-instructions = 10 +launch-job = 5 +assign-instructions = 5 +view-raw-instructions = 0 + +add-groups = 5 +modify-groups = 5 diff --git a/captain/example/levitating.conf b/captain/example/levitating.conf index c1c22e1..06b89ed 100644 --- a/captain/example/levitating.conf +++ b/captain/example/levitating.conf @@ -1,9 +1,9 @@ -template-directory = /home/jeff/Workspace/levitating/captain/templates +template-directory = /home/jeff/workspace/levitating/captain/templates -database = /home/jeff/Workspace/levitating/captain/example/store.db +database = /home/jeff/workspace/levitating/captain/example/store.db -log-filename = /home/jeff/Workspace/levitating/captain/example/log/levitating.log +log-filename = /home/jeff/workspace/levitating/captain/example/log/levitating.log log-level = 10 @@ -11,22 +11,25 @@ project = misc-build description = A builder for stuff -public-cert = /home/jeff/Workspace/levitating/captain/example/pub.crt +public-cert = /home/jeff/workspace/levitating/captain/example/pub.crt -private-cert = /home/jeff/Workspace/levitating/captain/example/priv.key +private-cert = /home/jeff/workspace/levitating/captain/example/priv.key -uploads-directory = /home/jeff/Workspace/levitating/captain/example/uploads +uploads-directory = /home/jeff/workspace/levitating/captain/example/uploads -results-directory = /home/jeff/Workspace/levitating/captain/example/results +results-directory = /home/jeff/workspace/levitating/captain/example/results -static-directory = /home/jeff/Workspace/levitating/captain/example/static +static-directory = /home/jeff/workspace/levitating/captain/example/static -script-directory = /home/jeff/Workspace/levitating/captain/sql +script-directory = /home/jeff/workspace/levitating/captain/sql -instructions-directory = /home/jeff/Workspace/levitating/captain/example/instructions +instructions-directory = /home/jeff/workspace/levitating/captain/example/instructions -release-directory = /home/jeff/Workspace/levitating/captain/example/releases +release-directory = /home/jeff/workspace/levitating/captain/example/releases temp-directory = /tmp/levitating -security-salt = aBcD____
\ No newline at end of file +security-salt = aBcD____ + +# Must be absolute path or relative to this file +permissions_file = levitating-permissions.conf diff --git a/captain/http.f90 b/captain/http.f90 index 9e2fca5..8449ba2 100644 --- a/captain/http.f90 +++ b/captain/http.f90 @@ -27,6 +27,7 @@ implicit none integer, parameter::HTTP_CODE_NOTFOUND = 404 integer, parameter::HTTP_CODE_FAILURE = 500 integer, parameter::HTTP_CODE_REDIRECT = 302 + integer, parameter::HTTP_CODE_UNAUTHORIZED = 401 contains diff --git a/captain/levitating-captain.prj b/captain/levitating-captain.prj index 4af03c5..f949fb2 100644 --- a/captain/levitating-captain.prj +++ b/captain/levitating-captain.prj @@ -49,6 +49,9 @@ }], "Name":"+example", "Files":[{ + "filename":"example/levitating-permissions.conf", + "enabled":"1" + },{ "filename":"example/levitating.conf", "enabled":"1" }] diff --git a/captain/requtils.f90 b/captain/requtils.f90 index 41eacc6..620170a 100644 --- a/captain/requtils.f90 +++ b/captain/requtils.f90 @@ -71,6 +71,24 @@ contains end if end function notfound_code + + pure function notpermitted_code(req) + use http, only: HTTP_UNAUTHORIZED => HTTP_CODE_UNAUTHORIZED + use server_response, only: request, GEMINI_UNAUTHORIZED => GEMINI_CODE_BAD_REQUEST + implicit none + + class(request), intent(in)::req + integer::notpermitted_code + + if(req%protocol == 'gemini') then + ! You might think we'd use Gemini certificates, but fuck certificates... + ! Just fail with a bad request. + notpermitted_code = GEMINI_UNAUTHORIZED + else + notpermitted_code = HTTP_UNAUTHORIZED + end if + + end function notpermitted_code subroutine basic_mimetype(actual_filename, mimetype) use utilities, only: get_one_line_output_shell_command @@ -239,19 +257,30 @@ contains call req%path_component(1, category) call req%path_starting_with_component(2, filename) - resp%body_filename => get_special_full_filename(trim(category), trim(filename)) - - inquire(file=resp%body_filename, exist=exists) - if(.not. exists) then - - resp%code = notfound_code(req) - call write_log("File did not exist: "//resp%body_filename, LOG_NORMAL) + if((req%auth_level < global_permissions%get("view-raw-instructions") .and. trim(category) == "instructions") .or. & + (req%auth_level < global_permissions%get("access-releases") .and. trim(category) == "releases") .or. & + (req%auth_level < global_permissions%get("access-logs") .and. trim(category) == "results")) & + then + resp%code = notpermitted_code(req) + else + + resp%body_filename => get_special_full_filename(trim(category), trim(filename)) + + inquire(file=resp%body_filename, exist=exists) + if(.not. exists) then - resp%code = success_code(req) - call basic_mimetype(resp%body_filename, resp%body_mimetype) + resp%code = notfound_code(req) + call write_log("File did not exist: "//resp%body_filename, LOG_NORMAL) + + else + + resp%code = success_code(req) + call basic_mimetype(resp%body_filename, resp%body_mimetype) + end if + end if end function request_static @@ -652,6 +681,7 @@ contains use captain_db use server_response use remote_launch + use config, only: global_permissions implicit none type(request), intent(in)::req @@ -668,15 +698,15 @@ contains command = req%query_string(1:i-1) argument = req%query_string(i+1:len_trim(req%query_string)) - if(trim(command) == "launch") then + if(trim(command) == "launch" .and. req%auth_level >= global_permissions%get("launch-job")) then call launch_instructions_on_player(instruction_name, argument) - else if(trim(command) == "assign") then + else if(trim(command) == "assign" .and. req%auth_level >= global_permissions%get("assign-instructions")) then i = get_instruction_id(trim(instruction_name)) j = get_player_id(trim(argument)) call add_player_for_instruction(i, j) - else if(trim(command) == "remove") then + else if(trim(command) == "remove" .and. req%auth_level >= global_permissions%get("assign-instructions")) then i = get_instruction_id(trim(instruction_name)) j = get_player_id(trim(argument)) call remove_player_for_instruction(i, j) diff --git a/captain/response.f90 b/captain/response.f90 index 34c537c..e172cc1 100644 --- a/captain/response.f90 +++ b/captain/response.f90 @@ -73,6 +73,8 @@ implicit none character(len=:), pointer::token => null() character(len=4)::method = "GET" + integer::auth_level + type(query)::q type(cookies)::c @@ -111,6 +113,8 @@ contains subroutine request_init(self, str, server_explicit, protocol_explicit, method, cookiestring) use logging use utilities, only: toupper + use captain_db, only: get_session_auth_db + use auth_levels, only: AUTH_NONE implicit none class(request) :: self @@ -222,6 +226,14 @@ contains if(.not.associated(self%token) .and. associated(self%c%get_value("token"))) then self%token => self%c%get_value("token") end if + else + call self%c%init() + end if + + if(associated(self%token)) then + self%auth_level = get_session_auth_db(self%token) + else + self%auth_level = AUTH_NONE end if end subroutine request_init diff --git a/captain/web.f90 b/captain/web.f90 index b990c1a..9d8fc3c 100644 --- a/captain/web.f90 +++ b/captain/web.f90 @@ -70,6 +70,17 @@ contains end function request_is_authenticated + subroutine echo_error_stdout(error_code) + implicit none + + integer, intent(in)::error_code + + Print *, "<html><head><title>Falling...</title></head><body><p>An Error Occurred:" + Print *, error_code + Print *, "</p></body></html>" + + end subroutine echo_error_stdout + subroutine handle_basic_template_components(req, page) use server_response use page_template @@ -151,6 +162,7 @@ contains use captain_db use server_response use request_utils, only: get_player_status_utf8, render_jobs_links, generate_simple_pager + use config, only: global_permissions implicit none type(request)::req @@ -219,14 +231,17 @@ contains res = "<h2>"//trim(instruction_name)//"</h2>" - one_link => html_link(trim(instruction_name)//".json", & - "View Raw") - res = trim(res)//nl//"<p><em>"//one_link//"</em></p>" - deallocate(one_link) + if(req%auth_level >= global_permissions%get("view-raw-instructions")) then + one_link => html_link(trim(instruction_name)//".json", & + "View Raw") + res = trim(res)//nl//"<p><em>"//one_link//"</em></p>" + deallocate(one_link) + end if if(n_players == 0) then res = trim(res)//nl//"<p>No players currently can run these instructions</p>" - else + + else if(req%auth_level >= global_permissions%get("launch-job")) then res = trim(res)//nl//"<h3>Launch Now</h3>"//nl//"<ul>" do i = 1, n_players @@ -260,39 +275,41 @@ contains end if - all_players => get_player_names() - if(associated(all_players)) then - res = trim(res)//nl//"<h3>Assign</h3>"//nl//"<p>Assign a player to these instructions</p>"//nl//"<ul>" - do i = 1, size(all_players) - if(n_players > 0) then - j = get_player_id(all_players(i)) - if(any(j == players)) then - cycle + if(req%auth_level >= global_permissions%get("assign-instructions")) then + all_players => get_player_names() + if(associated(all_players)) then + res = trim(res)//nl//"<h3>Assign</h3>"//nl//"<p>Assign a player to these instructions</p>"//nl//"<ul>" + do i = 1, size(all_players) + if(n_players > 0) then + j = get_player_id(all_players(i)) + if(any(j == players)) then + cycle + end if end if - end if - - one_link => html_link(req%page//"?assign="//trim(all_players(i)), & - trim(all_players(i))) - - res = trim(res)//nl//"<li>"//one_link//"</li>" - deallocate(one_link) - end do - res = trim(res)//nl//"</ul>" - deallocate(all_players) - end if - - if(n_players > 0) then - res = trim(res)//nl//"<h3>Remove</h3>"//nl//"<p>Remove a player from these instructions</p>"//nl//"<ul>" - do i = 1, n_players - call get_player_name(players(i), player_name) - - one_link => html_link(req%page//"?remove="//trim(player_name), & - trim(player_name)) - - res = trim(res)//nl//"<li>"//one_link//"</li>" - deallocate(one_link) - end do - res = trim(res)//nl//"</ul>" + + one_link => html_link(req%page//"?assign="//trim(all_players(i)), & + trim(all_players(i))) + + res = trim(res)//nl//"<li>"//one_link//"</li>" + deallocate(one_link) + end do + res = trim(res)//nl//"</ul>" + deallocate(all_players) + end if + + if(n_players > 0) then + res = trim(res)//nl//"<h3>Remove</h3>"//nl//"<p>Remove a player from these instructions</p>"//nl//"<ul>" + do i = 1, n_players + call get_player_name(players(i), player_name) + + one_link => html_link(req%page//"?remove="//trim(player_name), & + trim(player_name)) + + res = trim(res)//nl//"<li>"//one_link//"</li>" + deallocate(one_link) + end do + res = trim(res)//nl//"</ul>" + end if end if end function generate_one_instuction_html @@ -300,6 +317,7 @@ contains function generate_instructions_html(req) result(res) use captain_db use server_response, only:request + use config, only: global_permissions implicit none type(request)::req @@ -340,17 +358,20 @@ contains end if - res = trim(res)//new_line(' ')//"<h2>Management</h2>" - - scanlink => html_link(req%page//"?scan", "Scan for instructions now") - res = trim(res)//new_line(' ')//"<p>"//scanlink//"</p>" - deallocate(scanlink) + if(req%auth_level >= global_permissions%get("scan-instructions")) then + res = trim(res)//new_line(' ')//"<h2>Management</h2>" + + scanlink => html_link(req%page//"?scan", "Scan for instructions now") + res = trim(res)//new_line(' ')//"<p>"//scanlink//"</p>" + deallocate(scanlink) + end if end function generate_instructions_html function generate_groups_html(req) result(res) use captain_db use server_response, only:request + use config, only: global_permissions implicit none type(request)::req @@ -389,11 +410,13 @@ contains end if - res = trim(res)//new_line(' ')//"<h2>Management</h2>" - - res = trim(res)//new_line(' ')// & - '<form action="groups/add.html" method="POST"><label for="name">Name:</label>'// & - '<input name="name" id="name" /><input type="submit" value="Add"/></form>' + if(req%auth_level >= global_permissions%get("add-groups")) then + res = trim(res)//new_line(' ')//"<h2>Management</h2>" + + res = trim(res)//new_line(' ')// & + '<form action="groups/add.html" method="POST"><label for="name">Name:</label>'// & + '<input name="name" id="name" /><input type="submit" value="Add"/></form>' + end if end function generate_groups_html @@ -404,6 +427,7 @@ contains use query_utilities use logging use remote_launch, only: launch_group + use config, only: global_permissions implicit none type(request)::req @@ -437,7 +461,7 @@ contains call q%init(req%query_string) qreq => q%get_value("add") - if(associated(qreq)) then + if(associated(qreq) .and. req%auth_level >= global_permissions%get("add-groups")) then call write_log("ADD: "//trim(qreq)) @@ -454,7 +478,7 @@ contains qreq => q%get_value("delete") - if(associated(qreq)) then + if(associated(qreq) .and. req%auth_level >= global_permissions%get("modify-groups")) then i = index(qreq, ',') player_name = qreq(i+1:len(qreq)) @@ -465,12 +489,12 @@ contains call remove_entry_from_group_db(id, i, j) - else if(trim(req%query_string) == "launch") then + else if(trim(req%query_string) == "launch" .and. req%auth_level >= global_permissions%get("launch-job")) then call launch_group(id) write(launch_msg, '(I4, 1X, A13)') get_group_entries_count_db(id), "jobs launched" - else if(trim(req%query_string) == "destroy") then + else if(trim(req%query_string) == "destroy" .and. req%auth_level >= global_permissions%get("modify-groups")) then call delete_group_db(id) @@ -511,15 +535,22 @@ contains one_link => html_link("../instructions/"//trim(instruction_name)//".html", trim(instruction_name)) play_link => html_link("../players/"//trim(player_name)//".html", trim(player_name)) - delete_link => html_link(trim(group_name)//".html?delete="// & - trim(instruction_name)//","//trim(player_name), & - "<em>Remove</em>") - res = trim(res)//new_line(' ')//"<li>"//one_link//" on "//play_link//" - "//delete_link//"</li>" + + res = trim(res)//new_line(' ')//"<li>"//one_link//" on "//play_link + + if(req%auth_level >= global_permissions%get("modify-groups")) then + delete_link => html_link(trim(group_name)//".html?delete="// & + trim(instruction_name)//","//trim(player_name), & + "<em>Remove</em>") + res = trim(res)//" - "//delete_link + deallocate(delete_link) + end if + + res = trim(res)//"</li>" deallocate(one_link) deallocate(play_link) - deallocate(delete_link) end do @@ -527,7 +558,7 @@ contains end if - if(n_instructions_total > 0) then + if(n_instructions_total > 0 .and. req%auth_level >= global_permissions%get("modify-groups")) then res = trim(res)//new_line(' ')//"<h3>Add Instructions</h3>" @@ -559,16 +590,22 @@ contains end if end if - res = trim(res)//new_line(' ')//'<h3>Destroy This Group</h3>'//new_line(' ')// & - '<p><a href="'//req%page//'?destroy">💣 Destroy</a></p>'//new_line(' ')// & - '<p><em>This operation will not destroy any instructions</em></p>' + if(req%auth_level >= global_permissions%get("modify-groups")) then + res = trim(res)//new_line(' ')//'<h3>Destroy This Group</h3>'//new_line(' ')// & + '<p><a href="'//req%page//'?destroy">💣 Destroy</a></p>'//new_line(' ')// & + '<p><em>This operation will not destroy any instructions</em></p>' + end if end function generate_one_group_html - function generate_players_html() result(res) + function generate_players_html(req) result(res) use captain_db use request_utils, only: get_status_utf8 + use server_response, only: request + use config, only: global_permissions implicit none + + type(request), intent(in)::req character(len=:), pointer::res character(len=PLAYER_NAME_LENGTH), dimension(:), pointer::players @@ -619,9 +656,11 @@ contains deallocate(players) end if - res = trim(res)//new_line(' ')//"<h2>Management</h2>"// & - new_line(' ')//'<form action="players/add.html" method="POST"><label for="name">Name:</label>'// & - '<input name="name" id="name" /><input type="submit" value="Add"/></form>' + if(req%auth_level >= global_permissions%get("add-players")) then + res = trim(res)//new_line(' ')//"<h2>Management</h2>"// & + new_line(' ')//'<form action="players/add.html" method="POST"><label for="name">Name:</label>'// & + '<input name="name" id="name" /><input type="submit" value="Add"/></form>' + end if end function generate_players_html @@ -629,6 +668,7 @@ contains use captain_db use server_response use request_utils + use config, only: global_permissions implicit none type(request), intent(in)::req @@ -683,24 +723,26 @@ contains res = trim(res)//"<p>None Yet</p>" end if - ! Token assignment - res = trim(res)//new_line(' ')//"<h3>Security</h3>"//new_line(' ')//"<p>" - - if(player_has_token_db(trim(player_name))) then - res = trim(res)//"Player currently has a token assigned." - else - res = trim(res)//"<em>Player is insecure! Please assign a token!</em>" + if(req%auth_level >= global_permissions%get("modify-players")) then + ! Token assignment + res = trim(res)//new_line(' ')//"<h3>Security</h3>"//new_line(' ')//"<p>" + + if(player_has_token_db(trim(player_name))) then + res = trim(res)//"Player currently has a token assigned." + else + res = trim(res)//"<em>Player is insecure! Please assign a token!</em>" + end if + + res = trim(res)//"</p>" + + res = trim(res)//new_line(' ')// & + '<form action="assign_token.html" method="POST">'//new_line(' ')// & + '<label for="token">Token:</label>'// & + '<input name="token" id="token" />'//new_line(' ')// & + '<input type="hidden" name="player" id="player" value="'//trim(player_name)//'"/>'//new_line(' ')// & + '<input type="submit" value="Set"/></form>' end if - res = trim(res)//"</p>" - - res = trim(res)//new_line(' ')// & - '<form action="assign_token.html" method="POST">'//new_line(' ')// & - '<label for="token">Token:</label>'// & - '<input name="token" id="token" />'//new_line(' ')// & - '<input type="hidden" name="player" id="player" value="'//trim(player_name)//'"/>'//new_line(' ')// & - '<input type="submit" value="Set"/></form>' - end function generate_one_player_html function generate_one_job_html(req) result(res) @@ -827,6 +869,17 @@ contains res => null() + ! Auth check + if(req%auth_level < global_permissions%get("access-releases")) then + allocate(character(len=64)::res) + if(req%auth_level == 0) then + res = "Login required." + else + res = "Unacceptable permissions" + end if + return + end if + ! Easy safety check - no relative paths if(index(local_path, '..') > 0) then allocate(character(len=64)::res) @@ -1045,7 +1098,7 @@ contains else if(trim(req%location) == "/players.html") then call page%assign('title', 'Players') - contents => generate_players_html() + contents => generate_players_html(req) call page%assign('contents', contents) else if(req%location(1:9) == '/players/') then @@ -1155,7 +1208,7 @@ contains use captain_db, only: add_player_db, add_group_db, update_player_token_db, create_user_session_db, & validate_user_db use page_template - use config, only: template_filepath + use config, only: template_filepath, global_permissions use logging use server_response, only:request, response use http, only: HTTP_CODE_FAILURE, HTTP_CODE_SUCCESS @@ -1187,12 +1240,12 @@ contains call req%path_component(2, second) ! Add a player - if(trim(second) == "add.html") then + if(trim(second) == "add.html" .and. req%auth_level >= global_permissions%get("add-players")) then call add_player_db(posted%get_value("name")) call page%assign('destination', 'players.html') - else if(trim(second) == "assign_token.html") then + else if(trim(second) == "assign_token.html" .and. req%auth_level >= global_permissions%get("modify-players")) then call update_player_token_db(posted%get_value("player"), posted%get_value("token")) call page%assign('destination', "players/"//posted%get_value("player")//".html") @@ -1204,7 +1257,7 @@ contains call req%path_component(2, second) ! Add a group - if(trim(second) == "add.html") then + if(trim(second) == "add.html" .and. req%auth_level >= global_permissions%get("add-groups")) then call add_group_db(posted%get_value("name")) call page%assign('destination', 'groups.html') @@ -1293,8 +1346,9 @@ contains end if call echo_file_stdout(resp%body_filename) - case(HTTP_CODE_FAILURE) + case default call write_log("Failure reported for location: "//trim(req%location), LOG_NORMAL) + call echo_error_stdout(resp%code) ! Need some more... end select |