diff --git a/2021-oop1/ku/stream-05-27-2021/secure_classes/Account.hpp b/2021-oop1/ku/stream-05-27-2021/secure_classes/Account.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7c59c93fdd2eb0a12c87eb82eea0bbb2699c18bb --- /dev/null +++ b/2021-oop1/ku/stream-05-27-2021/secure_classes/Account.hpp @@ -0,0 +1,24 @@ +#ifndef ACCOUNT_HPP +#define ACCOUNT_HPP + +class Account +{ + private: + int euros_; + + public: + Account() : euros_(0) {} + Account(const Account& copy) : euros_(copy.euros_) {} + Account& operator=(const Account& rhs) + { + if (this != &rhs) + euros_ = rhs.euros_; + return *this; + } + ~Account() = default; + + int getEuros() const { return euros_; } + void changeEurosBy(int difference) { euros_ += difference; } +}; + +#endif // ACCOUNT_HPP diff --git a/2021-oop1/ku/stream-05-27-2021/secure_classes/Bank.hpp b/2021-oop1/ku/stream-05-27-2021/secure_classes/Bank.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b874c87029ce8836824ab81934586ee50672cefe --- /dev/null +++ b/2021-oop1/ku/stream-05-27-2021/secure_classes/Bank.hpp @@ -0,0 +1,67 @@ +#ifndef BANK_HPP +#define BANK_HPP + +#include "Account.hpp" +#include <stdexcept> +#include <iostream> + +class Bank +{ + private: + Account* account_; + int pin_code_; + + public: + // (*) References / Pointers as a parameters stay valid outside + // No copy -> attributes of classes can be modified from outside + Bank(Account* account, int pin_code) : account_(new Account(*account)), pin_code_(pin_code) + { + // Checks in public methods that are called with parameters + if (pin_code_ < 0x1000 || pin_code_ >= 0x10000) + throw std::runtime_error("Der Pin-Code ist zu kurz / zu lang!"); + } + Bank(const Bank&) = delete; + Bank& operator=(const Bank&) = delete; + ~Bank() { delete account_; } + + bool verifyPinCode() const + { + // Very bad using just std::cin + int pin_code; + std::cout << "Enter your pin: "; + std::cin.unsetf(std::ios::dec); + std::cin.unsetf(std::ios::hex); + std::cin.unsetf(std::ios::oct); + std::cin >> pin_code; + return pin_code == pin_code_; + } + + // (#) Methods that return references / pointers may screw up + // the concept of encapsulation + Account const* getAccount() + { + if (!verifyPinCode()) + throw std::runtime_error("Der Pin-Code ist nicht gültig!"); + + return account_; + } + + // These two methods are safe, as they do not return a reference / pointer + int getAccountEuros() const + { + if (!verifyPinCode()) + throw std::runtime_error("Der Pin-Code ist nicht gültig!"); + + return account_->getEuros(); + } + + void changeAccountEurosBy(int euros) + { + if (!verifyPinCode()) + throw std::runtime_error("Der Pin-Code ist nicht gültig!"); + + account_->changeEurosBy(euros); + } +}; + +#endif // BANK_HPP diff --git a/2021-oop1/ku/stream-05-27-2021/secure_classes/main.cpp b/2021-oop1/ku/stream-05-27-2021/secure_classes/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f612a2b23c2a6a823247bbb2953beda5f80180c5 --- /dev/null +++ b/2021-oop1/ku/stream-05-27-2021/secure_classes/main.cpp @@ -0,0 +1,91 @@ +#include "Bank.hpp" +#include "Account.hpp" + +int main(void) +{ + int account_state; + + //------------------------------------------------------------------------------------------------ + std::cout << "\nÜberprüfungen in Settern / Konstruktoren!" << std::endl; + + Account* my_account = new Account; + + // Bank my_bank(my_account, 0x1337abc); // not valid after check in constructor + Bank my_bank(my_account, 0x1337); // this is okay + + account_state = my_bank.getAccountEuros(); // pin-code check + std::cout << "Kontostand: " << account_state << std::endl; + + + //------------------------------------------------------------------------------------------------ + std::cout << "\nPointer als Übergabeparameter!" << std::endl; + + my_account->changeEurosBy(-100); // account from parameters, no pin-code check (* in Bank.hpp) + + account_state = my_account->getEuros(); // no pin-code check + std::cout << "falscher Kontostand: " << account_state << std::endl; + + account_state = my_bank.getAccountEuros(); // pin-code check + std::cout << "richtiger Kontostand: " << account_state << std::endl; + + + //------------------------------------------------------------------------------------------------ + std::cout << "\nPointer als Rückgabewerte!" << std::endl; + + Account const* my_second_account = my_bank.getAccount(); // getter of account, (# in Bank.hpp) + // my_second_account->changeEurosBy(-500); // invalid, if pointer is const; no pin-code check + + Account* hacked_account = const_cast<Account*>(my_second_account); // don't use const_cast + hacked_account->changeEurosBy(-500); // now valid and no pin-code check + + account_state = my_second_account->getEuros(); // no pin-code check + std::cout << "falscher Kontostand: " << account_state << std::endl; + + account_state = my_bank.getAccountEuros(); // pin-code check + std::cout << "richtiger Kontostand: " << account_state << std::endl; + + // Solution: Don't return a reference / pointer, rather try to provide a method + // to handle everything inside of the class. If the reference / pointer + // has to be returned anyway, make it at least const. + + + //------------------------------------------------------------------------------------------------ + std::cout << "\nReinterpret Cast!" << std::endl; + std::cout << "Größe der Klasse: " << sizeof(my_bank) << std::endl; + + uint8_t* array = reinterpret_cast<uint8_t*>(&my_bank); // interpret the class as a byte-array + for (size_t i = 0; i < sizeof(my_bank); i += 4) + { + std::cout.width(2); + std::cout << std::hex << static_cast<int>(array[i]) << " "; + std::cout.width(2); + std::cout << std::hex << static_cast<int>(array[i + 1]) << " "; + std::cout.width(2); + std::cout << std::hex << static_cast<int>(array[i + 2]) << " "; + std::cout.width(2); + std::cout << std::hex << static_cast<int>(array[i + 3]) << std::endl; + } + std::cout << std::endl; + + array[8] = 0x38; // change a byte of the class (no encapsulation) + + for (size_t i = 0; i < sizeof(my_bank); i += 4) + { + std::cout.width(2); + std::cout << std::hex << static_cast<int>(array[i]) << " "; + std::cout.width(2); + std::cout << std::hex << static_cast<int>(array[i + 1]) << " "; + std::cout.width(2); + std::cout << std::hex << static_cast<int>(array[i + 2]) << " "; + std::cout.width(2); + std::cout << std::hex << static_cast<int>(array[i + 3]) << std::endl; + } + std::cout << std::endl; + + account_state = my_bank.getAccountEuros(); + std::cout << "richtiger Kontostand: " << std::dec << account_state << std::endl << std::endl; + + // Don't do this in the practicals ever! :D + + return 0; +}