diff --git a/MySQL_conf_pbx/test1/Rest-API-Postman/CezenPBX_API.postman_collection_Security.json b/MySQL_conf_pbx/test1/Rest-API-Postman/CezenPBX_API.postman_collection_Security.json new file mode 100644 index 0000000..2c66ad7 --- /dev/null +++ b/MySQL_conf_pbx/test1/Rest-API-Postman/CezenPBX_API.postman_collection_Security.json @@ -0,0 +1,913 @@ +{ + "info": { + "_postman_id": "721d5504-301f-488d-a25b-5e78769eac5a", + "name": "CezenPBX_API", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "29498098" + }, + "item": [ + { + "name": "create a new endpoint", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"1005\",\n// \"transport\": \"transport-udp\",\n// \"context\": \"default\",\n// \"disallow\": \"all\",\n// \"allow\": \"ulaw,alaw\",\n// \"directMedia\": \"no\",\n \"connectedLineMethod\": null,\n \"callerid\": null,\n \"dtmfMode\": null,\n// \"mohsuggest\": \"default\",\n \"mailboxes\": null\n}\n\n// {\n// \"id\": \"1004\",\n// \"transport\": \"transport-udp\",\n// \"aors\": \"1004\",\n// \"auth\": \"1004\",\n// \"context\": \"default\",\n// \"disallow\": \"all\",\n// \"allow\": \"ulaw,alaw\",\n// \"directMedia\": \"no\",\n// \"connectedLineMethod\": null,\n// \"callerid\": \"User <1004>\",\n// \"dtmfMode\": null,\n// \"mohsuggest\": \"default\",\n// \"mailboxes\": \"1004@default\"\n// }", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/cezen/add_user", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "add_user" + ] + } + }, + "response": [] + }, + { + "name": "create a new extension", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"context\": \"default\",\n \"extension\": \"1005\",\n \"priority\": 1,\n \"app\": \"Dial\",\n \"appdata\": \"PJSIP/1005,20,m(default)\"\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/cezen/add_extension", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "add_extension" + ] + } + }, + "response": [ + { + "name": "1005", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"context\": \"default\",\n \"extension\": \"1005\",\n \"priority\": 1,\n \"app\": \"Dial\",\n \"appdata\": \"PJSIP/1005,20,m(default)\"\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/cezen/add_extension", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "add_extension" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Method" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Headers" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-XSS-Protection", + "value": "0" + }, + { + "key": "Cache-Control", + "value": "no-cache, no-store, max-age=0, must-revalidate" + }, + { + "key": "Pragma", + "value": "no-cache" + }, + { + "key": "Expires", + "value": "0" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Date", + "value": "Thu, 15 May 2025 10:20:09 GMT" + }, + { + "key": "Keep-Alive", + "value": "timeout=60" + }, + { + "key": "Connection", + "value": "keep-alive" + } + ], + "cookie": [], + "body": "{\n \"status\": false,\n \"message\": \"Data likely already exists or DB issue\",\n \"exceptionMessage\": \"could not execute statement [(conn=482) Duplicate entry '1005-PJSIP/1005,20,m(default)-1' for key 'extension_table_unique_val'] [insert into extensions_table (app,appdata,context,exten,priority) values (?,?,?,?,?)]; SQL [insert into extensions_table (app,appdata,context,exten,priority) values (?,?,?,?,?)]; constraint [extension_table_unique_val]\"\n}" + }, + { + "name": "test1", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"context\": \"default\",\n \"extension\": \"test1\",\n \"priority\": 1,\n \"app\": \"Dial\",\n \"appdata\": \"test123rf\"\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/cezen/add_extension", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "add_extension" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Method" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Headers" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-XSS-Protection", + "value": "0" + }, + { + "key": "Cache-Control", + "value": "no-cache, no-store, max-age=0, must-revalidate" + }, + { + "key": "Pragma", + "value": "no-cache" + }, + { + "key": "Expires", + "value": "0" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Date", + "value": "Thu, 15 May 2025 10:21:06 GMT" + }, + { + "key": "Keep-Alive", + "value": "timeout=60" + }, + { + "key": "Connection", + "value": "keep-alive" + } + ], + "cookie": [], + "body": "{\n \"status\": true,\n \"message\": \"test1 Persisted \",\n \"exceptionMessage\": \"\"\n}" + } + ] + }, + { + "name": "set_password", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"1005\",\n \"authType\": \"userpass\",\n \"userName\": \"1005\",\n \"password\": \"12345\",\n \"md5Cred\": null,\n \"realm\": null\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/cezen/set_password", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "set_password" + ] + } + }, + "response": [ + { + "name": "set_password", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"1005\",\n \"authType\": \"userpass\",\n \"userName\": \"1005\",\n \"password\": \"12345\",\n \"md5Cred\": null,\n \"realm\": null\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/cezen/set_password", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "set_password" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Method" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Headers" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-XSS-Protection", + "value": "0" + }, + { + "key": "Cache-Control", + "value": "no-cache, no-store, max-age=0, must-revalidate" + }, + { + "key": "Pragma", + "value": "no-cache" + }, + { + "key": "Expires", + "value": "0" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Date", + "value": "Thu, 15 May 2025 10:21:54 GMT" + }, + { + "key": "Keep-Alive", + "value": "timeout=60" + }, + { + "key": "Connection", + "value": "keep-alive" + } + ], + "cookie": [], + "body": "{\n \"status\": false,\n \"message\": \"Endpoint and password already set \",\n \"exceptionMessage\": \"could not execute statement [(conn=482) Duplicate entry '1005' for key 'PRIMARY'] [insert into ps_auths (auth_type,md5_cred,password,realm,username,id) values (?,?,?,?,?,?)]; SQL [insert into ps_auths (auth_type,md5_cred,password,realm,username,id) values (?,?,?,?,?,?)]; constraint [PRIMARY]\"\n}" + } + ] + }, + { + "name": "SetAORS", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"1005\",\n \"maxContacts\": 1\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/cezen/set_aors", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "set_aors" + ] + } + }, + "response": [ + { + "name": "SetAORS", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"1005\",\n \"maxContacts\": 1\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/cezen/set_aors", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "set_aors" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Method" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Headers" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-XSS-Protection", + "value": "0" + }, + { + "key": "Cache-Control", + "value": "no-cache, no-store, max-age=0, must-revalidate" + }, + { + "key": "Pragma", + "value": "no-cache" + }, + { + "key": "Expires", + "value": "0" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Date", + "value": "Thu, 15 May 2025 10:22:12 GMT" + }, + { + "key": "Keep-Alive", + "value": "timeout=60" + }, + { + "key": "Connection", + "value": "keep-alive" + } + ], + "cookie": [], + "body": "{\n \"status\": true,\n \"message\": \"1005 Persisted \",\n \"exceptionMessage\": \"\"\n}" + } + ] + }, + { + "name": "DeleteExtension", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "http://localhost:8081/cezen/delete_extension?sipNumber=testEndPoint", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "delete_extension" + ], + "query": [ + { + "key": "sipNumber", + "value": "testEndPoint" + } + ] + } + }, + "response": [ + { + "name": "DeleteExtension", + "originalRequest": { + "method": "DELETE", + "header": [], + "url": { + "raw": "http://localhost:8081/cezen/delete_extension?sipNumber=1005", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "delete_extension" + ], + "query": [ + { + "key": "sipNumber", + "value": "1005" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Method" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Headers" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-XSS-Protection", + "value": "0" + }, + { + "key": "Cache-Control", + "value": "no-cache, no-store, max-age=0, must-revalidate" + }, + { + "key": "Pragma", + "value": "no-cache" + }, + { + "key": "Expires", + "value": "0" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Date", + "value": "Thu, 15 May 2025 10:22:35 GMT" + }, + { + "key": "Keep-Alive", + "value": "timeout=60" + }, + { + "key": "Connection", + "value": "keep-alive" + } + ], + "cookie": [], + "body": "true" + } + ] + }, + { + "name": "Add_a_global_extension_feature", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"context\": \"default\",\n \"extension\": \"w\",\n \"priority\": 5,\n \"app\": \"Dial\",\n \"appdata\": \"W conf\"\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/cezen/add_feature", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "add_feature" + ] + } + }, + "response": [ + { + "name": "Add_a_global_extension_feature", + "originalRequest": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"context\": \"default\",\n \"extension\": \"w\",\n \"priority\": 5,\n \"app\": \"Dial\",\n \"appdata\": \"W conf\"\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/cezen/add_feature", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "cezen", + "add_feature" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Method" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Headers" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-XSS-Protection", + "value": "0" + }, + { + "key": "Cache-Control", + "value": "no-cache, no-store, max-age=0, must-revalidate" + }, + { + "key": "Pragma", + "value": "no-cache" + }, + { + "key": "Expires", + "value": "0" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Date", + "value": "Thu, 15 May 2025 10:53:48 GMT" + }, + { + "key": "Keep-Alive", + "value": "timeout=60" + }, + { + "key": "Connection", + "value": "keep-alive" + } + ], + "cookie": [], + "body": "{\n \"status\": false,\n \"message\": \"w configured as default Already exists\",\n \"exceptionMessage\": \"jakarta.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call\"\n}" + } + ] + }, + { + "name": "signup", + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"userName\": \"Mathew Francis\",\n \"email\":\"asda@gmail.com\",\n \"password\": \"1234567890\",\n \"confirmPassword\": \"1234567890\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8081/open/signup", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "open", + "signup" + ] + } + }, + "response": [] + }, + { + "name": "login", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "1234", + "type": "string" + }, + { + "key": "username", + "value": "mathew", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "http://localhost:8081/open/login", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "open", + "login" + ] + } + }, + "response": [ + { + "name": "login", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "http://localhost:8081/open/login", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "open", + "login" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Method" + }, + { + "key": "Vary", + "value": "Access-Control-Request-Headers" + }, + { + "key": "Set-Cookie", + "value": "Authorization=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNYXRoZXcgRnJhbmNpcyIsInN1YiI6IkpXVF9Ub2tlbiIsInVzZXJuYW1lIjoiY29tLmV4YW1wbGUuY2V6ZW5QQlguZW50aXR5LnVzZXIuVXNlckVudGl0eUAzMGI5ZjFlOCIsImF1dGhvcml0aWVzIjoiUk9MRV9hZG1pbiIsImlhdCI6MTc0NzI5MDAyMCwiZXhwIjoxNzQ3MzIwMDIwfQ.kjyArki3Cbc90Jjf68pl5iPeg61GWaxb6yT6ivTNXes; Path=/; Secure; HttpOnly" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-XSS-Protection", + "value": "0" + }, + { + "key": "Cache-Control", + "value": "no-cache, no-store, max-age=0, must-revalidate" + }, + { + "key": "Pragma", + "value": "no-cache" + }, + { + "key": "Expires", + "value": "0" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Date", + "value": "Thu, 15 May 2025 06:20:20 GMT" + }, + { + "key": "Keep-Alive", + "value": "timeout=60" + }, + { + "key": "Connection", + "value": "keep-alive" + } + ], + "cookie": [], + "body": "{\n \"status\": false,\n \"message\": \"Login not yet implemented\",\n \"exceptionMessage\": \"Login not yet implemented\"\n}" + } + ] + }, + { + "name": "logout", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "http://localhost:8081/logout", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8081", + "path": [ + "logout" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/MySQL_conf_pbx/test1/db_asterisk/security.sql b/MySQL_conf_pbx/test1/db_asterisk/security.sql new file mode 100644 index 0000000..951c79c --- /dev/null +++ b/MySQL_conf_pbx/test1/db_asterisk/security.sql @@ -0,0 +1,102 @@ +USE asterisk_db; + +SHOW DATABASES; + +SHOW TABLES; + + +SELECT * FROM `ps_endpoints`; + +DELETE FROM `ps_endpoints` WHERE `id` = '1004'; + +SELECT * FROM `extensions_table`; +DESCRIBE `extensions_table`; + +ALTER TABLE `extensions_table` +ADD CONSTRAINT `extension_table_unique_val_two_check` UNIQUE (`exten`, `appdata`); + + +ALTER TABLE `extensions_table` +DROP INDEX `extension_table_unique_val`; + + +ALTER TABLE `extensions_table` +ADD CONSTRAINT `extension_table_unique_val` UNIQUE (`exten`, `appdata`, `priority`); + +DELETE FROM `extensions_table` WHERE priority = 4 and exten = "1005" ; +DELETE FROM `extensions_table` WHERE exten = "1005" OR exten = "1004"; + +DELETE FROM `ps_endpoints` WHERE id = "1004" OR id = "1005"; +DELETE FROM `extensions_table` WHERE exten = "1004" OR exten = "1005"; + +DELETE FROM `ps_auths` WHERE id = "1004" OR id = "1005"; +DELETE FROM `ps_aors` WHERE id = "1004" OR id = "1005"; + +SELECT * FROM `extensions_table` WHERE app = "Dial"; +SELECT * FROM extensions_table WHERE context = 'default' AND exten = '1005'; +SELECT * FROM extensions_table WHERE exten = '1004' AND context = 'default'; + + + +SELECT * FROM `ps_endpoints`; +SELECT * FROM `extensions_table`; +-- +SELECT * FROM ps_auths; +SELECT * FROM ps_aors; +DESCRIBE `ps_auths`; +DESCRIBE `ps_aors`; +DESCRIBE `extensions_table`; + +INSERT INTO `ps_aors`(`id`,`max_contacts`) VALUES ("1004", 1); +INSERT INTO `ps_auths`(`id`, `auth_type`, `username`, `password`, `md5_cred`, `realm`) VALUES("1004", "userpass", "1004", "12345", null, null); + + + + +-- USER ROLES ROLE GOES HERE +CREATE TABLE `roles`( +`role_id` INTEGER NOT NULL AUTO_INCREMENT, +`role_name` VARCHAR(20) UNIQUE NOT NULL, +CONSTRAINT `roles_pk` PRIMARY KEY (`role_id`) +)ENGINE = 'Innodb' AUTO_INCREMENT = 1, DEFAULT CHARSET 'latin1'; + +DESCRIBE `roles`; + + +CREATE TABLE `user_roles`( +`u_id` INTEGER NOT NULL, +`role_id` INTEGER NOT NULL, +CONSTRAINT `user_roles_pk` PRIMARY KEY(`u_id`,`role_id`) +)ENGINE = 'Innodb' AUTO_INCREMENT = 1, DEFAULT CHARSET 'latin1'; + + +-- foreign key to be added to this table in alter table form +CREATE TABLE `user`( +`u_id` INTEGER NOT NULL AUTO_INCREMENT, +`user_name` VARCHAR(70) UNIQUE NOT NULL, +`password` VARCHAR(68) NOT NULL, +-- fk to uder_account +CONSTRAINT `user_table_pk` PRIMARY KEY(`u_id`) +)ENGINE = 'Innodb', AUTO_INCREMENT = 1, DEFAULT CHARSET 'latin1'; + +ALTER TABLE `user` ADD COLUMN `user_email_id` VARCHAR(50) UNIQUE NOT NULL; + + +ALTER TABLE `user_roles` ADD CONSTRAINT `user_lones_U_fk_to_user` FOREIGN KEY(`u_id`) REFERENCES `user`(`u_id`); +ALTER TABLE `user_roles` ADD CONSTRAINT `user_lones_R_fk_to_user` FOREIGN KEY(`role_id`) REFERENCES `roles`(`role_id`); +DESC `user_roles`; + + +INSERT `roles`(`role_name`) VALUES ('ROLE_admin'); + +SELECT * FROM `user`; +SELECT * FROM `user_roles`; +SELECT * FROM `roles`; + +DELETE FROM `user` WHERE `user_name` = 'Mathew Francis'; +DELETE FROM `user_roles` WHERE `u_id` = (SELECT `u_id` FROM `user_roles` LIMIT 1); +DELETE FROM `roles` WHERE `roles`.`role_name` = 'ROLE_admin' + + + + diff --git a/MySQL_conf_pbx/test1/mariadb_data/aria_log.00000001 b/MySQL_conf_pbx/test1/mariadb_data/aria_log.00000001 index 8b58be4..ed13395 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/aria_log.00000001 and b/MySQL_conf_pbx/test1/mariadb_data/aria_log.00000001 differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/aria_log_control b/MySQL_conf_pbx/test1/mariadb_data/aria_log_control index e0a15ce..1a9d87a 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/aria_log_control and b/MySQL_conf_pbx/test1/mariadb_data/aria_log_control differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/extensions_table.ibd b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/extensions_table.ibd index 929a38f..dda4fe9 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/extensions_table.ibd and b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/extensions_table.ibd differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_aors.ibd b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_aors.ibd index c053994..dd45690 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_aors.ibd and b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_aors.ibd differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_auths.ibd b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_auths.ibd index 1b36389..cad5e9c 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_auths.ibd and b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_auths.ibd differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_endpoints.ibd b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_endpoints.ibd index 2680add..bd717e1 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_endpoints.ibd and b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/ps_endpoints.ibd differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/roles.frm b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/roles.frm new file mode 100644 index 0000000..9ee21f9 Binary files /dev/null and b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/roles.frm differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/roles.ibd b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/roles.ibd new file mode 100644 index 0000000..b92c3ff Binary files /dev/null and b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/roles.ibd differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user.frm b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user.frm new file mode 100644 index 0000000..43809b8 Binary files /dev/null and b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user.frm differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user.ibd b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user.ibd new file mode 100644 index 0000000..62121b3 Binary files /dev/null and b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user.ibd differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user_roles.frm b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user_roles.frm new file mode 100644 index 0000000..1a6a76c Binary files /dev/null and b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user_roles.frm differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user_roles.ibd b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user_roles.ibd new file mode 100644 index 0000000..2bfc8a1 Binary files /dev/null and b/MySQL_conf_pbx/test1/mariadb_data/asterisk_db/user_roles.ibd differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/ib_logfile0 b/MySQL_conf_pbx/test1/mariadb_data/ib_logfile0 index 13a022c..fc2e6b3 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/ib_logfile0 and b/MySQL_conf_pbx/test1/mariadb_data/ib_logfile0 differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/ibdata1 b/MySQL_conf_pbx/test1/mariadb_data/ibdata1 index 00f0192..b9cb7d2 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/ibdata1 and b/MySQL_conf_pbx/test1/mariadb_data/ibdata1 differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/mysql/innodb_index_stats.ibd b/MySQL_conf_pbx/test1/mariadb_data/mysql/innodb_index_stats.ibd index 1ded65a..21e9201 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/mysql/innodb_index_stats.ibd and b/MySQL_conf_pbx/test1/mariadb_data/mysql/innodb_index_stats.ibd differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/mysql/innodb_table_stats.ibd b/MySQL_conf_pbx/test1/mariadb_data/mysql/innodb_table_stats.ibd index 3380537..f6de378 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/mysql/innodb_table_stats.ibd and b/MySQL_conf_pbx/test1/mariadb_data/mysql/innodb_table_stats.ibd differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/undo001 b/MySQL_conf_pbx/test1/mariadb_data/undo001 index a665506..4007320 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/undo001 and b/MySQL_conf_pbx/test1/mariadb_data/undo001 differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/undo002 b/MySQL_conf_pbx/test1/mariadb_data/undo002 index f7bbda5..5230c87 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/undo002 and b/MySQL_conf_pbx/test1/mariadb_data/undo002 differ diff --git a/MySQL_conf_pbx/test1/mariadb_data/undo003 b/MySQL_conf_pbx/test1/mariadb_data/undo003 index 174979c..ee69e21 100644 Binary files a/MySQL_conf_pbx/test1/mariadb_data/undo003 and b/MySQL_conf_pbx/test1/mariadb_data/undo003 differ diff --git a/MySQL_conf_pbx/test1/springCezenPBX/pom.xml b/MySQL_conf_pbx/test1/springCezenPBX/pom.xml index 7489bb1..4b0f43d 100644 --- a/MySQL_conf_pbx/test1/springCezenPBX/pom.xml +++ b/MySQL_conf_pbx/test1/springCezenPBX/pom.xml @@ -70,6 +70,35 @@ org.springframework.boot spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-test + test + + + + + io.jsonwebtoken + jjwt-api + 0.12.3 + + + io.jsonwebtoken + jjwt-impl + 0.12.3 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.12.3 + runtime + diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DAO/BasicAsteriskOpsDAO.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DAO/BasicAsteriskOpsDAO.java index 34920d3..0f026b9 100644 --- a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DAO/BasicAsteriskOpsDAO.java +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DAO/BasicAsteriskOpsDAO.java @@ -8,6 +8,7 @@ import jakarta.persistence.TypedQuery; import jakarta.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import org.springframework.transaction.UnexpectedRollbackException; @Repository public class BasicAsteriskOpsDAO implements CezenPbxOpsDAO { @@ -114,21 +115,21 @@ public class BasicAsteriskOpsDAO implements CezenPbxOpsDAO { } @Override - @Transactional public ReturnStatus saveAnExtensionByCharacters(ExtensionsTable extensionsTable) { - - try{ - - this.entityManager.persist(extensionsTable); - + try { + this.doPersist(extensionsTable); // calls @Transactional method return new ReturnStatus(true, extensionsTable.getExtension() +" configured as "+ extensionsTable.getContext() +" added", ""); - - }catch (Exception e){ + } catch (Exception e) { return new ReturnStatus(false, extensionsTable.getExtension() +" configured as "+ extensionsTable.getContext() +" Already exists", e.toString()); } } + + @Transactional + public void doPersist(ExtensionsTable extensionsTable) { + entityManager.persist(extensionsTable); + } } diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DAO/UserOpsDAO.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DAO/UserOpsDAO.java new file mode 100644 index 0000000..f998341 --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DAO/UserOpsDAO.java @@ -0,0 +1,18 @@ +package com.example.cezenPBX.DAO; + +import com.example.cezenPBX.DTO.ReturnStatus; +import com.example.cezenPBX.entity.user.UserEntity; + +// TODO only one admin allowed ... once the admin creates an +// account they should not be able to make the account again +// admin login, logout and signup DAO operations +public interface UserOpsDAO { + + // check if user exists; + public boolean checkIfAdminExists(UserEntity userEntity) throws Exception; + + // admin login + public ReturnStatus adminSetUsernameAndPassword(UserEntity userEntity); + + public UserEntity getUserByUserName(String userName); +} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DAO/UserOpsDAOImpl.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DAO/UserOpsDAOImpl.java new file mode 100644 index 0000000..675f9f5 --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DAO/UserOpsDAOImpl.java @@ -0,0 +1,75 @@ +package com.example.cezenPBX.DAO; + +import com.example.cezenPBX.DTO.ReturnStatus; +import com.example.cezenPBX.entity.user.Role; +import com.example.cezenPBX.entity.user.UserEntity; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; +import jakarta.transaction.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +@Repository +public class UserOpsDAOImpl implements UserOpsDAO{ + + @Autowired + private EntityManager entityManager; + + @Override + public boolean checkIfAdminExists(UserEntity userEntity){ + // check if info exists in the database users table + // this enforces the fact that only one user can exist + Query query = entityManager.createQuery( + "SELECT COUNT(u) FROM UserEntity u JOIN u.roles r WHERE r.role = :roleName"); + query.setParameter("roleName", "ROLE_Admin"); + Long count = (Long) query.getSingleResult(); + return count > 0; + + } + + // get roles from the database + + // Admin sets a username and password for the first time + @Override + @Transactional + public ReturnStatus adminSetUsernameAndPassword(UserEntity userEntity) { + + try { + if (checkIfAdminExists(userEntity)) { + return new ReturnStatus(false, "Admin already exists", ""); + } + // Fetch existing ROLE_Admin from DB + TypedQuery query = entityManager.createQuery("FROM Role r WHERE r.role = :roleName", Role.class) + .setParameter("roleName", "ROLE_Admin"); + + Role role = query.getSingleResult(); + + userEntity.setARole(role); + // Persist the user + entityManager.persist(userEntity); + return new ReturnStatus(true, "Admin created", ""); + } catch (Exception e) { + return new ReturnStatus(false, "Admin creation failed", e.getMessage()); + } + } + + // get user details by username + // throws an exception if the user doesn't exist + // exception is caught and returns null ... custom authentication provider must catch the exception + @Override + public UserEntity getUserByUserName(String userName) { + + try{ + TypedQuery query = this.entityManager + .createQuery("SELECT u FROM UserEntity u JOIN FETCH u.roles AS r WHERE u.userName = :userName", UserEntity.class); + + query.setParameter("userName", userName); + + return query.getSingleResult(); + + }catch ( Exception e){ + return null; + } + } +} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DTO/user/AdminSetPasswordDTO.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DTO/user/AdminSetPasswordDTO.java new file mode 100644 index 0000000..a850c8f --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/DTO/user/AdminSetPasswordDTO.java @@ -0,0 +1,20 @@ +package com.example.cezenPBX.DTO.user; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public record AdminSetPasswordDTO( + @NotBlank(message = "Username cannot be blank") + String userName, + + @Email(message = "Email is not valid") + String email, + + @NotBlank(message = "Password cannot be blank") + @Size(min = 3, message = "Password must be at least 8 characters long") + String password, + + @NotBlank(message = "Confirm password cannot be blank") + String confirmPassword +) {} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/config/CezenLoginSecurityChain.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/config/CezenLoginSecurityChain.java new file mode 100644 index 0000000..fec13ba --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/config/CezenLoginSecurityChain.java @@ -0,0 +1,112 @@ +package com.example.cezenPBX.config; + + +import com.example.cezenPBX.security.JWTTokenGeneratorFilter; +import com.example.cezenPBX.security.JWTTokenValidatorFilter; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; +import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; + +import java.util.Collections; +import java.util.List; + +// this class will handel the routs that are protected and +// allow spring security to accept login details from our custom login page +@Configuration +public class CezenLoginSecurityChain { + + @Bean + SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { + + //the token is generated here + CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler(); + requestHandler.setCsrfRequestAttributeName("_csrf"); + + //CSRF cookie + final CookieCsrfTokenRepository cookieCsrfTokenRepo = new CookieCsrfTokenRepository(); + //make secure true when using only https + cookieCsrfTokenRepo.setCookieCustomizer(responseCookieBuilder -> responseCookieBuilder.secure(true)); + + // bellow line is used when you are using JWT tokens instead of jSession session keys but i put always because i guess CSRF token needs it + http. + logout((logout) -> logout.deleteCookies("Authorization", "JSESSIONID", "XSRF-TOKEN")) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + + //now because we aare sending the JWT token to The UI Application in a Header + //we need to manage it in the CORs config + .cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + //check CORs and CSRF in Previous commits + CorsConfiguration config = new CorsConfiguration(); +// config.setAllowedOrigins(Collections.singletonList("http://localhost:4200")); + config.setAllowedOrigins(Collections.singletonList("*")); + config.setAllowedMethods(Collections.singletonList("*")); + config.setAllowCredentials(true); + config.setAllowedHeaders(Collections.singletonList("*")); + //the JWT will be sent to UI under Authorization header and XSR under X-XSRF-TOKEN + config.setExposedHeaders(List.of("Authorization", "X-XSRF-TOKEN")); + config.setMaxAge(3600L); + return config; + } + })) + + + //temporarily disabling cross sight resource forgery + .csrf(AbstractHttpConfigurer::disable) +// .csrf((csrf) -> +// csrf.csrfTokenRequestHandler(requestHandler). +// ignoringRequestMatchers("/open/signup","/open/login","/user/getXSRfToken") +// //.csrfTokenRepository(new CookieCsrfTokenRepository()) +// .csrfTokenRepository(cookieCsrfTokenRepo) +// ) + //.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class) + + //token generation after BasicAuthenticationFilter.class + .addFilterAfter(new JWTTokenGeneratorFilter(), BasicAuthenticationFilter.class) + //then position the verification filter + .addFilterBefore(new JWTTokenValidatorFilter(), BasicAuthenticationFilter.class) + .authorizeHttpRequests((requests) -> requests + //only admin can use this rout + //user roles :- ROLE_admin ROLE_employee ROLE_manager ROLE_user + .requestMatchers( + "/cezen/add_user", + "/cezen/add_feature", + "/cezen/delete_extension", + "/cezen/set_aors", + "/cezen/set_password", + "/cezen/add_extension" + ).hasAnyRole("admin") + //any one who is authenticated can access /logout + .requestMatchers("/open/login", "/user/getXSRfToken", "/logout").authenticated() + //all the rest are open to public + .requestMatchers("/open/signup").permitAll() + //.requestMatchers(HttpMethod.POST, "/open/**").permitAll() + ) + // redirect to /login if the user is not authenticated Customizer.withDefaults() enables a security feature using the defaults provided by Spring Security + .formLogin(Customizer.withDefaults()) + .httpBasic(Customizer.withDefaults()); + + System.out.print("Security chain configured"); + return http.build(); + + } + + // to encode the password + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/config/CustomAuthenticationProviderForCezen.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/config/CustomAuthenticationProviderForCezen.java new file mode 100644 index 0000000..f7edc02 --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/config/CustomAuthenticationProviderForCezen.java @@ -0,0 +1,81 @@ +package com.example.cezenPBX.config; + +import com.example.cezenPBX.DAO.UserOpsDAO; +import com.example.cezenPBX.entity.user.UserEntity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class CustomAuthenticationProviderForCezen implements AuthenticationProvider { + + @Autowired + private UserOpsDAO userOpsDAO; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + + //get credentials from login form + String username = authentication.getName(); + String pwd = authentication.getCredentials().toString(); + + //sanity check + if (username.isEmpty() || pwd.isEmpty()) return null; + + //check for employee + UserEntity user = null; + try { + //check if employee exists if yes then fetch details + user = this.userOpsDAO.getUserByUserName(username); + } catch (Exception e) { + System.out.println(e.toString()); + return null; + } + + if (passwordEncoder.matches(pwd, user.getPassword())) { + + //then it is a match a number of springs granted authorities + List authorities = new ArrayList<>(); + + //loop through the users authorities and add each of them to simple granted authority + try { + //check if user is part of permission set for admin signing in + boolean isAdmin = false; + for(var permission : user.getRoles()){ + if(permission.getRole().equals("ROLE_admin")) isAdmin = true; + } + if(!isAdmin) throw new BadCredentialsException("no employee permission for given employee"); + + user.getRoles().forEach(a -> authorities.add(new SimpleGrantedAuthority(a.getRole()))); + } catch (Exception e) { + //use/**/r doesn't have permissions or roles = null + System.out.println(e.toString()); + return null; + } + + return new UsernamePasswordAuthenticationToken(user.getUserName(), pwd, authorities); + } else { + throw new BadCredentialsException("Invalid password!"); + } + } + + @Override + public boolean supports(Class authentication) { + //tells spring that i want to support username password style of auth + return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); + } + +} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/constents/SecurityConstants.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/constents/SecurityConstants.java new file mode 100644 index 0000000..f72c978 --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/constents/SecurityConstants.java @@ -0,0 +1,7 @@ +package com.example.cezenPBX.constents; + +public interface SecurityConstants { + + public static final String JWT_KEY = ";sdmn3426FHB426RH62389;]['/.sdwswa"; + public static final String JWT_HEADER = "Authorization"; +} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/controller/SignUpController.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/controller/SignUpController.java new file mode 100644 index 0000000..d982a11 --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/controller/SignUpController.java @@ -0,0 +1,38 @@ +package com.example.cezenPBX.controller; + +import com.example.cezenPBX.DTO.ReturnStatus; +import com.example.cezenPBX.DTO.user.AdminSetPasswordDTO; +import com.example.cezenPBX.service.PbxUserService; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/open") +public class SignUpController { + + @Autowired + private PbxUserService pbxUserService; + + //sign up route + @PostMapping("/signup") + public ReturnStatus signUp(@RequestBody @Valid AdminSetPasswordDTO adminSetPasswordDTO){ + + return this.pbxUserService.adminSetUserNamePasswordSet( + adminSetPasswordDTO.userName(), + adminSetPasswordDTO.password(), + adminSetPasswordDTO.confirmPassword(), + adminSetPasswordDTO.email() + ); + } + + // and a login route + @GetMapping("/login") + public ReturnStatus login(){ + return new ReturnStatus(true, "Welcome user authenticated successfully", ""); + } + + + + // forgot password +} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/entity/user/Role.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/entity/user/Role.java new file mode 100644 index 0000000..d79fbb6 --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/entity/user/Role.java @@ -0,0 +1,65 @@ +package com.example.cezenPBX.entity.user; + +import jakarta.persistence.*; + +import java.util.Collection; + +@Entity +@Table(name = "roles") +final public class Role { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "role_id") + private int id; + + //remember ROLE_ + @Column(name = "role_name") + private String role; + + //all employees under this role + // map by may be required + @ManyToMany( + fetch = FetchType.LAZY, + cascade = { + //The detach operation removes the entity from the persistent context. When we use CascadeType.DETACH, the child entity will also get removed from the persistent context. + CascadeType.DETACH, + CascadeType.MERGE, + CascadeType.PERSIST, + CascadeType.REFRESH, + } + //cascade = CascadeType.ALL + ) + @JoinTable( + name = "user_roles", + joinColumns = @JoinColumn(name = "role_id"), + inverseJoinColumns = @JoinColumn(name = "u_id") + ) + private Collection employees; + + public Role(){} + + public Role(String role) { + this.role = role; + } + + public int getId() { + return id; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public Collection getEmployees() { + return employees; + } + + public void setEmployees(Collection employees) { + this.employees = employees; + } +} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/entity/user/UserEntity.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/entity/user/UserEntity.java new file mode 100644 index 0000000..b4ac6e4 --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/entity/user/UserEntity.java @@ -0,0 +1,90 @@ +package com.example.cezenPBX.entity.user; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; + +import java.util.Collection; +import java.util.HashSet; + +@Entity +@Table(name = "user") +final public class UserEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "u_id") + private int id; + + @Column(name = "user_name") + private String userName; + + @JsonIgnore + @Column(name = "password") + private String password; + + @Column(name = "user_email_id") + private String email; + + //ROLE + @ManyToMany( + fetch = FetchType.LAZY, + cascade = { + //The detach operation removes the entity from the persistent context. When we use CascadeType.DETACH, the child entity will also get removed from the persistent context. + CascadeType.DETACH, + CascadeType.MERGE, + CascadeType.PERSIST, + CascadeType.REFRESH, + } + //cascade = CascadeType.ALL + ) + @JoinTable( + name = "user_roles", + joinColumns = @JoinColumn(name = "u_id"), + inverseJoinColumns = @JoinColumn(name = "role_id") + ) + private Collection roles; + + + public UserEntity(){} + + public UserEntity(String userName, String password, String email) { + this.userName = userName; + this.password = password; + this.email = email; + } + + public int getId() { + return id; + } + public String getUserName() { + return userName; + } + public void setUserName(String userName) { + this.userName = userName; + } + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } + public Collection getRoles() { + return roles; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public void setARole(Role role){ + if(this.roles == null){ + this.roles = new HashSet(); + } + + this.roles.add(role); + } +} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/security/JWTTokenGeneratorFilter.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/security/JWTTokenGeneratorFilter.java new file mode 100644 index 0000000..4af13bf --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/security/JWTTokenGeneratorFilter.java @@ -0,0 +1,81 @@ +package com.example.cezenPBX.security; + +import com.example.cezenPBX.constents.SecurityConstants; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.crypto.SecretKey; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +public class JWTTokenGeneratorFilter extends OncePerRequestFilter { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + + //at this point the user is authenticated we just have to send the token back + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (null != authentication) { + + //get the JWT key from the contents we defined + // Keys, Jwts class comes from pom.xml + SecretKey key = Keys.hmacShaKeyFor(SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8)); + + //creating a JWT token + // issuer issues a jwt token + //subject can be any value + String jwt = Jwts.builder().issuer("Mathew Francis").subject("JWT_Token") + //building the token + .claim("username", authentication.getName()) + .claim("authorities", populateAuthorities(authentication.getAuthorities())) + .issuedAt(new Date()) + .expiration(new Date((new Date()).getTime() + 30000000)) + //signing it with the key we set on line 35 + .signWith(key).compact(); + //SecurityConstants.JWT_HEADER, in the Constants SecurityConstants folder + //response.setHeader(SecurityConstants.JWT_HEADER, jwt); + //uncomment for cookie based saving + Cookie cookie = new Cookie(SecurityConstants.JWT_HEADER,jwt); + cookie.setHttpOnly(true); + cookie.setSecure(true); + cookie.setPath("/"); + response.addCookie(cookie); + System.out.println("JWT Generated"); + } + System.out.println("Intercepted"); + + System.out.println(response.getHeader("X-XSRF-TOKEN")); + filterChain.doFilter(request, response); + } + + //only generate if the path is login + //other words this method will return false for /login + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + + return !request.getServletPath().equals("/open/login"); + //return !(request.getServletPath().equals("/open/signup") || request.getServletPath().equals("/open/login")); + } + + // gets the authority's from granted authority which we set in the configuration CustomAuthenticationProvider class + // plug in user auth into jwt token + private String populateAuthorities(Collection collection) { + Set authoritiesSet = new HashSet<>(); + for (GrantedAuthority authority : collection) { + authoritiesSet.add(authority.getAuthority()); + } + return String.join(",", authoritiesSet); + } +} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/security/JWTTokenValidatorFilter.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/security/JWTTokenValidatorFilter.java new file mode 100644 index 0000000..9390541 --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/security/JWTTokenValidatorFilter.java @@ -0,0 +1,81 @@ +package com.example.cezenPBX.security; + +import com.example.cezenPBX.constents.SecurityConstants; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.crypto.SecretKey; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class JWTTokenValidatorFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + // SecurityConstants + // public static final String JWT_KEY = "jxgEQeXHuPq8VdbyYFNkANdudQ53YUn4"; + // public static final String JWT_HEADER = "Authorization"; + //String jwt = request.getHeader(SecurityConstants.JWT_HEADER); + //below is the COOKIE approach + String jwt = null; + for(var cookie : request.getCookies()){ + if(cookie.getName().equals("Authorization")){ + System.out.print("COOKIE"); + System.out.println(cookie.getValue()); + jwt = cookie.getValue(); + } + } + if (null != jwt) { + try { + //generating the key + SecretKey key = Keys.hmacShaKeyFor( + SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8)); + + //verification of legitimacy + Claims claims = Jwts.parser() + .verifyWith(key) + .build() + .parseSignedClaims(jwt) + .getPayload(); + String username = String.valueOf(claims.get("username")); + String authorities = (String) claims.get("authorities"); + +// System.out.println("JWT name : "+ username); +// System.out.println("JWT auth "+ authorities); + + //if successful the result will be stored in SecurityContextHolder + Authentication auth = new UsernamePasswordAuthenticationToken(username, null, + //this comes in a string of comas and values + AuthorityUtils.commaSeparatedStringToAuthorityList(authorities)); + SecurityContextHolder.getContext().setAuthentication(auth); + } catch (Exception e) { + throw new BadCredentialsException("Invalid Token received!"); + } + + } + filterChain.doFilter(request, response); + } + + //should be executed for all the api except the login api + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + + return request.getServletPath().equals("/open/signup") + || request.getServletPath().equals("/open/login"); +// //bellow was done to archive this /exposed/** +// request.getServletPath().split("/")[1].equals("exposed"); + } +} diff --git a/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/service/PbxUserService.java b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/service/PbxUserService.java new file mode 100644 index 0000000..7534b1d --- /dev/null +++ b/MySQL_conf_pbx/test1/springCezenPBX/src/main/java/com/example/cezenPBX/service/PbxUserService.java @@ -0,0 +1,35 @@ +package com.example.cezenPBX.service; + +import com.example.cezenPBX.DAO.UserOpsDAO; +import com.example.cezenPBX.DTO.ReturnStatus; +import com.example.cezenPBX.entity.user.UserEntity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class PbxUserService { + + @Autowired + private UserOpsDAO userOpsDAO; + + @Autowired + private PasswordEncoder passwordEncoder; + + + // must perform the sanity checks before being set to the database + // method will return a faulty return status if the damin exists + public ReturnStatus adminSetUserNamePasswordSet(String userName, String password, String confirmPassword, String email){ + // password will be checked here + if(!password.equals(confirmPassword)){ + return new ReturnStatus(false, "Passwords do not match", "Passwords do not match"); + } + // password encryption + UserEntity userEntity = new UserEntity(userName, passwordEncoder.encode(password), email); + + // commit the username and password to the database + return userOpsDAO.adminSetUsernameAndPassword(userEntity); + } +}