[Bf-extensions-cvs] [e16c55a1] master: BlenderKit: Oauth browser login. No more hassle with API keys for the users. Needs testing. renamed get_bkit_url to get_api_url task queue is a new simple module to do tasks, more timed tasks should come here instead of being in assetbar as of now. (download, search checks and more)

Vilem Duha noreply at git.blender.org
Sun May 19 16:02:54 CEST 2019


Commit: e16c55a110a5cde3bf5c83a156195b5d71481488
Author: Vilem Duha
Date:   Thu Apr 25 22:35:26 2019 +0200
Branches: master
https://developer.blender.org/rBAe16c55a110a5cde3bf5c83a156195b5d71481488

BlenderKit: Oauth browser login. No more hassle with API keys for the users. Needs testing.
renamed get_bkit_url to get_api_url
task queue is a new simple module to do tasks, more timed tasks should come here instead of being in assetbar as of now. (download, search checks and more)

===================================================================

M	blenderkit/__init__.py
M	blenderkit/categories.py
M	blenderkit/download.py
A	blenderkit/oauth.py
M	blenderkit/paths.py
M	blenderkit/ratings.py
M	blenderkit/search.py
A	blenderkit/tasks_queue.py
M	blenderkit/ui_panels.py
M	blenderkit/upload.py
M	blenderkit/upload_bg.py
M	blenderkit/utils.py

===================================================================

diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py
index 0bafa699..6a8b1886 100644
--- a/blenderkit/__init__.py
+++ b/blenderkit/__init__.py
@@ -41,9 +41,11 @@ if "bpy" in locals():
     importlib.reload(bg_blender)
     importlib.reload(paths)
     importlib.reload(utils)
+    importlib.reload(oauth)
+    importlib.reload(tasks_queue)
 else:
     from blenderkit import asset_inspector, search, download, upload, ratings, autothumb, ui, bg_blender, paths, utils, \
-        overrides, ui_panels, categories
+        overrides, ui_panels, categories, oauth, tasks_queue
 
 import os
 import math
@@ -71,7 +73,6 @@ from bpy.types import (
     PropertyGroup,
 )
 
-
 # logging.basicConfig(filename = 'blenderkit.log', level = logging.INFO,
 #                     format = '	%(asctime)s:%(filename)s:%(funcName)s:%(lineno)d:%(message)s')
 
@@ -1208,9 +1209,8 @@ class BlenderKitAddonPreferences(AddonPreferences):
     # this must match the addon name, use '__package__'
     # when defining this in a submodule of a python package.
     bl_idname = __name__
-    from os.path import expanduser
-    home = expanduser("~")
-    default_global_dict = home + os.sep + 'blenderkit_data'
+
+    default_global_dict = paths.default_global_dict()
 
     api_key: StringProperty(
         name="BlenderKit API Key",
@@ -1220,6 +1220,20 @@ class BlenderKitAddonPreferences(AddonPreferences):
         update=utils.save_prefs
     )
 
+    api_key_refresh: StringProperty(
+        name="BlenderKit refresh API Key",
+        description="API key used to refresh the token regularly.",
+        default="",
+        subtype="PASSWORD",
+        update=utils.save_prefs
+    )
+
+    login_attempt: BoolProperty(
+        name="Login/Signup attempt",
+        description="When this is on, BlenderKit is trying to connect and login.",
+        default=False
+    )
+
     global_dir: StringProperty(
         name="Global Files Directory",
         description="Global storage for your assets, will use subdirectories for the contents",
@@ -1389,6 +1403,8 @@ def register():
     bpy.app.handlers.load_post.append(scene_load)
     utils.load_prefs()
     overrides.register_overrides()
+    oauth.register()
+    tasks_queue.register()
 
 
 def unregister():
@@ -1403,6 +1419,8 @@ def unregister():
     ui_panels.unregister_ui_panels()
     bg_blender.unregister()
     overrides.unregister_overrides()
+    oauth.unregister()
+    tasks_queue.unregister()
 
     del bpy.types.Scene.blenderkit_models
     del bpy.types.Scene.blenderkit_scene
diff --git a/blenderkit/categories.py b/blenderkit/categories.py
index 3c50f2c7..e2dfed3d 100644
--- a/blenderkit/categories.py
+++ b/blenderkit/categories.py
@@ -60,7 +60,7 @@ def copy_categories():
 def fetch_categories(API_key):
     BLENDERKIT_API_MAIN = "https://www.blenderkit.com/api/v1/"
 
-    url = paths.get_bkit_url() + 'categories/'
+    url = paths.get_api_url() + 'categories/'
 
     headers = utils.get_headers(API_key)
 
diff --git a/blenderkit/download.py b/blenderkit/download.py
index 175a9771..bffb2e8a 100644
--- a/blenderkit/download.py
+++ b/blenderkit/download.py
@@ -174,7 +174,7 @@ def report_usages():
     api_key = user_preferences.api_key
     sid = get_scene_id()
     headers = utils.get_headers(api_key)
-    url = paths.get_bkit_url() + paths.BLENDERKIT_REPORT_URL
+    url = paths.get_api_url() + paths.BLENDERKIT_REPORT_URL
 
     assets = {}
     asset_obs = []
diff --git a/blenderkit/oauth.py b/blenderkit/oauth.py
new file mode 100644
index 00000000..78c8dfb3
--- /dev/null
+++ b/blenderkit/oauth.py
@@ -0,0 +1,176 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+import bpy
+
+import json
+import webbrowser
+from http.server import BaseHTTPRequestHandler, HTTPServer
+from urllib.parse import parse_qs, urlparse
+
+import requests
+import threading
+from blenderkit import tasks_queue, utils, paths
+
+CLIENT_ID = "IdFRwa3SGA8eMpzhRVFMg5Ts8sPK93xBjif93x0F"
+PORTS = [62485, 1234]
+
+
+class SimpleOAuthAuthenticator(object):
+    def __init__(self, server_url, client_id, ports):
+        self.server_url = server_url
+        self.client_id = client_id
+        self.ports = ports
+
+    def _get_tokens(self, authorization_code=None, refresh_token=None, grant_type="authorization_code"):
+        data = {
+            "grant_type": grant_type,
+            "state": "random_state_string",
+            "client_id": self.client_id,
+            "scopes": "read write",
+        }
+        if authorization_code:
+            data['code'] = authorization_code
+        if refresh_token:
+            data['refresh_token'] = refresh_token
+
+        response = requests.post(
+            '%s/o/token/' % self.server_url,
+            data=data
+        )
+        if response.status_code != 200:
+            return None, None
+        refresh_token = json.loads(response.content)['refresh_token']
+        access_token = json.loads(response.content)['access_token']
+        return access_token, refresh_token
+
+    def get_new_token(self):
+        class HTTPServerHandler(BaseHTTPRequestHandler):
+            def do_GET(self):
+                self.send_response(200)
+                self.send_header('Content-type', 'text/html')
+                self.end_headers()
+                if 'code' in self.path:
+                    self.auth_code = self.path.split('=')[1]
+                    # Display to the user that they no longer need the browser window
+                    self.wfile.write(bytes('<html><h1>You may now close this window.</h1></html>', 'utf-8'))
+                    qs = parse_qs(urlparse(self.path).query)
+                    self.server.authorization_code = qs['code'][0]
+
+        for port in self.ports:
+            try:
+                httpServer = HTTPServer(('localhost', port), HTTPServerHandler)
+            except OSError:
+                continue
+            break
+        webbrowser.open_new(
+            "%s/o/authorize?client_id=%s&state=random_state_string&response_type=code&"
+            "redirect_uri=http://localhost:%s/consumer/exchange/" % (self.server_url, self.client_id, port),
+        )
+
+        httpServer.handle_request()
+        authorization_code = httpServer.authorization_code
+        return self._get_tokens(authorization_code=authorization_code)
+
+    def get_refreshed_token(self, refresh_token):
+        return self._get_tokens(refresh_token=refresh_token, grant_type="refresh_token")
+
+
+def login_thread():
+    thread = threading.Thread(target=login, args=([]), daemon=True)
+    thread.start()
+
+
+def login():
+    authenticator = SimpleOAuthAuthenticator(server_url=paths.get_bkit_url(), client_id=CLIENT_ID, ports=PORTS)
+    auth_token, refresh_token = authenticator.get_new_token()
+    print('tokens retrieved')
+    tasks_queue.tasks_queue.put('blenderkit.oauth.write_tokens("%s", "%s")' % (auth_token, refresh_token))
+
+
+def refresh_token_thread():
+    preferences = bpy.context.preferences.addons['blenderkit'].preferences
+    if len(preferences.api_key_refresh) > 0:
+        thread = threading.Thread(target=refresh_token, args=([preferences.api_key_refresh]), daemon=True)
+        thread.start()
+
+
+def refresh_token(api_key_refresh):
+    authenticator = SimpleOAuthAuthenticator(server_url=paths.get_bkit_url(), client_id=CLIENT_ID, ports=PORTS)
+    auth_token, refresh_token = authenticator.get_refreshed_token(api_key_refresh)
+    tasks_queue.tasks_queue.put('blenderkit.oauth.write_tokens("%s", "%s")' % (auth_token, refresh_token))
+
+
+def write_tokens(auth_token, refresh_token):
+    print('writing tokens?')
+    preferences = bpy.context.preferences.addons['blenderkit'].preferences
+    preferences.api_key = auth_token
+    preferences.api_key_refresh = refresh_token
+    preferences.login_attempt = False
+    props = utils.get_search_props()
+    props.report = 'Login success!'
+
+
+class RegisterLoginOnline(bpy.types.Operator):
+    """Bring linked object hierarchy to scene and make it editable."""
+
+    bl_idname = "wm.blenderkit_login"
+    bl_label = "BlenderKit login or signup"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    @classmethod
+    def poll(cls, context):
+        return True
+
+    def execute(self, context):
+        preferences = bpy.context.preferences.addons['blenderkit'].preferences
+        preferences.login_attempt = True
+        login_thread()
+        return {'FINISHED'}
+
+
+class CancelLoginOnline(bpy.types.Operator):
+    """Cancel login attempt."""
+
+    bl_idname = "wm.blenderkit_login_cancel"
+    bl_label = "BlenderKit login cancel"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    @classmethod
+    def poll(cls, context):
+        return True
+
+    def execute(self, context):
+        preferences = bpy.context.preferences.addons['blenderkit'].preferences
+        preferences.login_attempt = False
+        return {'FINISHED'}
+
+classess = (
+    RegisterLoginOnline,
+    CancelLoginOnline,
+)
+
+
+def register():
+    for c in classess:
+        bpy.utils.register_class(c)
+
+
+def unregister():
+    for c in classess:
+        bpy.utils.unregister_class(c)
diff --git a/blenderkit/paths.py b/blenderkit/paths.py
index 71479c7e..1595ad78 100644
--- a/blenderkit/paths.py
+++ b/blenderkit/paths.py
@@ -18,9 +18,10 @@
 
 import bpy, os, sys
 
-BLENDERKIT_API_LOCAL = "http://localhost:8001/api/v1/"
-BLENDERKIT_API_MAIN = "https://www.blenderkit.com/api/v1/"
-BLENDERKIT_API_DEVEL = "https://devel.blenderk

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list