Pages

Thursday, August 17, 2017

Certified Information Systems Security Professional (CISSP)

Applying cryptography
encryption for confidentiality
hashed for integrity
digitally signed certificates for authentication
digital signatures can be used to ensure non-repudiation

(Nonrepudiation is the assurance that someone cannot deny something.)

cryptography : Science of secret writing
Authentication: Proves idenitiy
nonrepudiation: Proves sender
Cipher: cryptographic set of rules or technique
Ciphertext: transformed plaintext to unreadable form
Algorithm: Complex mathematical cipher
Key: crypto variable used with an algorithm
Keyspace: Number of possible key combinations

Encryption types:
Substitution : Replacing characters or bits
Transposition : Moving characters or bits
key stream: One bit at a time XOR function
Confusion: multiple rounds of substitution
Diffusion : multiple rounds of transposition
3 DES :  uses data encryption algorithm and 48 rounds
AES : used Rijndael and 3 key size options
DES: 16 round block symmetric algorithm

Keys:
symmetric : single shared key
asymmetric: key pair
session key : specific instance of symmetric key
ECC, RSA,EIGamel :asymmetric algorithm
AES,3DES,IDEA : symmetric key standard and algorithms

PKI:
Registration authority: manages digital certificates
Certificate authority: Issues and revoke digital certificates
Digital certificate: Identifier with embedded public key
X.509: ISO PKI standard framework
Public key: key that is distributed
Private key: key that is kept secure
CRL: certificate revocation list
OCSP: certificate revocation protocol

Hashing: Producing a one-way representation
Message digest: output of a hash function
SHA-x : NSA developed hash algorithm
HMAC: hash output with a symmetric key
Digital SIgnature: Digest encrypt its private key
DSA: Digital signature algorithm
Nonrepudiation: Proves sender
Salt: A rehash value added to a database

Link encryption: Payload + header encryption
End-to-end encryption: payload only encryption
IPsec: de facto vpn protocol
SSL/TLS: application layer cryptographic protocol
HTTPS: hyper text transfer protocol + SSL/TLS
SSH: used for secure remote authentication
SFTP : used for secure file transfer
S/MIME: use for secure email

Side channel attack: Measure execution time or power
brute force attack: Exhausting all possibilities
frequency analysis: looking for patterns
collision: Different input results in same hash values
birthday attack: Exploits probability to produce collisions
MiTM : Interception and modification
Replay: Capture and re-transmit
Crypto-retirement : DES (End of life)

SSL/TLS
SSL/TLS protocol is a set of rules governing client and server authentication and encrypted communications.
SSL/TLS requires the server to have a SSL digital certificate:
  • The server authenticates itself to the client and provides its public keys (used to encrypt the session key)
  • The client and server uses symmetric-key encryption to encryption the information exchanged in the session.
  • The server may be optionally configured to require client-side authentication before an encrypted session can be established.
Secure Shell
Secure Shell (SSH) is a cross-platform cryptographic protocol that establishes a secure connection between an SSH server and an SSH client supporting asymmetric authentication, message authentication code and symmetric encryption.
SSH is used to administer systems remotely, provide a command shell on a remote network or tunnel other protocols.
  • ssh is a replacement for cleartext telnet, login, rsh, and rsync
  • SFTP is a file transfer protocol that uses SSH to transfer files.

Internet Protocol Security (IPsec):
Ipsec is a suite of protocols that used cryptographic security services to protect communications over internal protocol (IP) networks.
  • Ipsec supports:
  • network-level peer authentication
  • data origin authentication
  • data integrity
  • data confidentiality(encryption)
  • replay protection
Eavesdropping : violation of confidentiality
Tampering: violation of integrity
Spoofing: violation of authenticity
SSL/TLS : Secure client-server communication
SSH : secure telnet replacement
S/MIME: Used to digitally sign email
Encryption: used to ensure confidentiality
Digital Signatures : MD encrypted with private key

NAT :
NAT is a stateful process used by the firewall to change the source IP address of outgoing packets.
NAT can be used to:
  • Anonymize(hide) internal address
  • Transform non routable ip address to routable addresses
  • Extend IPv4 address space
NAT mapping can be static, dynamic or PAT

Honeypots and Honeynets:
Honeypots are decoy(luring) servers or systems set up to gather information regarding attacks or intrusions.
  • Honeypots work by fooling attackers into believing it is a legitimate system.
  • Attackers attack the system without knowing that they are being observed covertly. 
  • Honeypots can be set up inside, outside, or in the DMZ of a firewall
Honeynets are the networks of Honeypots

DMZ: Semi-trusted network segment
Enclave network: Segment within a trusted network
Honeypot: research decoy
Firewall: Enforces security policies
IDS/IPS: detective and corrective control
NAT: used to translate internal ip address
False positive: normal identified as abnormal
Proxy: Acting on behalf of 

Securing multimedia collaboration:
IP telephony: Telecommunications services using IP
SIP: use MD5, TLS, and privacy extensions
RTP: requests redelivery of VOIP packets
Codec: conversion of audio/video to digital frames
H.323 : First widely adopted VOIP protocol
SIP redirect server : Facilities SIP device portability
SIP registrar server : facilities SIP user portability
CDN: High performance content distribution

Securing virtual private networks:
VPN technologies includes:
  • Point to point tunnelling protocol (PPTP)
  • Layer 2 tunneling protocol (L2TP)
  • Internet protocol security (IPsec)
  • Secure Socket layer (SSL)
Ipsec Modes:
Ipsec can be implemented in two modes:
  • Transport mode is used for end-to-end protection between client and server
    • The IP payload is encrypted
    • Transport is the default mode of Ipsec
  • Tunnel mode is used between server-server, server-gateway, or gateway-gateway (two direct endpoints)
    • The entire packet is encrypted
Ipsec components :
AH : Integrity, Origin Authentication, Replay Attack protection (HAMC)
ESP : Integrity, Origin Authentication, Replay Attack protection and Confidentiality (HMAC & Symmetric encryption)
IKE : Device authentication and establishing security association
SA : A negotiation that includes the algorithms that will be used (hashing and encryption), key length, and key information
SPI : Security association identifier

Ipsec uses AH and ESP

Ipsec Key Exchange:
Phase 1 : device authentication
Phase 2 : establish secure tunnel

Ipsec security Parameter Index (SPI)
  • Security associations are identified by a security parameter index (SPI)
  • Two separate SAs are established for each direction of data communication
Ipsec Security Filters
Ipsec filters can be used to filter (allow, restrict, and secure) traffic by source IP, destination IP, protocol, source port and destination port

SSL VPN
ssl vpn communicates at the OSI transport and session layer
  • A user connects to an SSL gateway or endpoint using a web browser. SSL/TLS capabilities are embedded in most of web browsers.
  • The traffic is encrypted with SSL/TLS
  • An SSL VPN portal is a single connection to multiple services.
  • The user is authenticated by a SSL VPN gateway
  • The user is presented a web page
  • The SSL VPN tunnel is used to access non web-based applications
VPN Comparisons:
PPTP : used when a PPP connection needs to be transmitted through a IP network
L2TP : used when a PPP connection needs to be transmitted through a non-IP network
IPSec: used for IP based traffic
HAIPE : used for high-security IPSec implementations
SSL VPN : mainly used for http traffic

PPTP: used to secure PPP on a IP network
L2TP: can be used on a non-IP network
Transport mode: Payload is encrypted
Tunnel mode: entire packet is encrypted
AH: Integrity and authentication only
ESP : integrity, authentication and confidentiality
Security Association : IPSec negotiated agreement
SSL VPN: uses client side browser
Securing Endpoints
NAC (network access control): Unified endpoint security enforcement
Proxy: Acts on behalf of endpoint
MDM(Mobile device management) : Usually includes a remote wipe feature
Malware : Code or script with malicious intent
HIDS(Host IDS): Monitors and analyses local host behaviour

Preventing and mitigating network attacks:
Opportunistic: influenced by an identified weakness
Spoofing: Impersonation
Poisoning: manipulating trusted data
MiTM: interjection between end points
Sniffing: Capturing network packets
Ransomware : class of malware
C&C: Command and control
DDos : Distributed consumption of resources

OSI Model : Seven layers of communication
TCP/IP Model: Four layers of connectivity
ARP : MAC to IP translation
IPv4: 32bit 4 octet ID
IPv6: 128-bit hexadecimal ID
Port : application ID
Well-known ports: 1-1023
DNSSEC: extensions to mitigate forged entries

VOIP : Transmission of voice traffic over IP
MPLS: Protocol-independent telecom support
DNP3: Process automation communications
SIP: Protocol used on VOIP networks
LER: Router used in a MPLS networks
Label: Replacement for a header in MPLS
FCoE : storage area network data channel over IP

Securing wireless networks
bluejacking : Injection attack
bluesnarfing: Unauthorized access
war driving : Hacking wireless networks
ad hoc mode: peer-to-pper connectivity
WEP : Broken encryption, no integrity
WPA2 : uses 802.1x, EAP, AES, and CBC-MAC
WWAN: uses point to point microwave links
GSM & CDMA : Cellular technologies

Security testing
Brute force : Trying every possible combination
Dictionary attack : Compares two sets of hashes
Work factor : Time and effort required
Hash : One-way fixed length fingerprint
Salt: Random string appended before hashing
Reverse lookup : Technique that assumes same input
Symmetric encryption : same key used to encrypt and decrypt
HMAC : hash value that includes a secret key

Operating and maintaining firewalls:
GeoIP : Ip address geographic location or range
Deny by default : Must be explicitly allowed
Allow by default : must be explicitly denied
Whitelist : known benign - proactively allowed
Blacklist : known malicious - proactively denied
Sanitation : Remove sensitive information
Sandbox: isolated environment
Honeypot : Decoy system

Source code security issues:
Buffer overflow : Overrun of allocated memory
Injection attack : Accepts and executes untrusted input
Convert channel: unauthorised flow of information
object reuse attack: Malicious repurpose of code
TOC|TOU : Race condition
Maintenance hook: Mechanism to bypass access controls
Fuzzing: Testing technique that uses invalid data
OAuth: Open authorisation protocol

Deciphering Ciphers and Algorithms
Cipher is a technique or set of rules that transforms cleartext(plaintext) into an unreadable form(cipher text or cryptogram) and back to cleartext

Algorithm is a set of steps to accomplish a task

Key(Cryptovariable)
- The key dictates what parts of the algorithm will be used, in what order, and with what values
- The key is secret
- keyspace is the number of possible key combinations
. 8 bit = 2*8 = 256 possible keys
. 256bit = 2*256 = 1.1.578*1077 possible keys

Cipher Characteristics
1. Stream
Stream cipher encodes the bits one at a time using a XOR key stream generator(resource-intensive)
-RC4 is the most well-known stream cipher.

2. Block 
- Substitution
- Transposition
Block cipher breaks the plaintext message into several blocks
- A block cipher algorithm puts the bits within these blocks through several rounds of substitution and transposition. The goal is confusion(changing values) and diffusion(changing order.
- Electronic Codebook Mode (ECB) each block is independent (doesn’t hide patterns - not suitable for long messages)
- Cipher Block Chaining Mode(CBC) includes an initialization vector and a component of the previous cipher text to leverage randomization

Block ciphers null : DES, 3DES, AES, IDEA, Blowfish, RC5, RC6, Skipjack, CAST

symmetric and asymmetric encryption:
symmetric means the same key is used to encrypt and decrypt
also referred as 
- single key
- shared key
- session key(if used for a single session)

computationally efficient
key sharing is null
not scalable 

Asymmetric means two different but related keys are used :
- known as key-pair
- one key is used for encrypt; the other is used to decrypt
- The keys are referred as a private and public key

computationally intensive
smaller key sizes
null distribution system
scalable

Applying asymmetric encryption:
Since it is computationally intensive, in most cases we use both symmetric and asymmetric keys (also known as hybrid)

Plaintext message  - > symmetric cipher [session key] -> encrypted message - > symmetric cipher [session key] -> plaintext message

but the problem here is how to share session key between Bob and Alice. To accomplish this we use asymmetric algorithm

session key - > Asymmetric Cipher + Alice

now Alice can use this session key to decrypt original message in first case.

Understanding hashing:
A hash is a one-way representation (fingerprint) of a string of text
- A hash function takes input of any length and creates a fixed length output
- Hash value is used to prove integrity

Hash algorithms examples
Message Digest (Mdx), Secure hash algorithm (SHA), Havel, Tiger

Hash Process
sender puts message through  a hashing algorithm and generates a message digest (hash) value -> sender sends plain text message and message digest to receiver - > receiver puts message through a hashing algorithm and generates a message digest(hash) value -> Receiver compares both message digests -> if the message digests are the same - the message was not modified in transmission - > if the message digest are different - the message was modified in transmission

sender 
hello, this is my message + run thorough SHA-2 = 1HFBK2FR7

Receiver :
receiver gets original message and hashed output ie message digest (1HFBK2FR7)
it runs through same algorithm SHA-2 and output should be : 1HFBK2FR7

if outputs are same, message is not modified during transmission.

Hashed MAC
A hashed message authentication code(HMAC) is hashed value that includes a symmetric key.
- An HAMC cannot be reproduce without knowing the key.
- HAMC provides integrity and data origin authentication
- HMAC is used by cryptographic protocols such as the TLS and IPsec to verify the integrity of transmitted data during secure communications

HMAC process:
Sender concatenates message + secret key and puts the results through a hashing algorithm and generates a HMAC value ->Sender appends the HAMC value to the message and sends it to the receiver -> Receiver concatenates messages + secret key and puts a results through a hashing algorithm and generates a HMAC value -> Receiver compares both values -> if the values are the same then the message was not modified in transmission and the origin is known

This will accomplish both integrity and confidentiality
Sender :
hello, this is my message + secret key (1234567) + run through SHA-2 = 79HVGRST

Receiver:
it gets original message and message digest output i.e. 79HVGRST
Receiver needs to have knowledge of secret key (1234567) it is not send by sender
run this message and secret key through same SHA-2 algorithm, output should be same.

Digital Signatures:
A digital signature is a hash value (message digest) encrypted with the sender’s private key.
- A digital signature provides integrity and non-repudiation

Digital signatures require two algorithms:
- Hashing algorithm (e.g. SHA-x)
- Digital signature function such as RSA or DSA (Digital signature algorithm)

Digitally signed message:
Bob creates a message digest(hash value) of his message -> The message digest is encrypted with Bob’s private key -> The encrypted message digest and plaintext message are sent to Alice -> Alice decrypts the message digest using Bob’s public key (providing non-repudiation) -> Alice hashes the plaintext message using the same hash algorithm -> Alice compares the two hash values for a match (proving integrity)

Integrity(not changed during transmission), Confidentiality(only authorized person can see message) and non-repudiation (can’t deny it came from you)
- A message can be hashed, which provides for integrity
- A message can be digitally signed, which provides for non-repudiation and integrity
- A message can be encrypted, which provides for confidentiality.
- A message can be encrypted and digitally signed, which provides for confidentiality, nonrepudiation and integrity.

Deconstructing the Digital certificate lifecycle
A digital certificate is an electronic “passport” that identifies a person, device, domain. organization, or publisher (code).
- The certificate is issued by a trusted certification authority, a web of trust or self-signed.

X.509 v3 digital certificates fields
version : version of certificate
serial number : unique identifier
Signature : Algorithm used to sign the certificate
Issuer : Name of issuer
Validity: valid data of cert
Subject : Name of owner
Public key : Public  key of named owner
Issuer Unique id : ID of certificate authority
Subject unique id : ID of subject

Obtaining a Digital Certificate:
Applicant requests a certificate from a Registration Authority (RA) ->
The RA process the request and validates the applicant ->
The RA forward the request to a certification Authority (CA) ->
The CA requests the public key from the applicant ->
The key-pair is locally generated and the public key is sent to the CA ->
The CA creates and signs the digital certificate -> 
The certificate includes the public key ->
The CA issues the digital certificate to the applicant ->
The CA maintains and, if necessary, revokes the certificate

Digital certificate Revocation:
Certificate revocation list (CRL)
- CA maintained list of certificates that have been revoked

Online certificate status Protocol (OCSP)
- Client receives certificate
- Client sends OCSP request to a OCSP responder
- OCSP responder replies with a certificate status

Both are only enforced for extended validation (EV) certificates

Understanding Cryptographic Protocols:
Cryptographic communication protocols (rules) are designed to secure information flow

Information flow is vulnerable to 
- Eavesdropping and packet capture, which is a violation of confidentiality
- Tampering which is violation of intergrity
- Spoofing and misrepresentation, which can be a violation of authentication, integrity, and availability

Transmission Modes
Link Encryption
- All control information (header, trailers, and routing information) is encrypted along with the payload

needs dedicated communication channel between A and B

End-to-end encryption
- only the payload is encrypted
- Intermediary devices do not have encryption related functions

communication channel in this case is public i.e. internet. Trailer, header and routing information is visible.

Common cryptographic Protocols:
SSL | TLS : 
Use : Securing web based protocols and transmissions,
Purpose : confidentiality, Authentication, Integrity
Cryptographic components : Encryption, HAMC

HTTPS : 
Use: layer SSL | TLS on top of HTTP
Purpose : confidentiality, Authentication, Integrity
Cryptographic components : Encryption, HAMC

FTPS : 
Use : layer SSL | TLS on top of FTP
Purpose : confidentiality
Cryptographic components : Encryption

SSH : 
Use:  Secure channel between a local an remote device (telnet replacement)
Purpose: confidentiality, Integrity
Cryptographic components : Encryption, HAMC

SFTP: 
Use : Layer SSH on top of FTP
Purpose : confidentiality
Cryptographic components : Encryption

S/MIME: 
Use: Secure email communications
Purpose : confidentiality, Integrity, Nonrepudiation
Cryptographic components : Encryption, HAMC

Non-IP networking Protocols:
- MPLS : operates in between L2 and L3
- DNP3 : L2 protocol
- FCoE  : L2 protocol 

Attacks techniques
Scanning: probing for information
Sniffing: Packet capture
Poisoning : Manipulating trusted data
Spoofing : Impersonation
Session Hijacking : Unauthorized insertion

Sniffing can happen at any layer of OSI model

Application: user id/password sniffing
Presentation: SSL/TLS session sniffing
Session:Telnet and FTP sniffing
Transport: TCP session sniffing, UDP sniffing
Network:IP, Port sniffing
Datalink :MAC/ARP sniffing
Physical:Surveillance sniffing

Poisoning:
A poisining attack is when a trusted source of data is manipulated:
- ARP cache
- Routing table
- DNS pharming
- Website

Session Hijacking:
A session hijacking attack intercepts communication between two systems
- Man in middle (may use spoofing and/or poisoning)
- Replay Attack

Spoofing:
A spoofing attack is when an attacker impersonates(pretend to be) an address, system, or person
- MAC address
- IP address
- Domain
- Hyperlink
- Email sender
- Trusted source


















































Tuesday, August 1, 2017

Python OOP fundamentals

Print:
print("%s is %s years old" % (name, age))
print('{0} is {1} years old'.format(name, age))

format in custom way:
print("First: {foo}, Second: {bar}".format(bar=22.42,foo=41))  

# reuse single. used more than once
print("More: {0:6.2f} or {0:6.3f}".format(5.1234))

# ignore multiple. Extra parameters ignored.

print("More: {1:6.2f} or {1:6.3f}".format(12.345, 5.1234))

Class name
1. private (_)
- begins with single underscore
def _copy(list):

- means used in present module
not available elsewhere
module “import” ignored
internal use only

2. Framework-reserved (_ _)
- Two or more
  must start and end with _
def __init__

pip = pip install packages
Pypi = python package index
PEP : Python enhancement proposal

def show():
  print("\t***************")
  print('\t* Hello World *')
  print('\t***************')

show()

or

banner:

def ShowStars(num):
  return "\t*" + ("*" * (num + 3))
  
def Show(message):
  xx = len(message)
  stars = ShowStars(xx)
  print(stars)
  print("\t* " + message + " *")
  print(stars)
  
Show("This is a MESSAGE")

Type change:
sNum = '20'
print(type(sNum))
iNum = int(sNum)
print('*'*iNum)

comparison operators:
__gt__()
__lt__()

import builtins
help(builtins)

isFun = True
isTough = False
if isFun or isTough:
  print("one out of 2 ain't bad")

if not isFun:
  print("Practices makes it fun")
  
if not (isFun and isTough):
  print("Learn C/C++")

sorting dict keys:

def sortLogic(ref):
  return len(ref)
  
data = {"First":"John", \
        "Last": "Doe",  \
        "Phone": "95496349",  \
        "Email": "test@hotmail.com"}

zkeys = list(data.keys())
print(zkeys)
zkeys.sort(key=sortLogic)
print(zkeys)

for zkey in zkeys:
  print("%10s:[%-20s]" % (zkey, data[zkey]))

sorted:
print(sorted(("Mr. Ed", "Mar Daisy", \
              "Mr. T", "Dr. Who"))) # tuple
print(sorted(["Mr. Ed", "Mar Daisy", \
              "Mr. T", "Dr. Who"])) # list
print(sorted({"Mr. Ed", "Mar Daisy", \
              "Mr. T", "Dr. Who"}))  # set
print(sorted({4:"Mr. Ed", 2:"Mar Daisy", \
              3:"Mr. T", 1:"Dr. Who"}))    # Dict Keys 


An object has two characteristics:
attributes
behavior
Let's take an example:
Parrot is an object,
name, age, color are attributes
singing, dancing are behavior
The concept of OOP in Python focuses on creating reusable code. This concept is also known as DRY (Don't Repeat Yourself).
In Python, the concept of OOP follows some basic principles:
Inheritance A process of using details from a new class without modifying existing class.
Encapsulation Hiding the private details of a class from other objects.

Polymorphism A concept of using common operation in different ways for different data input.


# class defines methods and data attributes of particular object
# The special __init__ method of class.Its job is to initialise or assign some values to its data attributes whenever we first create object.
# attributes are data we can assign values to and which can be used later.
# methods are used to perform operation on data attributes
# class attributes can be used outside class

--> A class is a blueprint for the object. From class, we construct instances. An instance is a specific object created from a particular class.
class Employee:
  def __init__(self):
    self.firstname = 'nawraj'
    self.lastname = 'Lekhak'

def main():
  print('This python programming is now starting')
    
main() 

when ever you define main it should be on level of class not within class because it is not class method and considered as main function.


### create class attributes
class Person:
  'Base class of a person'
  
  # class attributes
  first_name = 'Nawraj'
  last_name = 'lekhak'
  age = 30
  
def main():
  
  #print the title of the program
  
  print("\t *************Welcome to Employee Database ***********")
  print()
  
  # Print the class attributes values.
  print("First name is:", Person.first_name)
  print("Last name is:", Person.last_name)
  print("Age:", Person.age)
  print() ## to add additional line for seperation
  
# Call the main method to start the program
main()


Object

--> An object (instance) is an instantiation of a class. When class is defined, only the description for the object is defined. Therefore, no memory or storage is allocated


Methods

--> Methods are functions defined inside the body of a class. They are used to define the behaviours of an object
##create object based on Employee class
class Employee:
  'Base class of a Employee'
  
  # set default constructor
  ## instance attributes
  def __init__(self):
    self.first_name  = "Nawraj" # to reference data attributes
    self.last_name = "Lekhak"
    self.uid = '321'
    
def main():
  # create object based on Employee class
  # instantiate the Employee class
-->

  employee_01 = Employee() ## here employee_01 is obj of class Employee
  employee_02 = Employee()
  employee_03 = Employee()

##Obtain object attributes
class Employee:
  'Base class for our Employee'
  
  # set default constructor, instance attributes
  def __init__(self):
    self.first_name  = "Nawraj" # to reference data attributes
    self.last_name = "Lekhak"
    self.uid = '321'
   
 # instance method 
  def get_first_name(self):
    return self.first_name
    
  def get_last_name(self):
    return self.last_name
    
  def get_uid(self):
    return self.uid
    
def main():
  # create object based on Employee class
  employee_01 = Employee()
  
  ## Access and print the first last_name. Access class attributes
  print('First name:', employee_01.get_first_name())
  print('Last name:', employee_01.get_last_name())
  print('Uid:', employee_01.get_uid())
  
# call main method
main()

##Changing object attribute values
class Employees:
  'Base class for our Employees'
  
  # set default constructor when new objects are created
  def __init__(self):
    self.first_name  = "n/a" # to reference data attributes
    self.last_name = "n/a"
    self.uid = 'n/a'
    
def main():
  # create object based on Employees class
  employee_01 = Employees()
  print('First name:', employee_01.first_name)
  print('Last name:', employee_01.last_name)
  print('Uid:', employee_01.uid)
  print()
  
  # Let's modify this object attribute values
  employee_01.first_name = 'Nawraj'
  employee_01.last_name = 'Lekhak'
  employee_01.uid = '388921'
  
  # Print new assigned values
  print("New Values:")
  print('First name:', employee_01.first_name)
  print('Last name:', employee_01.last_name)
  print('Uid:', employee_01.uid)
  
# start out program
main()

##Acessing object methods
class Employees:
  'Base class for Employees'
   
  # set default constructor when new objects are created
  def __init__(self):
    self.first_name  = "Nawraj" 
    self.last_name = "Lekhak"
    self.uid = '102'
  
  ## Getter method
  def get_first_name(self):
    return self.first_name
    
  def get_last_name(self):
    return self.last_name
    
  def get_uid(self):
    return self.uid
  
  ## set method  
  def set_first_name(self, xfirstname):
    self.first_name = xfirstname
    
  def set_last_name(self, xlastname):
    self.last_name = xlastname
    
  def set_uid(self, xuid):
    self.uid = xuid 

def main():
  # create object based on Employees class
  employee = Employees()
  
  ## access getter methods
  print('First name:', employee.get_first_name())
  print('Last name:', employee.get_last_name())
  print('Uid:', employee.get_uid())
  print()
  
  # Modify object attributes Values
  employee.set_first_name('John')
  employee.set_last_name('Williams')
  employee.set_uid('111')
  
  # access setter methods
  print("New Values:")
  print('First name:', employee.get_first_name())
  print('Last name:', employee.get_last_name())
  print('Uid:', employee.get_uid())
  
# start out program
main()

## Passing arguments to constructor
class Employee:
  'Base class for Employees'
  
  def __init__(self, myFirstName, myLastName, myUID):
    self.first_name  = myFirstName 
    self.last_name = myLastName
    self.user_id = myUID
  
  def show_employee_details(self):
    print('First Name:', self.first_name)
    print('Last name:', self.last_name)
    print("user id:", self.user_id)
  
def main():
  # create new object
  employee = Employee('Nawraj', 'Lekhak','741')
  employee01 = Employee('Ram', 'Bhatt','101')
  
  # Display employee show_employee_details
  employee.show_employee_details()
  print()
  employee01.show_employee_details()
  
# start out program
main()  

## destructor. Also known as garbage collector.
class MyClass:
  
  def __init__(self):
    print("Create new object..")
    
  def __del__(self):
    print("Destroying used object..")
  
def main():
  # create new object
  object1 = MyClass()
  object2 = MyClass()
  object3 = MyClass()
  
  print("\n\n") ## create two new line 

  del(object1)
  del(object2)
  del(object3)
   
# start out program
main()  

## Public access modifier
class BankAccount:
  
  def __init__(self, my_initial_balance):
    self.balance = my_initial_balance
    
  def get_balance(self):
    return self.balance
    
def main():
  # Create new bank Account.
  account = BankAccount(500)
  
  #Display the current balance.
  print('Current balance is $', account.get_balance())
  
  ## Access object attribute value outside class
  account.balance += 800
  
  print('New balance is : $', account.get_balance())
  
# start out program
main()  
  

Encapsulation

-->
Using OOP in Python, we can restrict access to methods and variables. This prevent data from direct modification which is called encapsulation. In Python, we denote private attribute using underscore as prefix i.e single “ _ “ or double “ __“.

## Private access modifier
class BankAccount:
  
  def __init__(self, my_starting_balance):
    ## __ to make data attribute private to use within class
    self.__balance = my_starting_balance
    
  def get_balance(self):
    return self.__balance
    
  def deposit(self, amount_to_deposit):
    self.__balance += amount_to_deposit
    
def main():
  # Create new bank Account.
  account = BankAccount(500)
  
  account.deposit(400)
  
  #Display the starting balance.
  print('Starting balance is: $', account.get_balance())
  
  ## This will not be acessible
  print('Starting balance is: $', account.balance())
  
  account.balance += 4000
  print('Starting balance is: $', account.get_balance())
  
# start out program
main()  

# python child classes
class BankAccount:# Parent class
  def __init__(self, initial_balance):
    self.__balance = initial_balance
    
  def get_balance(self):
    return self.__balance
    
  def deposit(self,amount):
    self.__balance += amount
    
  def withdraw(self, amount):
    self.__balance -= amount
    
class SavingsAccount(BankAccount): # child class from BankAccount
  def __init__(self,initial_balance):
    BankAccount.__init__(self,initial_balance)
    
def main():
  # create savings account object
  savings_account = SavingsAccount(200)
  
  # Display current __balance
  print('SAving account balance $', savings_account.get_balance())
  savings_account.deposit(300)
  print('New account balance $', savings_account.get_balance())
  
# start program
main()

# python child classes
class BankAccount:# Parent class
  def __init__(self, initial_balance):
    self.__balance = initial_balance
    
  def get_balance(self):
    return self.__balance
    
  def deposit(self,amount):
    self.__balance += amount
    
  def withdraw(self, amount):
    self.__balance -= amount
    
class CheckingAccount(BankAccount): # child class
  
  def __init__(self,initial_balance):
    BankAccount.__init__(self,initial_balance)
    
def main():
  # create checking account object
  checking_account = CheckingAccount(300)
  
  # Display current __balance
  print('Initial account balance $', checking_account.get_balance())
  checking_account.deposit(300)
  print('New balance with deposit $', checking_account.get_balance())
  checking_account.withdraw(200)
  print('New balance after withdraw $', checking_account.get_balance())
  
# start program
main()

Inheritance

-->
Inheritance is a way of creating new class for using details of existing class without modifying it. The newly formed class is a derived class (or child class). Similarly, the existing class is a base class (or parent class).

# Inheritance. Class inherit from another class
class BankAccount:# Parent class
  def __init__(self, initial_balance):
    self.__balance = initial_balance
    
  def get_balance(self):
    return self.__balance
    
  def deposit(self,amount):
    self.__balance += amount
    
  def withdraw(self, amount):
    if amount > self.__balance:
      print("You do not have enough fund")
    else:
      self.__balance -= amount
  
def main():
  # create checking account object
  account = BankAccount(100)
  
  # Display current balance
  print('Initial account balance $', account.get_balance())
  account.deposit(300)
  print('New balance with deposit $', account.get_balance())
  account.withdraw(500)
  print('New balance after withdraw $', account.get_balance())
  
# start program
main()

# Import. 
import savings_account # name of file without extension
import checking_account 
  
def main():
  
  # create a saving and checking account
  
  mySavingsAccount = savings_account.SavingsAccount(100)
  myCheckingAccount = checking_account.CheckingAccount(500)
  
  # Display initial balance for both account
  print('SAving account balance $', mySavingsAccount.get_balance())
  print('Checking account balance $', myCheckingAccount.get_balance())
  
# start program
main()

--> Additionally, we use super() function before __init__() method. This is because we want to pull the content of __init__() method from the parent class into the child class

Polymorphism


-->
Polymorphism is an ability (in OOP) to use common interface for multiple form (data types).

-->
Suppose, we need to color a shape, there are multiple shape option (rectangle, square, circle). However we could use same method to color any shape. This concept is called Polymorphism.

# Overriding parent method within child class with same name. Also called polymorphism. 
class BankAccount:
  
  def __init__(self,myBalance):
    self.__balance = myBalance
    
  def get_balance(self):
    print('Calling the get_balance method from "Parent" class')
    
class CheckingAccount(BankAccount):
  def __init__(self, myBalance):
    BankAccount.__init__(self,myBalance)
    
  def get_balance(self):
    BankAccount.get_balance(self)## this will call parent class method. commenting this will call child class method
    print('Calling the get_balance method from "Child" class')
    
def main():
  account = CheckingAccount(50)
  account.get_balance()
  
main()

positional parameters and keyword parameters

if __name__ == ‘__main__’:
       main()

holds true if you are running script directly instead of import some module/file


python debugger :
python script.py -pdb
import pdb
pdb.set_trace()

Some useful ones to remember are:
  • b: set a breakpoint
  • c: continue debugging until you hit a breakpoint
  • s: step through the code
  • n: to go to next line of code
  • l: list source code for the current file (default: 11 lines including the line being executed)
  • u: navigate up a stack frame
  • d: navigate down a stack frame
  • p: to print the value of an expression in the current context

“To return both the key and value in a tuple, you can use the items() function:”

A decorator is a function that takes one function as input and returns another function

“Uses of _ and __ in Names
Names that begin and end with two underscores (__) are reserved for use within Python, so you should not use them with your own variables. This naming pattern was chosen because it seemed unlikely to be selected by application developers for their own variables.”
For instance, the name of a function is in the system variable function .__name__, and its documentation string is function .__doc__:”


Packages
We went from single lines of code, to multiline functions, to standalone programs, to multiple modules in the same directory. To allow Python applications to scale even more, you can organize modules into file hierarchies called packages.
Maybe we want different types of text forecasts: one for the next day and one for the next week. One way to structure this is to make a directory named sources, and create two modules within it: daily.py and weekly.py. Each has a function called forecast. The daily version returns a string, and the weekly version returns a list of seven strings.
Here’s the main program and the two modules. (The enumerate() function takes apart a list and feeds each item of the list to the for loop, adding a number to each item as a little bonus.)

from sources import daily, weekly”

“You’ll need one more thing in the sources directory: a file named __init__.py. This can be empty, but Python needs it to treat the directory containing it as a package”

“Let’s try again, this time including the special Python object initialization method __init__:
>>> class Person():
...     def __init__(self):
...         pass
This is what you’ll see in real Python class definitions. I admit that the __init__() and self look strange. __init__() is the special Python name for a method that initializes an individual object from its class definition. 1 The self argument specifies that it refers to the individual object itself.
When you define __init__() in a class definition, its first parameter should be self. Although self is not a reserved word in Python, it’s common usage. No one reading your code later (including you!) will need to guess what you meant if you use self.”

Inheritance:
Creating a new class from an existing class but with some additions or changes. It’s an excellent way to reuse code. When you use inheritance, the new class can automatically use all the code from the old class but without copying any of it.
You define only what you need to add or change in the new class, and this overrides the behaviour of the old class. The original class is called a parent, superclass, or base class; the ”“new class is called a child, subclass, or derived class. These terms are interchangeable in object-oriented programming.
So, let’s inherit something. We’ll define an empty class called Car. Next, define a subclass of Car called Yugo. You define a subclass by using the same class keyword but with the parent class name inside the parentheses (class Yugo(Car) below):
>>> class Car():
...     pass
...
>>> class Yugo(Car):
...     pass
...
Next, create an object from each class:
>>> give_me_a_car = Car()
>>> give_me_a_yugo = Yugo()”


“Without doing anything special, Yugo inherited the exclaim() method from Car. In fact, Yugo says that it is a Car, which might lead to an identity crisis. Let’s see what we can do about that.”

“Use super() when the child is doing something its own way but still needs something from the parent (as in real life).”

“The names of these methods begin and end with double underscores (__). You’ve already seen one: __init__ initializes a newly created object from its class definition and any arguments that were passed in.”

decorator:
a decorator is a function that takes another function as an argument , add some kind of functionality and return another function. all of this without altering the source code of original function that is passed.

@decorator_function
def display():
print(‘display function ran’)
is same as 
display = decorator_function(display)

__name__ : Every module in Python has a special attribute called __name__. It is a built-in variable that returns the name of the module.

__main__ : Like other programming languages, Python too has an execution entry point i.e. main. 'main' is the name of the scope in which top-level code executes. Basically you have two ways of using a Python module: Run it directly as a script, or import it. When a module is run as a script, its __name__ is set to __main__.
Thus,the value of __name__ attribute is set to __main__ when the module is run as main program. Otherwise the value of __name__ is set to contain the name of the module.
__file__ represents the file the code is executing from
os.path.dirname(__file__) gives you the directory the file is in
os.path.pardir stands for ".." which means one directory above the current one
os.path.join(os.path.dirname(__file__), os.path.pardir) joins the directory name and ".."
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) resolves the above path and gives you an absolute path for the parent directory of the directory your file is in

With statement in Python
In Python you need to give access to a file by opening it. You can do it by using the open() function. Open returns a file object, which has methods and attributes for getting information about and manipulating the opened file. 
With statement
With the "With" statement, you get better syntax and exceptions handling. "The with statement simplifies exception handling by encapsulating common preparation and cleanup tasks."
In addition, it will automatically close the file. The with statement provides a way for ensuring that a clean-up is always used.
Without the with statement, we would write something like this:
file = open("welcome.txt")
data = file.read()
print data
file.close()  # It's important to close the file when you're done with it

With Statement Usage
Opening a file using with is as simple as: with open(filename) as file:
with open("welcome.txt") as file: # Use file to refer to the file object
   data = file.read()
   do something with data
Opens output.txt in write mode
with open('output.txt', 'w') as file:  # Use file to refer to the file object
    file.write('Hi there!')
Notice, that we didn't have to write "file.close()". That will automatically be called. 

#The @ symbol is used for class, function and method decorators.





-->