aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Armstrong <jeff@approximatrix.com>2022-04-26 17:10:33 -0400
committerJeffrey Armstrong <jeff@approximatrix.com>2022-04-26 17:10:33 -0400
commitff0095ff928dca78bf07148c4081191fd57f9c4d (patch)
tree9af74ca04fc2a4f0b5237c492c9e411458c8c010
parent9d912cdd8f2fc637a5876ca21a7ce5906e34e889 (diff)
downloadlevitating-ff0095ff928dca78bf07148c4081191fd57f9c4d.tar.gz
levitating-ff0095ff928dca78bf07148c4081191fd57f9c4d.zip
Added routines for validating user passwords and creating new users. Added routines for looking up sessions.
-rw-r--r--captain/config.f908
-rw-r--r--captain/crypt.f902
-rw-r--r--captain/db.f90138
-rw-r--r--captain/example/levitating.conf2
-rw-r--r--captain/levitating-captain.prj3
-rw-r--r--captain/sql/create.sql5
-rw-r--r--captain/sqlite.f9022
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
@@ -84,6 +84,9 @@
"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