aboutsummaryrefslogtreecommitdiff
path: root/fifconfig.py
diff options
context:
space:
mode:
Diffstat (limited to 'fifconfig.py')
-rw-r--r--fifconfig.py232
1 files changed, 232 insertions, 0 deletions
diff --git a/fifconfig.py b/fifconfig.py
new file mode 100644
index 0000000..6ff8d17
--- /dev/null
+++ b/fifconfig.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python3
+# File: fifconfig.py
+# Location: https://gitlab.com/bgstack15/fifconfig
+# Author: bgstack15
+# SPDX-License-Identifier: GPL-3.0
+# Startdate: 2022-03-15 20:35
+# Title: Flask-ifconfig
+# Purpose: Act similar to http://ifconfig.me but with flask, for my network testing usage
+# History:
+# Usage:
+# Reference:
+# https://tedboy.github.io/flask/generated/generated/flask.Request.html
+# https://bgstack15.ddns.net/cgit/stackbin/tree/stackbin.py
+# https://pypi.org/project/dicttoxml/
+# Improve:
+# Dependencies:
+# pip-devuan: json2html
+# pip-centos7: json2html
+# dep-devuan: python3-dicttoxml | python3-xmltodict
+# dep-centos7: python36-xmltodict
+# reverse-proxy configs that include X-Forwarded-Prefix headers.
+# Documentation: see README.md
+
+from flask import Flask, request, jsonify, url_for
+
+app = Flask(__name__)
+try:
+ app.config.from_pyfile('fifconfig.conf')
+except:
+ pass
+
+def _make_dict_safe_for_text(response):
+ """ Mostly for converting the silly x-forwarded-for ImmutableList so that it does not show that class name. """
+ # This works except for the silly "ImmutableList([" text output for that one 'via' item.
+ #response = { i:str(response[i]) for i in response }
+ # So instead of that beautiful oneliner, you get this stupid 9-line block.
+ gen = (i for i in response)
+ new_response = {}
+ for i in gen:
+ if "<class 'werkzeug.datastructures.ImmutableList'>" == str(type(response[i])):
+ #print(f"i {i} is a immutablelist")
+ new_response[i] = ','.join(response[i])
+ else:
+ new_response[i] = str(response[i])
+ return new_response
+
+def get_attribs(
+ request,
+ lia = False,
+ ia = False,
+ ua = False,
+ l = False,
+ re = False,
+ m = False,
+ e = False,
+ mt = False,
+ c = False,
+ xff = False
+):
+ """
+ Main function that builds the desired response dict
+ that will later be turned into json or html by the
+ various small @app.route functions.
+ """
+ response = {}
+ r = request
+ rh = r.headers
+ if lia:
+ response['lastipaddress'] = r.remote_addr
+ if ia:
+ response['ipaddress'] = r.remote_addr
+ if r.access_route and len(r.access_route) > 0:
+ response['ipaddress'] = r.access_route[0]
+ if ua:
+ response['useragent'] = str(r.user_agent)
+ if l:
+ response['language'] = r.accept_languages
+ if re:
+ response['referer'] = r.referrer
+ if m:
+ response['method'] = r.method
+ if e:
+ response['encoding'] = r.accept_encodings
+ if mt:
+ response['mimetype'] = r.accept_mimetypes
+ if c:
+ response['charset'] = r.accept_charsets
+ if xff:
+ #response['x-forwarded-for'] = rh.get('X-Forwarded-For') or ''
+ response['x-forwarded-for'] = r.access_route
+ #'charset': rh.get('Accept-Charset') or '',
+ #'endpoint': request.endpoint
+ # via is the same as x-forwarded-for
+ #'via': r.access_route
+ print(f"DEBUG: dict is {response}")
+ return response
+
+def prepare_output(request, response):
+ """
+ Used to customize the output to json, html, or text depending on what the client asked for. In order of most important to least important:
+ 1. request argument, i.e., '?json'
+ 2. Accept-Mimetypes header
+ """
+ possible_formats = ['json','html','text','xml']
+ _format = "text"
+ # priority two
+ for i in request.accept_mimetypes:
+ for j in i:
+ if 'application/xml' == j and _format != "html":
+ _format = "xml"
+ if 'application/json' == j:
+ _format = "json"
+ if 'application/xhtml+xml' == j or 'text/html' == j:
+ _format = "html"
+ if 'text/plain' == j:
+ _format = "text"
+ # priority one
+ if request.args:
+ if 'xml' in request.args:
+ _format = "xml"
+ if 'json' in request.args:
+ _format = "json"
+ if 'html' in request.args:
+ _format = "html"
+ if 'text' in request.args:
+ _format = "text"
+ if _format not in possible_formats:
+ print(f"DEBUG (prepare_output): how did format {_format} get defined?! Using text.")
+ _format = "text"
+ # main process
+ if "html" == _format:
+ print("Sending html")
+ if True:
+ from json2html import json2html
+ response = _make_dict_safe_for_text(response)
+ response = json2html.convert(json = response)
+ if not 'nolinks' in request.args:
+ prefix = ''
+ if 'HTTP_X_FORWARDED_PREFIX' in request.environ:
+ prefixes = request.environ['HTTP_X_FORWARDED_PREFIX']
+ prefix = ''.join(prefixes.split(',')).replace(' ','')
+ response += f"<div style='font-size: 80%;'>"
+ response += f"<a href='{prefix}{url_for('ip')}'>ip</a> "
+ response += f"<a href='{prefix}{url_for('ua')}'>ua</a> "
+ response += f"<a href='{prefix}{url_for('lang')}'>lang</a> "
+ response += f"<a href='{prefix}{url_for('encoding')}'>encoding</a> "
+ response += f"<a href='{prefix}{url_for('mime')}'>mime</a> "
+ response += f"<a href='{prefix}{url_for('charset')}'>charset</a> "
+ response += f"<a href='{prefix}{url_for('forwarded')}'>forwarded</a> "
+ #response += f"<p>{request.environ}<p>"
+ response += f"<a href='{app.config['SOURCE_URL']}'>SOURCE</a>"
+ response += f"</div>"
+ return response
+ else:
+ print("Unable to load json2html, sending plain text.")
+ return str(response)
+ elif "xml" == _format:
+ print("Sending xml")
+ pretty = False
+ if request.args and 'pretty' in request.args:
+ pretty = True
+ try:
+ import xmltodict
+ response = xmltodict.unparse({'info':response})
+ except:
+ try:
+ # This lib is objectively better but not available natively on CentOS 7
+ print("Trying dicttoxml")
+ from dicttoxml import dicttoxml
+ response = dicttoxml(response,custom_root="info")
+ except:
+ print("Unable to load xmltodict, sending plain text.")
+ return str(response)
+ if pretty:
+ try:
+ from xml.dom.minidom import parseString
+ response = parseString(response).toprettyxml()
+ except:
+ pass
+ return response
+ elif "json" == _format:
+ print("Sending json")
+ return jsonify(response)
+ else:
+ # the only other option is text
+ print("Sending text")
+ response = _make_dict_safe_for_text(response)
+ new_response = ""
+ if 1 == len(response):
+ for i in response:
+ return response[i]
+ else:
+ for i in response:
+ new_response = new_response + '\n' + str(i) + ': ' + str(response[i])
+ return new_response
+
+@app.route('/')
+def root():
+ response = get_attribs(request, lia = False, ia = True, ua = True, l = True, re = True, m = True, e = True, mt = True, c = True, xff = True)
+ response = prepare_output(request, response)
+ return response
+
+@app.route('/ip')
+def ip():
+ return prepare_output(request, get_attribs(request, ia = True))
+
+@app.route('/ua')
+def ua():
+ return prepare_output(request, get_attribs(request, ua = True))
+
+@app.route('/lang')
+def lang():
+ return prepare_output(request, get_attribs(request, l = True))
+
+@app.route('/encoding')
+def encoding():
+ return prepare_output(request, get_attribs(request, e = True))
+
+@app.route('/mime')
+def mime():
+ return prepare_output(request, get_attribs(request, mt = True))
+
+@app.route('/charset')
+def charset():
+ return prepare_output(request, get_attribs(request, c = True))
+
+@app.route('/forwarded')
+def forwarded():
+ return prepare_output(request, get_attribs(request, xff = True))
+
+if __name__ == "__main__":
+ app.run()
bgstack15