
Задание 3.
Доработка программного обеспечения – добавление аутентификации
Создайте две роли – «Ваши инициалы junior» и «Ваши инициалы admin». Junior обладает только правом чтения из базы данных, admin – полные права. Добавьте в программу поле ввода логина/пароля для подключения к базе данных от имени различных пользователей. Типы пользователей – администратор, пользователь.
-- Создание роли junior с правами на чтение данных
CREATE ROLE inicjunior LOGIN PASSWORD '123456';
GRANT CONNECT ON DATABASE students TO inicjunior;
GRANT USAGE ON SCHEMA public TO inicjunior;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO inicjunior;
-- Обновление прав на будущие таблицы в схеме public для роли junior
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO inicjunior;
-- Создание роли admin с полными правами
CREATE ROLE inicadmin LOGIN PASSWORD '123456';
GRANT ALL PRIVILEGES ON DATABASE students TO inicadmin;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO inicadmin;
-- Обновление прав на будущие таблицы для роли admin
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO inicadmin;
Здесь inicjunior обладает правами только на чтение данных, а inicadmin — полными правами на чтение, запись и изменение данных.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libpq-fe.h>
/* Функция выхода из программы с сообщением об ошибке. */
void err_exit(PGconn *conn) {
PQfinish(conn);
exit(1);
}
/* Функция печати результата запроса на экран */
void print_query(PGresult *res) {
int rows = PQntuples(res);
int cols = PQnfields(res);
for(int i=0; i<rows; i++) {
for(int j=0; j<cols; j++) {
printf("%s ", PQgetvalue(res, i, j));
}
printf("\n");
}
}
/* Функция подключения к базе данных с аутентификацией */
PGconn* connect_db(const char* user, const char* password) {
char conninfo[256];
snprintf(conninfo, sizeof(conninfo), "user=%s password=%s dbname=students", user, password);
PGconn *conn = PQconnectdb(conninfo);
if (PQstatus(conn) == CONNECTION_BAD) {
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
PQfinish(conn);
exit(1);
}
return conn;
}
/* Функция для поиска самого младшего студента в указанной группе */
void find_youngest_student_in_group(PGconn *conn, const char* group_number) {
const char *param_values[1] = { group_number };
PGresult *res = PQexecParams(
conn,
"SELECT last_name, first_name, birthday "
"FROM youngest_student_in_group "
"WHERE students_group_number = $1 "
"LIMIT 1;",
1,
NULL,
param_values,
NULL,
NULL,
0
);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, "No data retrieved\n");
PQclear(res);
err_exit(conn);
}
printf("Самый младший студент в группе %s:\n", group_number);
print_query(res);
PQclear(res);
}
int main() {
char username[50], password[50];
printf("Введите логин (inicadmin или inicjunior): ");
scanf("%49s", username);
printf("Введите пароль: ");
scanf("%49s", password);
PGconn *conn = connect_db(username, password);
char group_number[50];
printf("Введите номер группы (например, ИВТ-43): ");
scanf("%49s", group_number);
find_youngest_student_in_group(conn, group_number);
PQfinish(conn);
return 0;
}
Программа запрашивает у пользователя логин и пароль и использует их для подключения к базе данных.
В зависимости от роли (inicjunior или inicadmin) будут предоставлены различные уровни доступа.
inicjunior сможет только просматривать данные.
inicadmin имеет полный доступ, включая возможности модификации.
Далее программа запрашивает номер группы, после чего выполняет запрос к представлению для вывода информации о самом младшем студенте из указанной группы.
Доработка программного обеспечения – добавление CRUD операций
От имени администратора сделайте возможность добавлять, изменять и удалять значения оценок у любого студента.
От имени пользователя сделайте возможность выполнять запрос на просмотр всех его оценок и поиск конкретной строки по введенным значениям фамилии, имени и номера группы. Используя разработанную функцию, попробуйте выполнить SQL инъекцию на изменение значение оценки от имени данного пользователя.
GRANT SELECT ON TABLE field_comprehensions TO inicjunior;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libpq-fe.h>
// Функция выхода из программы с сообщением об ошибке
void err_exit(PGconn *conn) {
PQfinish(conn);
exit(1);
}
// Функция печати результата запроса на экран
void print_query(PGresult *res) {
int rows = PQntuples(res);
int cols = PQnfields(res);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%s ", PQgetvalue(res, i, j));
}
printf("\n");
}
}
// Функция подключения к базе данных с аутентификацией
PGconn* connect_db(const char* user, const char* password) {
char conninfo[256];
snprintf(conninfo, sizeof(conninfo), "user=%s password=%s dbname=postgres", user, password);
PGconn *conn = PQconnectdb(conninfo);
if (PQstatus(conn) == CONNECTION_BAD) {
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn));
PQfinish(conn);
exit(1);
}
return conn;
}
// Функция для получения field_id по названию дисциплины
char* get_field_id(PGconn *conn, const char* field_name) {
const char *param_values[1] = { field_name };
PGresult *res = PQexecParams(
conn,
"SELECT field_id FROM fields WHERE field_name = $1;",
1,
NULL,
param_values,
NULL,
NULL,
0
);
if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) == 0) {
fprintf(stderr, "Field not found: %s\n", PQerrorMessage(conn));
PQclear(res);
return NULL; // Ошибка получения field_id
}
char *field_id = strdup(PQgetvalue(res, 0, 0)); // Используем strdup для копирования строки
PQclear(res);
return field_id; // Возвращаем строку
}
// Функция для добавления оценки
void add_grade(PGconn *conn, int student_id, const char* field_id, int mark) {
char student_id_str[10];
char mark_str[3];
sprintf(student_id_str, "%d", student_id);
sprintf(mark_str, "%d", mark);
const char *param_values[3] = { student_id_str, field_id, mark_str }; // field_id передаётся как строка
PGresult *res = PQexecParams(
conn,
"INSERT INTO field_comprehensions (student_id, field, mark) VALUES ($1, $2, $3);", // Изменено на 'field'
3,
NULL,
param_values,
NULL,
NULL,
0
);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "Failed to add grade: %s\n", PQerrorMessage(conn));
PQclear(res);
err_exit(conn);
}
printf("Grade added successfully.\n");
PQclear(res);
}
// Функция для обновления оценки
void update_grade(PGconn *conn, int student_id, const char* field_name, int new_mark) {
char* field_id = get_field_id(conn, field_name);
if (field_id == NULL) {
fprintf(stderr, "Не удалось получить ID дисциплины.\n");
return; // Прерываем выполнение, если ID не получен
}
// Преобразование student_id и new_mark в строковые форматы
char student_id_str[10];
sprintf(student_id_str, "%d", student_id);
char new_mark_str[10];
sprintf(new_mark_str, "%d", new_mark);
const char *param_values[3] = { student_id_str, field_id, new_mark_str };
PGresult *res = PQexecParams(
conn,
"UPDATE field_comprehensions SET mark = $3 WHERE student_id = $1 AND field = $2;", // Изменено на 'field'
3,
NULL,
param_values,
NULL,
NULL,
0
);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "Failed to update grade: %s\n", PQerrorMessage(conn));
PQclear(res);
err_exit(conn);
}
printf("Grade updated successfully.\n");
PQclear(res);
free(field_id);
}
// Функция для удаления оценки
void delete_grade(PGconn *conn, int student_id, const char* field_id) {
char student_id_str[10];
sprintf(student_id_str, "%d", student_id);
const char *param_values[2] = { student_id_str, field_id };
PGresult *res = PQexecParams(
conn,
"DELETE FROM field_comprehensions WHERE student_id = $1 AND field = $2;", // Изменено на 'field'
2,
NULL,
param_values,
NULL,
NULL,
0
);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "Failed to delete grade: %s\n", PQerrorMessage(conn));
PQclear(res);
err_exit(conn);
}
printf("Grade deleted successfully.\n");
PQclear(res);
}
// Функция для просмотра всех оценок студента
void view_grades(PGconn *conn, int student_id) {
char query[256];
snprintf(query, sizeof(query),
"SELECT f.field_name, fc.mark FROM field_comprehensions fc "
"JOIN fields f ON fc.field = f.field_id "
"WHERE fc.student_id = %d;", student_id);
PGresult *res = PQexec(conn, query);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, "Error executing query in view_grades: %s\n", PQerrorMessage(conn));
PQclear(res);
err_exit(conn);
}
printf("All grades for student %d:\n", student_id);
print_query(res);
PQclear(res);
}
// Функция для поиска оценок по имени, фамилии и номеру группы
void search_grades(PGconn *conn, const char* last_name, const char* first_name, const char* students_group_number) {
const char *param_values[3] = { last_name, first_name, students_group_number };
PGresult *res = PQexecParams(
conn,
"SELECT f.field_name, fc.mark FROM field_comprehensions fc "
"JOIN students s ON fc.student_id = s.student_id "
"JOIN fields f ON fc.field = f.field_id "
"WHERE s.last_name = $1 AND s.first_name= $2 AND s.students_group_number = $3;",
3,
NULL,
param_values,
NULL,
NULL,
0
);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, "Error executing query in search_grades: %s\n", PQerrorMessage(conn));
PQclear(res);
err_exit(conn);
}
printf("Search results:\n");
print_query(res);
PQclear(res);
}
int main() {
char username[50], password[50];
printf("Введите логин (inicadmin или inicjunior): ");
scanf("%49s", username);
printf("Введите пароль: ");
scanf("%49s", password);
PGconn *conn = connect_db(username, password);
if (strcmp(username, "inicadmin") == 0) {
int option;
printf("Admin Menu: 1-Add grade, 2-Update grade, 3-Delete grade\n");
scanf("%d", &option);
if (option == 1) {
int student_id, mark;
char field_name[50];
printf("Введите ID студента (6 цифр): ");
scanf("%d", &student_id);
if (student_id < 100000 || student_id > 999999) {
fprintf(stderr, "ID студента должно состоять из 6 цифр.\n");
return 1;
}
printf("Введите название дисциплины: ");
scanf(" %[^\n]", field_name); // Считываем строку, включая пробелы
printf("Введите оценку (от 1 до 5): ");
scanf("%d", &mark);
char* field_id = get_field_id(conn, field_name);
if (field_id == NULL) {
fprintf(stderr, "Не удалось получить ID дисциплины.\n");
return 1;
}
add_grade(conn, student_id, field_id, mark);
free(field_id); // Освобождаем выделенную память после использования
} else if (option == 2) {
int student_id, new_mark;
char field_name[50];
printf("Введите ID студента (6 цифр): ");
scanf("%d", &student_id);
if (student_id < 100000 || student_id > 999999) {
fprintf(stderr, "ID студента должно состоять из 6 цифр.\n");
return 1;
}
printf("Введите название дисциплины: ");
scanf(" %[^\n]", field_name); // Считываем строку, включая пробелы
printf("Введите новую оценку (от 1 до 5): ");
scanf("%d", &new_mark);
update_grade(conn, student_id, field_name, new_mark);
} else if (option == 3) {
int student_id;
char field_name[50];
printf("Введите ID студента (6 цифр): ");
scanf("%d", &student_id);
if (student_id < 100000 || student_id > 999999) {
fprintf(stderr, "ID студента должно состоять из 6 цифр.\n");
return 1;
}
printf("Введите название дисциплины: ");
scanf(" %[^\n]", field_name); // Считываем строку, включая пробелы
char* field_id = get_field_id(conn, field_name);
if (field_id == NULL) {
fprintf(stderr, "Не удалось получить ID дисциплины.\n");
return 1;
}
delete_grade(conn, student_id, field_id);
free(field_id); // Освобождаем выделенную память после использования
}
} else if (strcmp(username, "inicjunior") == 0) {
int student_id;
printf("Введите ID студента (6 цифр): ");
scanf("%d", &student_id);
if (student_id < 100000 || student_id > 999999) {
fprintf(stderr, "ID студента должно состоять из 6 цифр.\n");
return 1;
}
view_grades(conn, student_id);
char surname[50], name[50], group[50];
printf("Введите фамилию: ");
scanf("%49s", surname);
printf("Введите имя: ");
scanf("%49s", name);
printf("Введите номер группы: ");
scanf("%49s", group);
search_grades(conn, surname, name, group);
}
PQfinish(conn);
return 0;
}
SELECT f.mark
FROM fields f
JOIN field_comprehensions fc ON f.field_id = fc.field
WHERE fc.student_id = 812850 AND f.field_name = 'Философия';