From ff0095ff928dca78bf07148c4081191fd57f9c4d Mon Sep 17 00:00:00 2001 From: Jeffrey Armstrong Date: Tue, 26 Apr 2022 17:10:33 -0400 Subject: Added routines for validating user passwords and creating new users. Added routines for looking up sessions. --- captain/config.f90 | 8 ++- captain/crypt.f90 | 2 +- captain/db.f90 | 138 ++++++++++++++++++++++++++++++++++++++++ captain/example/levitating.conf | 2 + captain/levitating-captain.prj | 3 + captain/sql/create.sql | 5 ++ captain/sqlite.f90 | 22 +++++++ 7 files changed, 178 insertions(+), 2 deletions(-) diff --git a/captain/config.f90 b/captain/config.f90 index f3c9bd1..509c489 100644 --- a/captain/config.f90 +++ b/captain/config.f90 @@ -68,6 +68,9 @@ implicit none character(*), parameter::TEMP_DIRECTORY_VARIABLE = "temp-directory" character(1024)::temp_dir = "/tmp" + character(*), parameter::SALT_VARIABLE = "security-salt" + character(1024)::app_salt + contains subroutine get_variable(str, v) @@ -156,7 +159,10 @@ contains else if(cvariable == TEMP_DIRECTORY_VARIABLE) then call set_temporary_directory(cvalue) - + + else if(cvariable == SALT_VARIABLE) then + app_salt = trim(cvalue) + end if end subroutine assign_config diff --git a/captain/crypt.f90 b/captain/crypt.f90 index fc76fdb..c14d4bb 100644 --- a/captain/crypt.f90 +++ b/captain/crypt.f90 @@ -46,7 +46,7 @@ contains ! Build the salt prefix = "$2b$"//c_null_char - c_salt_ptr = crypt_gensalt_c(c_loc(prefix), 16, c_null_ptr, 0) + c_salt_ptr = crypt_gensalt_c(c_loc(prefix), int(16,kind=c_long), c_null_ptr, 0) allocate(character(len=len_trim(phrase)+1) :: c_phrase) c_phrase = trim(phrase)//c_null_char diff --git a/captain/db.f90 b/captain/db.f90 index b7712fa..00ec00e 100644 --- a/captain/db.f90 +++ b/captain/db.f90 @@ -1395,4 +1395,142 @@ contains end subroutine queue_scheduled_jobs + function new_user_db(username, password, email, auth_level) + use config, only: app_salt + use m_crypt, only: hash + use auth_levels, only: AUTH_NORMAL_USER + implicit none + + character(*), intent(in)::username, password, email + integer, intent(in), optional::auth_level + logical::new_user_db + type(sqlite3_stmt)::stmt + + character(len=:), pointer::hashed_pass + + integer::user_auth_level + + if(present(auth_level)) then + user_auth_level = auth_level + else + user_auth_level = AUTH_NORMAL_USER + end if + + new_user_db = .FALSE. + + hashed_pass => hash(trim(password)//trim(app_salt)) + if(associated(hashed_pass)) then + + if(stmt%prepare(db, "INSERT INTO users(username, password, email, level) VALUES(?, ?, ?, ?)") == SQLITE_OK) then + if(stmt%bind_text(1, username) == SQLITE_OK .and. & + stmt%bind_text(2, hashed_pass) == SQLITE_OK .and. & + stmt%bind_text(3, email) == SQLITE_OK .and. & + stmt%bind_int(4, user_auth_level) == SQLITE_OK) then + + new_user_db = any(stmt%step() == (/ SQLITE_OK, SQLITE_DONE /)) + + end if + end if + call stmt%finalize() + + deallocate(hashed_pass) + end if + + end function new_user_db + + function get_password_hash_pointer_db(username) result(password) + implicit none + + character(len=*), intent(in)::username + character(len=:), pointer::password + type(sqlite3_stmt)::stmt + integer::textlength + + password => null() + + if(stmt%prepare(db, "SELECT password FROM users WHERE username=? LIMIT 1") == SQLITE_OK) then + if(stmt%bind_text(1, username) == SQLITE_OK) then + if(stmt%step() == SQLITE_ROW) then + + textlength = stmt%column_text_length(0) + if(textlength > 0) then + allocate(character(len=textlength) :: password) + call stmt%column_text(0, password) + end if + end if + end if + call stmt%finalize() + end if + + end function get_password_hash_pointer_db + + function validate_user_db(username, password) + use config, only: app_salt + use m_crypt, only: hash + implicit none + + character(len=*), intent(in)::username, password + logical::validate_user_db + + character(len=:), pointer::hashed_pass, db_hashed_pass + + validate_user_db = .FALSE. + + hashed_pass => hash(trim(password)//trim(app_salt)) + if(associated(hashed_pass)) then + db_hashed_pass => get_password_hash_pointer_db(username) + if(associated(db_hashed_pass)) then + + validate_user_db = (hashed_pass == db_hashed_pass) + + deallocate(db_hashed_pass) + end if + + deallocate(hashed_pass) + end if + + end function validate_user_db + + function get_user_auth_db(username) + implicit none + + character(len=*), intent(in)::username + integer::get_user_auth_db + + type(sqlite3_stmt)::stmt + + get_user_auth_db = -1 + + if(stmt%prepare(db, "SELECT level FROM users WHERE username=? LIMIT 1") == SQLITE_OK) then + if(stmt%bind_text(1, username) == SQLITE_OK) then + if(stmt%step() == SQLITE_ROW) then + get_user_auth_db = stmt%column_int(0) + end if + end if + call stmt%finalize() + end if + + end function get_user_auth_db + + function get_session_auth_db(session) + implicit none + + character(len=*), intent(in)::session + integer::get_session_auth_db + + type(sqlite3_stmt)::stmt + + get_session_auth_db = -1 + + 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 + if(stmt%step() == SQLITE_ROW) then + get_session_auth_db = stmt%column_int(0) + end if + end if + call stmt%finalize() + end if + + end function get_session_auth_db + end module captain_db diff --git a/captain/example/levitating.conf b/captain/example/levitating.conf index 34dc683..c1c22e1 100644 --- a/captain/example/levitating.conf +++ b/captain/example/levitating.conf @@ -28,3 +28,5 @@ instructions-directory = /home/jeff/Workspace/levitating/captain/example/instruc release-directory = /home/jeff/Workspace/levitating/captain/example/releases temp-directory = /tmp/levitating + +security-salt = aBcD____ \ No newline at end of file diff --git a/captain/levitating-captain.prj b/captain/levitating-captain.prj index a9a6e6e..e55447f 100644 --- a/captain/levitating-captain.prj +++ b/captain/levitating-captain.prj @@ -83,6 +83,9 @@ "Files":[{ "filename":"api.f90", "enabled":"1" + },{ + "filename":"auth_level.f90", + "enabled":"1" },{ "filename":"captain.f90", "enabled":"1" diff --git a/captain/sql/create.sql b/captain/sql/create.sql index 8e93e23..9e4d204 100644 --- a/captain/sql/create.sql +++ b/captain/sql/create.sql @@ -17,3 +17,8 @@ CREATE TABLE checkin(player INTEGER PRIMARY KEY, year INTEGER, month INTEGER, da CREATE TABLE schedule(instruction INTEGER NOT NULL, player INTEGER NOT NULL, day INTEGER DEFAULT 0, hour INTEGER DEFAULT 0, FOREIGN KEY(instruction) REFERENCES instructions(id) ON DELETE CASCADE, FOREIGN KEY(player) REFERENCES players(id) ON DELETE CASCADE ); +CREATE TABLE users(id INTEGER PRIMARY KEY, username TEXT UNIQUE NOT NULL, email TEXT NOT NULL, password TEXT NOT NULL, level INTEGER DEFAULT 1); + +CREATE TABLE sessions(user INTEGER, session TEXT NOT NULL, expiration TEXT NOT NULL, FOREIGN KEY(user) REFERENCES users(id) ON DELETE CASCADE); + +CREATE VIEW session_auth AS SELECT sessions.session, users.username, users.level FROM sessions INNER JOIN users ON sessions.user = users.id; diff --git a/captain/sqlite.f90 b/captain/sqlite.f90 index e7bf6a1..6bad946 100644 --- a/captain/sqlite.f90 +++ b/captain/sqlite.f90 @@ -116,6 +116,13 @@ implicit none type(c_ptr)::c_sqlite3_column_text end function c_sqlite3_column_text + function c_sqlite3_column_bytes(p, i) bind(c, name="sqlite3_column_bytes") + use iso_c_binding + type(c_ptr), value::p + integer(c_int), value::i + integer(c_int)::c_sqlite3_column_bytes + end function c_sqlite3_column_bytes + end interface ! Good to go @@ -165,6 +172,7 @@ implicit none procedure::column_int => stmt_column_int procedure::column_text => stmt_column_text procedure::column_type => stmt_column_type + procedure::column_text_length => stmt_column_text_length end type @@ -375,4 +383,18 @@ contains end subroutine stmt_column_text + function stmt_column_text_length(self, i) + implicit none + + class(sqlite3_stmt), intent(inout)::self + integer, intent(in)::i + integer::stmt_column_text_length + + stmt_column_text_length = 0 + if(self%column_type(i) /= SQLITE_NULL) then + stmt_column_text_length = c_sqlite3_column_bytes(self%stmt, i) + end if + + end function stmt_column_text_length + end module sqlite \ No newline at end of file -- cgit v1.2.3