* Added third party apps portal page stub * [WIP] third party apps portal page * Added portal page third party apps Added page to manage OAuth 2.0 active sessions * [Fix] Typo me.html * frappe/www/third_party_apps. * [Fix] Added column for last log inversion-14
@@ -0,0 +1,9 @@ | |||||
frappe.ready(() => { | |||||
$(".btn-delete-app").on("click", function(event) { | |||||
frappe.call({ | |||||
method:"frappe.www.third_party_apps.delete_client", | |||||
args: {"client_id": $(this).data("client_id"), | |||||
} | |||||
}).done(r => location.href="/third_party_apps"); | |||||
}); | |||||
}); |
@@ -18,10 +18,12 @@ | |||||
<li><a href="/update-password"> | <li><a href="/update-password"> | ||||
<h6 class="text-muted">{{ _("Reset Password") }}</h6> | <h6 class="text-muted">{{ _("Reset Password") }}</h6> | ||||
</a></li> | </a></li> | ||||
<li><a href="/update-profile?name={{ user }}"> | |||||
<li><a href="/update-profile?name={{ user }}"> | |||||
<h6 class="text-muted">{{ _("Edit Profile") }}</h6> | <h6 class="text-muted">{{ _("Edit Profile") }}</h6> | ||||
</a></li> | </a></li> | ||||
<li><a href="/third_party_apps"> | |||||
<h6 class="text-muted">{{ _("Manage Third Party Apps") }}</h6> | |||||
</a></li> | |||||
</ul> | </ul> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -0,0 +1,66 @@ | |||||
{% extends "templates/web.html" %} | |||||
{% block title %} {{ _("Third Party Apps") }} {% endblock %} | |||||
{% block header %} | |||||
<h1>{{ _("Third Party Apps") }}</h1> | |||||
{% endblock %} | |||||
{% block page_sidebar %} | |||||
{% include "templates/pages/web_sidebar.html" %} | |||||
{% endblock %} | |||||
{% block style %} | |||||
{% endblock %} | |||||
{% block page_content %} | |||||
<!-- no-cache --> | |||||
<div class='padding'></div> | |||||
{% if app %} | |||||
<h4>{{ app.app_name }}</h4> | |||||
<div class="web-list-item"> | |||||
<div class="row"> | |||||
<div class="col-xs-12"> | |||||
<div class="well"> | |||||
<div class="text-muted">{{ _("This will log out {0} from all other devices".format(app.app_name)) }}</div> | |||||
<div class="padding"></div> | |||||
<div class="text-right"> | |||||
<button class="btn btn-default" onclick="location.href = '/third_party_apps';">Cancel</button> | |||||
<button class="btn btn-danger btn-delete-app" data-client_id="{{ app.client_id }}">Revoke</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
{% elif apps|length > 0 %} | |||||
<h4>{{ _("Active Sessions") }}</h4> | |||||
{% for app in apps %} | |||||
<div class="web-list-item"> | |||||
<div class="row"> | |||||
<div class="col-xs-6"> | |||||
{{ app.app_name }} | |||||
</div> | |||||
<div class="col-xs-4 text-right text-muted"> | |||||
<small class="text-right"> | |||||
{{ _("logged in") }} {{ frappe.utils.pretty_date(app.creation) }} | |||||
</small> | |||||
</div> | |||||
<div class="col-xs-2 text-right small text-muted"> | |||||
<a class="btn btn-xs btn-link" href="/third_party_apps?app={{ app.name }}">{{ _("Revoke") }}</a> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
{% endfor %} | |||||
{% else %} | |||||
<div class="text-center text-muted"> | |||||
{{ _("No Active Sessions")}} | |||||
</div> | |||||
{% endif %} | |||||
<div class="padding"></div> | |||||
<script> | |||||
{% include "templates/includes/integrations/third_party_apps.js" %} | |||||
</script> | |||||
{% endblock %} |
@@ -0,0 +1,53 @@ | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
from frappe import _ | |||||
import frappe.www.list | |||||
no_cache = 1 | |||||
no_sitemap = 1 | |||||
def get_context(context): | |||||
if frappe.session.user == 'Guest': | |||||
frappe.throw(_("You need to be logged in to access this page"), frappe.PermissionError) | |||||
active_tokens = frappe.get_all("OAuth Bearer Token", | |||||
filters=[["user", "=", frappe.session.user]], | |||||
fields=["client"], distinct=True, order_by="creation") | |||||
client_apps = [] | |||||
for token in active_tokens: | |||||
creation = get_first_login(token.client) | |||||
app = { | |||||
"name": token.get("client"), | |||||
"app_name": frappe.db.get_value("OAuth Client", token.get("client"), "app_name"), | |||||
"creation": creation | |||||
} | |||||
client_apps.append(app) | |||||
app = None | |||||
if (frappe.form_dict.has_key("app")): | |||||
app = frappe.get_doc("OAuth Client", frappe.form_dict.app) | |||||
app = app.__dict__ | |||||
app["client_secret"] = None | |||||
if app: | |||||
context.app = app | |||||
context.apps = client_apps | |||||
context.show_sidebar = True | |||||
def get_first_login(client): | |||||
login_date = frappe.get_all("OAuth Bearer Token", | |||||
filters=[["user", "=", frappe.session.user], ["client", "=", client]], | |||||
fields=["creation"], order_by="creation", limit=1) | |||||
login_date = login_date[0].get("creation") if login_date and len(login_date) > 0 else None | |||||
return login_date | |||||
@frappe.whitelist() | |||||
def delete_client(client_id): | |||||
active_client_id_tokens = frappe.get_all("OAuth Bearer Token", filters=[["user", "=", frappe.session.user], ["client","=", client_id]]) | |||||
for token in active_client_id_tokens: | |||||
frappe.delete_doc("OAuth Bearer Token", token.get("name"), ignore_permissions=True) |