diff --git a/app/models.py b/app/models.py index 79aa39b..29650a5 100644 --- a/app/models.py +++ b/app/models.py @@ -36,12 +36,15 @@ class ClientPassword(db.Model): item = db.Column(db.String(255), nullable=False) username = db.Column(db.String(255), nullable=False) password = db.Column(db.String(255), nullable=False) + ip_address = db.Column(db.String(255)) # Ensure this column exists client_id = db.Column(db.Integer, db.ForeignKey('client.id'), nullable=False) category_id = db.Column(db.Integer, db.ForeignKey('password_category.id')) category = db.relationship('PasswordCategory') + def __repr__(self): return f"" + class PasswordCategory(db.Model): __tablename__ = 'password_category' id = db.Column(db.Integer, primary_key=True) diff --git a/app/routes/clients.py b/app/routes/clients.py index 2f5d78a..326bbd1 100644 --- a/app/routes/clients.py +++ b/app/routes/clients.py @@ -65,15 +65,24 @@ def add_client_password(client_id): item = request.form.get('item') username = request.form.get('username') password_text = request.form.get('password') + ip_address = request.form.get('ip_address') # Capture the IP address category_id = request.form.get('category_id') + if item and username and password_text: - new_pass = ClientPassword(item=item, username=username, password=password_text, client_id=client.id) + new_pass = ClientPassword( + item=item, + username=username, + password=password_text, + ip_address=ip_address, # Set the IP address + client_id=client.id + ) if category_id: new_pass.category_id = int(category_id) db.session.add(new_pass) db.session.commit() return redirect(url_for('clients.client_detail', client_id=client.id)) + @clients_bp.route('/clients//attach_domain', methods=['POST']) def attach_domain(client_id): client = Client.query.get_or_404(client_id) @@ -167,3 +176,32 @@ def remove_remote(client_id, remote_id): db.session.delete(remote) db.session.commit() return redirect(url_for('clients.client_detail', client_id=client_id)) + +@clients_bp.route('/clients//print', methods=['GET']) +def print_client(client_id): + client = Client.query.get_or_404(client_id) + available_domains = Domain.query.filter_by(ssl_only=False).all() + available_ssl = Domain.query.filter_by(ssl_only=True).all() + available_categories = PasswordCategory.query.all() + available_remote_categories = RemoteCategory.query.all() + # Group passwords by category. + passwords_by_category = {} + for p in client.passwords: + cat = p.category.name if p.category else "Uncategorized" + if cat not in passwords_by_category: + passwords_by_category[cat] = [] + passwords_by_category[cat].append(p) + # Group remotes by category. + remotes_by_category = {} + for r in client.remotes: + cat_name = r.remote_category.name + if cat_name not in remotes_by_category: + remotes_by_category[cat_name] = [] + remotes_by_category[cat_name].append(r) + return render_template('client_detail_print.html', client=client, + available_domains=available_domains, + available_ssl=available_ssl, + available_categories=available_categories, + available_remote_categories=available_remote_categories, + passwords_by_category=passwords_by_category, + remotes_by_category=remotes_by_category) diff --git a/app/routes/tools.py b/app/routes/tools.py index 061e47c..fd73213 100644 --- a/app/routes/tools.py +++ b/app/routes/tools.py @@ -1,6 +1,6 @@ import json from flask import Blueprint, render_template, request, redirect, url_for, flash, Response -from app.models import Client, Domain, ClientPassword, PasswordCategory +from app.models import Client, Domain, ClientPassword, PasswordCategory, RemoteCategory, ClientRemote from app import db tools_bp = Blueprint('tools', __name__) @@ -46,6 +46,7 @@ def delete_password_category(category_id): flash("Category deleted and associated passwords reassigned to General.", "success") return redirect(url_for('tools.password_categories')) + @tools_bp.route('/tools/export_clients', methods=['GET']) def export_clients(): clients = Client.query.all() @@ -57,13 +58,15 @@ def export_clients(): 'address': c.address, 'passwords': [], 'domains': [], - 'ssl_domains': [] + 'ssl_domains': [], + 'remotes': [] } for p in c.passwords: client_dict['passwords'].append({ 'item': p.item, 'username': p.username, 'password': p.password, + 'ip_address': p.ip_address, # Added IP address field 'category': p.category.name if p.category else None }) for d in c.domains: @@ -80,6 +83,16 @@ def export_clients(): 'ssl_expiration_date': d.ssl_expiration_date, 'ssl_only': d.ssl_only }) + for r in c.remotes: + client_dict['remotes'].append({ + 'remote_category': r.remote_category.name if r.remote_category else None, + 'name': r.name, + 'ip_address': r.ip_address, + 'port': r.port, + 'psk': r.psk, + 'address': r.address, + 'details': r.details + }) data.append(client_dict) json_data = json.dumps(data) response = Response(json_data, mimetype='application/json') @@ -115,6 +128,7 @@ def import_clients(): item = p.get('item'), username = p.get('username'), password = p.get('password'), + ip_address = p.get('ip_address'), # Read the IP address from JSON client_id = new_client.id, category = category ) @@ -149,6 +163,27 @@ def import_clients(): db.session.add(domain_obj) db.session.flush() new_client.ssl_domains.append(domain_obj) + # Import remote associations + for r in client_data.get('remotes', []): + remote_category_name = r.get('remote_category') + remote_category = None + if remote_category_name: + remote_category = RemoteCategory.query.filter_by(name=remote_category_name).first() + if not remote_category: + remote_category = RemoteCategory(name=remote_category_name) + db.session.add(remote_category) + db.session.flush() + new_remote = ClientRemote( + client_id = new_client.id, + remote_category = remote_category, + name = r.get('name'), + ip_address = r.get('ip_address'), + port = r.get('port'), + psk = r.get('psk'), + address = r.get('address'), + details = r.get('details') + ) + db.session.add(new_remote) db.session.commit() flash("Clients imported successfully", "success") except Exception as e: diff --git a/app/templates/client_detail.html b/app/templates/client_detail.html index a8a79c9..aee1cb2 100644 --- a/app/templates/client_detail.html +++ b/app/templates/client_detail.html @@ -184,6 +184,7 @@ Item Username Password + IP Address Action @@ -193,6 +194,7 @@ {{ p.item }} {{ p.username }} {{ p.password }} + {{ p.ip_address }}
@@ -226,24 +230,28 @@
- - -
- + + + +
+ + +
+
@@ -345,9 +353,11 @@
-
+
+ Print
+ {% endblock %} diff --git a/app/templates/client_detail_print.html b/app/templates/client_detail_print.html new file mode 100644 index 0000000..1626c1c --- /dev/null +++ b/app/templates/client_detail_print.html @@ -0,0 +1,168 @@ +{% extends "base.html" %} +{% block title %}Print - {{ client.name }}{% endblock %} +{% block content %} +
+
+

{{ client.name }}

+
+ + +
+

Details

+
+

Phone: {{ client.phone or "N/A" }} | Address: {{ client.address or "N/A" }}

+
+
+ +
+ + +
+

Remote

+ {% for category, remotes in remotes_by_category.items() %} + {% if remotes|length > 0 %} +

{{ category }}

+ + + {% if category == 'RDP' %} + + + + + + {% elif category == 'SSL-VPN' %} + + + + + {% elif category == 'VPN' %} + + + + + {% elif category == 'MGMT' %} + + + + + {% else %} + + + + {% endif %} + + + {% for remote in remotes %} + + {% if category == 'RDP' %} + + + + {% elif category == 'SSL-VPN' %} + + + {% elif category == 'VPN' %} + + + {% elif category == 'MGMT' %} + + + {% else %} + + {% endif %} + + {% endfor %} + +
NameIP AddressPort
IP AddressPort
IP AddressPSK
NameAddress
Details
{{ remote.name }}{{ remote.ip_address }}{{ remote.port }}{{ remote.ip_address }}{{ remote.port }}{{ remote.ip_address }}{{ remote.psk }}{{ remote.name }}{{ remote.address }}{{ remote.details }}
+ {% endif %} + {% endfor %} +
+ +
+ + +
+

Passwords

+ {% for category, passwords in passwords_by_category.items() %} + {% if passwords|length > 0 %} +

{{ category }}

+ + + + + + + + + + {% for p in passwords %} + + + + + + {% endfor %} + +
ItemUsernamePassword
{{ p.item }}{{ p.username }}{{ p.password }}
+ {% endif %} + {% endfor %} +
+ +
+ + +
+

Domains

+ {% if client.domains|length > 0 %} + + + + + + + + + {% for d in client.domains %} + + + + + {% endfor %} + +
DomainWHOIS Expiration
{{ d.domain }}{{ d.expiration_date }}
+ {% else %} +

No domain associations.

+ {% endif %} +
+ +
+ + +
+

SSL

+ {% if client.ssl_domains|length > 0 %} + + + + + + + + + {% for d in client.ssl_domains %} + + + + + {% endfor %} + +
DomainSSL Expiration
{{ d.domain }}{{ d.ssl_expiration_date }}
+ {% else %} +

No SSL associations.

+ {% endif %} +
+ + + +
+{% endblock %}