diff --git a/README.rst b/README.rst index 16d0aff..fab1e5e 100644 --- a/README.rst +++ b/README.rst @@ -27,61 +27,12 @@ Library contains adapters for usage from Django Web Framework and Flask Web Fram .. _PHP tool: https://github.com/IMSGlobal/lti-1-3-php-library -Example -======= +Usage examples +============== -First of all choose and configure test LTI 1.3 Platform. It may be: +Django: https://github.com/dmitry-viskov/pylti1.3-django-example -* `IMS Global test site`_ -* `Canvas LMS`_ (Detailed `instruction`_ how to setup Canvas as LTI 1.3 Platform is `here`_) - -.. _IMS Global test site: https://lti-ri.imsglobal.org -.. _Canvas LMS: https://github.com/instructure/canvas-lms -.. _instruction: https://github.com/dmitry-viskov/pylti1.3/wiki/Configure-Canvas-as-LTI-1.3-Platform -.. _here: https://github.com/dmitry-viskov/pylti1.3/wiki/Configure-Canvas-as-LTI-1.3-Platform - -The most simple way to check example is to use ``docker`` + ``docker-compose``. -Change the necessary configs in the ``examples/configs/game.json`` (`here is instruction`_ how to generate your own public + private keys): - -.. _here is instruction: https://github.com/dmitry-viskov/pylti1.3/wiki/How-to-generate-JWT-RS256-key-and-JWKS - -.. code-block:: javascript - - { - "" : { // This will usually look something like 'http://example.com' - "client_id" : "", // This is the id received in the 'aud' during a launch - "auth_login_url" : "", // The platform's OIDC login endpoint - "auth_token_url" : "", // The platform's service authorization endpoint - "key_set_url" : "", // The platform's JWKS endpoint - "key_set": null, // in case if platform's JWKS endpoint somehow unavailable you may paste JWKS here - "private_key_file" : "", // Relative path to the tool's private key - "deployment_ids" : [""] // The deployment_id passed by the platform during launch - } - } - -and execute: - -.. code-block:: shell - - $ docker-compose up --build - -You may use virtualenv instead of docker: - -.. code-block:: shell - - $ virtualenv venv - $ source venv/bin/activate - $ cd examples - $ pip install -r requirements.txt - $ cd game - $ python manage.py runserver 127.0.0.1:9001 - -Now there is game example tool you can launch into on the port 9001: - -.. code-block:: shell - - OIDC Login URL: http://127.0.0.1:9001/login/ - LTI Launch URL: http://127.0.0.1:9001/launch/ +Flask: https://github.com/dmitry-viskov/pylti1.3-flask-example Configuration ============= diff --git a/examples/Dockerfile b/examples/Dockerfile deleted file mode 100644 index 636b152..0000000 --- a/examples/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM python:3.6.6-alpine3.7 - -RUN apk add --update \ - build-base libffi-dev openssl-dev \ - xmlsec xmlsec-dev \ - && rm -rf /var/cache/apk/* - -ADD requirements.txt /tmp -RUN pip install -r /tmp/requirements.txt - -EXPOSE 9001 -CMD python manage.py runserver 0.0.0.0:9001 diff --git a/examples/README.rst b/examples/README.rst new file mode 100644 index 0000000..d2d9318 --- /dev/null +++ b/examples/README.rst @@ -0,0 +1,7 @@ +Examples +========= + +Django: https://github.com/dmitry-viskov/pylti1.3-django-example + +Flask: https://github.com/dmitry-viskov/pylti1.3-flask-example + diff --git a/examples/configs/cert_suite_private.key b/examples/configs/cert_suite_private.key deleted file mode 100644 index c2170cc..0000000 --- a/examples/configs/cert_suite_private.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAsW3eobPIj5LsyHcMGckVSSC621uL+0zkeMoWfXfNmvTH+zt5 -WOeEIdz+X7fK+F+lO7ic5WdJEGmp9/cjAf0Z6SsmnvvHlHV/xsWtJm4DiuuF2MAa -hRQ5QEkhaEdh5QM2vAYyc8Nfxe504vA3czuynrW9MsOdZHeVzF+zWhhEl+olC5fW -A1rhTUPpdxuZ0opVIrGJtI/QYfndoN+7zTs/4CXqG6WpB+AZio8j7c6fJLC7J33c -pxB1+O+64Qbh+5sxz46cEByboAB8qerYCmcfxxfBbwyySBBK5X77aNHWA01B1kpO -Q2VB8YKQk+OrXsPgJobPkR9ONWa9DC9JjEdUJwIDAQABAoIBAQCAA+qutt2NIY/v -71zuudO+yHupSzsLXOY3dG+XpTnWhKhJTxb1m00Ndbqe6yfp3nCET2X8anIgAmzc -+RXsGGZ6gmTCLp1IMyK3EuckJBowQFB5G9nGjNnl1R3idCZgqtnx/XKnbZ6LW8o/ -9tu7K6ZrtmrE1riXxWRyadYoufu7ssNTqtj03oh3Tvw+Ze6xvF6hpaxnbVHxJcGt -xZO51L6rGOSFq5CJ81BswyBDOKB/Z2OC0o3m2t4ZF4/2Lf070sB7RoejGD7mhYVe -lEOoC95C14hfcspzmDEb8I/n0MvAxlwddM4KZRilAJ+e2R0rM9M1MnyYsmYUsMNX -EKWcx+/5AoGBAOLtNVbIohpY5kbX4WREJ/0INPbbx0gf68ozEZTjsOzIP7oaIzry -URmxyZzSpx446QCO8s26vuxrPGm7OAteNS7UpDdunzKsaIlZScZQEpE9htp3MKKw -KXaA4l7H55uWWnaUAcDqjEdybhYL6SbPKhOaK53VeHOLro900FiRnfaDAoGBAMgp -O8GwAI3LbD06Fn+DT+3hj/i8wxbWilgJlI+RU+wWfQ421jMKv2dck8zbnzKGxEwA -3WPh6gGMlkavEZ95d0qZ/TOkSh+VIjJuOrjcckRcrKcycYJJUzreO7ENsFbA+8xL -Qp2gNV+NntiChzSUGY5Nup3keoaT9iV13oYDSdqNAoGARDn9Z3I7CqDf2zzcz0CO -pUzqX64EZHL0eX6RMqqibw5l2pYxMW/ZYlhJvZS4GiYSJ9DSv3f+Hya+qytW1lQk -uUfFd8USqDGd3G2z+KPqcTCGcviS7tb4IGDvrn976xNxb2VggZgDRRfqcUZzeu+e -PvaDVpjv9g1xFkCQw5BEZfECgYBcSB5jywhGV14c0FYlDd5g9xiQfj6XnewEcM5M -bp05gJjBX+jbeX4LYnRGA49fFSEVRWTMsxBXDIEQL5C5bJ/iBiLllz4RV4l/pLBw -IDqSaAO1xhztC29S+bidhYkiRjEQ3DXnREC3QCzW9z7sr8ckg5OhTgBrYXYfiTtB -n+yB1QKBgG/J+WhkqMEtZ8CgdoiTIqYKmFsLvl07wETAVU6Nv1sEI+jnhyug0QtQ -yLAlBOVyrXuJ1DZMX6hTRij4L0jvnJFSq0Sv8COuLIH90xdq/NTNQ3LAy60l/3b1 -ojAnnRJORDegdJjCBxJ59Fch6Qfd+e8742DVsJu8zVo2garUVMH3 ------END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/examples/configs/game.json b/examples/configs/game.json deleted file mode 100644 index 547ef1f..0000000 --- a/examples/configs/game.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "http://imsglobal.org": { - "client_id": "pytest12345", - "auth_login_url": "https://lti-ri.imsglobal.org/platforms/370/authorizations/new", - "auth_token_url": "https://lti-ri.imsglobal.org/platforms/370/access_tokens", - "key_set_url": "https://lti-ri.imsglobal.org/platforms/370/platform_keys/361.json", - "key_set": { - "keys": [ - { - "kty": "RSA", - "e": "AQAB", - "n": "r3WB5ECKptJliYft6F_XJysCy1KevoGJgKNHgdVR20lplUv1SzRH1mifzOmEzxWM0kj6blS7SRxK9GFGs6optHAmzcb6_joegKzLHSj14RRVSoI0MgyltJcAl8z6d4yZ9KobV8OvpICnMgsGO20Wih-Cq-oSUjtJT7WET3GZmzmM9MzamiGsCtC0dUWdDOW1FOMzTt8et9YA5jOfkLdJdPyZ5mdUZjBkYMlDGoD8fPRPdS9M-uczxvUeuKvyy1BVGlu5AG0xy-wN1tKjSE1iuC5Kkm39CZwQXBRpStDExWw_ApzP40SK3CKez4ls3jjkE3i4CpJSgLn1D8rT6wOpJw", - "kid": "uhMfBQzVLmaJNU9c1am2X9pTzcEYhgYL2hO6hbYAvdw", - "alg": "RS256", - "use": "sig" - } - ] - }, - "private_key_file": "private.key", - "deployment_ids": ["py1234"] - }, - "https://canvas.instructure.com": { - "client_id": "10000000000004", - "auth_login_url": "http://canvas.docker/api/lti/authorize_redirect", - "auth_token_url": "http://canvas.docker/login/oauth2/token", - "key_set_url": "http://canvas.docker/api/lti/security/jwks", - "key_set": null, - "private_key_file": "private.key", - "deployment_ids": ["6:8865aa05b4b79b64a91a86042e43af5ea8ae79eb"] - }, - "ltiadvantagevalidator.imsglobal.org": { - "client_id": "imstestuser", - "auth_login_url": "https://ltiadvantagevalidator.imsglobal.org/ltitool/oidcauthurl.html", - "auth_token_url": "https://oauth2server.imsglobal.org/oauth2server/authcodejwt", - "key_set_url": "https://oauth2server.imsglobal.org/jwks", - "key_set": null, - "private_key_file": "cert_suite_private.key", - "deployment_ids": ["testdeploy"] - } -} diff --git a/examples/configs/private.key b/examples/configs/private.key deleted file mode 100644 index 71e59b7..0000000 --- a/examples/configs/private.key +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/examples/configs/public.jwk.json b/examples/configs/public.jwk.json deleted file mode 100644 index 60a52af..0000000 --- a/examples/configs/public.jwk.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "e": "AQAB", - "n": "uvEnCaUOy1l9gk3wjW3Pib1dBc5g92-6rhvZZOsN1a77fdOqKsrjWG1lDu8kq2nL-wbAzR3DdEPVw_1WUwtr_Q1d5m-7S4ciXT63pENs1EPwWmeN33O0zkGx8I7vdiOTSVoywEyUZe6UyS-ujLfsRc2ImeLP5OHxpE1yULEDSiMLtSvgzEaMvf2AkVq5EL5nLYDWXZWXUnpiT_f7iK47Mp2iQd4KYYG7YZ7lMMPCMBuhej7SOtZQ2FwaBjvZiXDZ172sQYBCiBAmOR3ofTL6aD2-HUxYztVIPCkhyO84mQ7W4BFsOnKW4WRfEySHXd2hZkFMgcFNXY3dA6de519qlcrL0YYx8ZHpzNt0foEzUsgJd8uJMUVvzPZgExwcyIbv5jWYBg0ILgULo7ve7VXG5lMwasW_ch2zKp7tTILnDJwITMjF71h4fn4dMTun_7MWEtSl_iFiALnIL_4_YY717cr4rmcG1424LyxJGRD9L9WjO8etAbPkiRFJUd5fmfqjHkO6fPxyWsMUAu8bfYdVRH7qN_erfGHmykmVGgH8AfK9GLT_cjN4GHA29bK9jMed6SWdrkygbQmlnsCAHrw0RA-QE0t617h3uTrSEr5vkbLz-KThVEBfH84qsweqcac_unKIZ0e2iRuyVnG4cbq8HUdio8gJ62D3wZ0UvVgr4a0", - "alg": "RS256", - "kid": "NtQYzsKs_TWLQ0p3bLmfM7fOwY0nEBVVH3z3Q-zJ06Y", - "kty": "RSA", - "use": "sig" -} diff --git a/examples/configs/public.key b/examples/configs/public.key deleted file mode 100644 index 9109ee3..0000000 --- a/examples/configs/public.key +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuvEnCaUOy1l9gk3wjW3P -ib1dBc5g92+6rhvZZOsN1a77fdOqKsrjWG1lDu8kq2nL+wbAzR3DdEPVw/1WUwtr -/Q1d5m+7S4ciXT63pENs1EPwWmeN33O0zkGx8I7vdiOTSVoywEyUZe6UyS+ujLfs -Rc2ImeLP5OHxpE1yULEDSiMLtSvgzEaMvf2AkVq5EL5nLYDWXZWXUnpiT/f7iK47 -Mp2iQd4KYYG7YZ7lMMPCMBuhej7SOtZQ2FwaBjvZiXDZ172sQYBCiBAmOR3ofTL6 -aD2+HUxYztVIPCkhyO84mQ7W4BFsOnKW4WRfEySHXd2hZkFMgcFNXY3dA6de519q -lcrL0YYx8ZHpzNt0foEzUsgJd8uJMUVvzPZgExwcyIbv5jWYBg0ILgULo7ve7VXG -5lMwasW/ch2zKp7tTILnDJwITMjF71h4fn4dMTun/7MWEtSl/iFiALnIL/4/YY71 -7cr4rmcG1424LyxJGRD9L9WjO8etAbPkiRFJUd5fmfqjHkO6fPxyWsMUAu8bfYdV -RH7qN/erfGHmykmVGgH8AfK9GLT/cjN4GHA29bK9jMed6SWdrkygbQmlnsCAHrw0 -RA+QE0t617h3uTrSEr5vkbLz+KThVEBfH84qsweqcac/unKIZ0e2iRuyVnG4cbq8 -HUdio8gJ62D3wZ0UvVgr4a0CAwEAAQ== ------END PUBLIC KEY----- diff --git a/examples/docker-compose.yml b/examples/docker-compose.yml deleted file mode 100644 index a612699..0000000 --- a/examples/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '3' -services: - game: - restart: always - container_name: pylti1p3 - build: . - stdin_open: true - tty: true - volumes: - - ./configs:/configs - - ./game:/game - - ../pylti1p3:/pylti1p3 - working_dir: /game - ports: - - "9001:9001" diff --git a/examples/game/game/__init__.py b/examples/game/game/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/game/game/middleware.py b/examples/game/game/middleware.py deleted file mode 100644 index c29ed96..0000000 --- a/examples/game/game/middleware.py +++ /dev/null @@ -1,22 +0,0 @@ -import django -from django.conf import settings -from django.utils.deprecation import MiddlewareMixin - - -class SameSiteMiddleware(MiddlewareMixin): - - def process_response(self, request, response): - django_support_samesite_none = django.VERSION[0] > 3 \ - or (django.VERSION[0] == 3 and django.VERSION[1] >= 1) - if request.is_secure() and not django_support_samesite_none: - session_cookie_samesite = getattr(settings, 'SESSION_COOKIE_SAMESITE', None) - csrf_cookie_samesite = getattr(settings, 'CSRF_COOKIE_SAMESITE', None) - - session_cookie_name = getattr(settings, 'SESSION_COOKIE_NAME', None) - csrf_cookie_name = getattr(settings, 'CSRF_COOKIE_NAME', None) - - if session_cookie_samesite is None and session_cookie_name in response.cookies: - response.cookies[session_cookie_name]['samesite'] = 'None' - if csrf_cookie_samesite is None and csrf_cookie_name in response.cookies: - response.cookies[csrf_cookie_name]['samesite'] = 'None' - return response diff --git a/examples/game/game/settings.py b/examples/game/game/settings.py deleted file mode 100644 index cb740a0..0000000 --- a/examples/game/game/settings.py +++ /dev/null @@ -1,115 +0,0 @@ -import os - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'mn*=d18zwp+n%)+in=7!zp4x)he+%y6+0$e3&!c&keg%&q!%z&' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = ['*'] - -INTERNAL_IPS = ['127.0.0.1'] - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'game', - 'debug_toolbar', -] - -MIDDLEWARE = [ - 'game.middleware.SameSiteMiddleware', - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'debug_toolbar.middleware.DebugToolbarMiddleware', -] - -ROOT_URLCONF = 'game.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(BASE_DIR, 'templates'), - ], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -WSGI_APPLICATION = 'game.wsgi.application' - -SESSION_COOKIE_NAME = 'sessionid' -SESSION_COOKIE_SAMESITE = None # should be set as 'None' for Django >= 3.1 -SESSION_COOKIE_SECURE = False # should be True in case of HTTPS usage (production) - -# Database -# https://docs.djangoproject.com/en/1.11/ref/settings/#databases - -DATABASES = {} - -CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - } -} - -SESSION_ENGINE = "django.contrib.sessions.backends.cache" - -# Password validation -# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/1.11/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.11/howto/static-files/ - -STATIC_URL = '/static/' diff --git a/examples/game/game/static/breakout.css b/examples/game/game/static/breakout.css deleted file mode 100644 index bc966f0..0000000 --- a/examples/game/game/static/breakout.css +++ /dev/null @@ -1,45 +0,0 @@ -body { - font-family: 'Roboto', sans-serif; -} -#scoreboard { - border: solid 1px #000; - border-left: none; - border-top-right-radius: 20px; - border-bottom-right-radius: 20px; - padding-bottom: 12px; - background: linear-gradient(to bottom, rgb(0, 0, 0), rgb(0, 0, 50) 500px); - color: white; -} -th, td { - color: white; -} -.dl-config { - font-family: Verdana, Geneva, Tahoma, sans-serif; - display: block; - width: 400px; - position: absolute; -} -.dl-config h1 { - text-align: center; -} -.dl-config ul { - list-style: none; - padding: 0; -} -.dl-config ul li a { - display: block; - width: 200px; - height:50px; - text-align: center; - border: black solid 1px; - border-radius: 15px; - margin:auto; - text-decoration: none; - color: black; - margin-top:8px; - font-size: 30px; - line-height: 46px; -} -.dl-config ul li a:hover { - background-color: #dddddd; -} diff --git a/examples/game/game/static/breakout.js b/examples/game/game/static/breakout.js deleted file mode 100644 index d71bf61..0000000 --- a/examples/game/game/static/breakout.js +++ /dev/null @@ -1,466 +0,0 @@ -function mainGame() { - var c = document.getElementById("breakout"); - var ctx = c.getContext("2d"); - - var bgctx = document.getElementById("breakoutbg").getContext("2d"); - bgctx.beginPath(); - var bggrad = ctx.createLinearGradient(0, 0, 0, c.height); - bggrad.addColorStop(0, "rgb(0, 0, 0)"); - bggrad.addColorStop(1, "rgb(0, 0, 50)"); - bgctx.fillStyle = bggrad; - bgctx.rect(0, 0, c.width, c.height); - bgctx.fill(); - bgctx.font = "10px Gugi"; - bgctx.fillStyle = '#FFFFFF'; - bgctx.textAlign = "left"; - bgctx.fillText("Powered By Turnitin", 6, c.height - 6); - - var score = 0; - - var difficulty = { - hard: { - speed_multiplier: 1.5 - }, - normal: { - speed_multiplier: 1 - }, - easy: { - speed_multiplier: 0.7 - }, - }; - - var ball = { - pos: { - x: c.width / 2 - 200, - y: c.height / 2 - 2, - }, - vel: { - x: 6 * difficulty[currDiff]['speed_multiplier'], - y: 6 * difficulty[currDiff]['speed_multiplier'], - }, - r: 10, - rot: 0, - velr: 0, - render: function () { - this.pos.x += this.vel.x; - this.pos.y += this.vel.y; - if (this.oob(c.width, this.pos.x, this.r)) { - this.vel.x = -this.vel.x; - this.pos.x += this.vel.x; - this.pos.y -= this.vel.y; - } - if (this.oob(c.height, this.pos.y, this.r)) { - if (this.pos.y > c.height - this.r) { - endGame(); - } - this.vel.y = -this.vel.y; - this.pos.y += this.vel.y; - this.pos.x -= this.vel.x; - } - - ctx.save(); - ctx.beginPath(); - var gradient = ctx.createRadialGradient(this.pos.x, this.pos.y, 2, this.pos.x, this.pos.y, 10); - ctx.fillStyle = "rgb(255, 232, 102)"; - ctx.strokeStyle = "rgb(255, 232, 102)"; - ctx.setLineDash([5, 5]); - ctx.lineWidth = 4; - ctx.translate(this.pos.x, this.pos.y); - ctx.rotate(this.rot * Math.PI); - ctx.arc(0, 0, this.r, 0, 2 * Math.PI); - if (this.vel.x > 0) { - this.velr = 0.01; - } else if (this.vel.x < 0) { - this.velr = -0.01; - } else { - this.velr = 0; - } - this.rot += this.velr; - ctx.fill(); - ctx.stroke(); - ctx.restore(); - - }, - oob: function (max, curr, offset) { - if (curr < offset || curr > (max - offset)) { - return true; - } - }, - left: function () { - return this.pos.x - this.r; - }, - right: function () { - return this.pos.x + this.r; - }, - top: function () { - return this.pos.y - this.r; - }, - bottom: function () { - return this.pos.y + this.r; - }, - }; - - var paddle = { - pos: { - x: c.width / 2 + 2, - y: c.height - 40, - }, - width: 80, - height: 20, - render: function () { - ctx.beginPath(); - var gradient = ctx.createLinearGradient(this.pos.x, this.pos.y, this.pos.x, this.pos.y + this.height); - gradient.addColorStop(0, "#999999"); - gradient.addColorStop(0.7, "#eeeeee"); - gradient.addColorStop(1, "#999999"); - ctx.fillStyle = gradient; - var hh = this.height / 2; - ctx.arc(this.pos.x + hh, this.pos.y + hh, hh, 0.5 * Math.PI, 1.5 * Math.PI); - ctx.rect(this.pos.x + hh, this.pos.y, this.width - this.height, this.height); - ctx.arc(this.pos.x + this.width - hh, this.pos.y + hh, hh, 1.5 * Math.PI, 0.5 * Math.PI); - ctx.fill(); - ctx.stroke(); - }, - left: function () { - return this.pos.x; - }, - right: function () { - return this.pos.x + this.width; - }, - top: function () { - return this.pos.y; - }, - bottom: function () { - return this.pos.y + this.height; - }, - test_hit: function () { - var hitx = this.test_hit_x(); - var hity = this.test_hit_y(); - if (!hitx || !hity) { - return 0; - } - if (hity) { - ball.vel.y = -Math.abs(ball.vel.y); - ball.pos.y += ball.vel.y; - ball.pos.x -= ball.vel.x; - } - if (hitx) { - var xdiff = ball.pos.x - (this.pos.x + (this.width / 2)); - ball.vel.x = (xdiff > 0 ? Math.ceil(xdiff / 5) : Math.floor(xdiff / 5)) * difficulty[currDiff]['speed_multiplier']; - ball.pos.x += ball.vel.x; - } - return 1; - }, - test_hit_x: function () { - if (this.left() > ball.right()) { - return 0; - } - if (this.right() < ball.left()) { - return 0; - } - return 1; - }, - test_hit_y: function () { - if (this.top() > ball.bottom()) { - return 0; - } - if (this.bottom() < ball.top()) { - return 0; - } - return 1; - }, - move: function () { - if (pressLeft) { - if (this.pos.x > 0) { - this.pos.x -= 8; - } - } - if (pressRight) { - if (this.pos.x < c.width - this.width) { - this.pos.x += 8; - } - } - } - }; - - function fire() { - this.r = 0; - this.a = 0; - this.render = function () { - if (this.a < 0.2) { - this.reset(); - } - - this.pos.x += this.vel.x + (Math.random() * 2) - 1; - this.pos.y += this.vel.y + (Math.random() * 2) - 1; - - this.r *= 0.95; - this.a *= 0.95; - - ctx.beginPath(); - ctx.fillStyle = 'rgba(' + (239 - this.green) + ', ' + this.green + ', 66,' + this.a + ')'; - ctx.arc(this.pos.x, this.pos.y, this.r, 0, 2 * Math.PI); - ctx.fill(); - - if (this.green < 232) { - this.green += 8; - } - }; - this.reset = function () { - this.pos = { - x: ball.pos.x, - y: ball.pos.y, - }; - this.vel = { - x: (Math.random() * 4) - 2, - y: (Math.random() * 4) - 2, - }; - this.r = (Math.random() * 5) + 1; - this.a = 0.9; - this.green = 62; - }; - } - - function brick() { - this.id = 0; - this.pos = { - x: 40, - y: 40, - }; - this.vely = 0; - this.rot = 0; - this.velr = 0; - this.hit = false; - this.last_hitx = false; - this.last_hity = false; - this.width = 40; - this.height = 20; - this.render = function () { - if (this.hit) { - if (this.pos.y > c.height + 60) { - return; - } - this.vely++; - this.pos.y += this.vely; - ctx.save(); - ctx.beginPath(); - this.rot += this.velr; - ctx.translate(this.pos.x + (this.width / 2), this.pos.y + (this.height / 2)); - ctx.rotate(this.rot * Math.PI); - var gradient = ctx.createRadialGradient(-(this.width / 2) + 10, -(this.height / 2) + 5, 0, -(this.width / 2) + 40, -(this.height / 2) + 15, 40); - gradient.addColorStop(0, 'rgba(137, 211, 234, 0.2)'); - gradient.addColorStop(1, 'rgba(137, 211, 234, 1)'); - ctx.strokeStyle = 'rgba(254, 254, 254, 0.8)'; - ctx.fillStyle = gradient; - ctx.rect(-(this.width / 2), -(this.height / 2), this.width, this.height); - ctx.fill(); - ctx.stroke(); - ctx.restore(); - return; - } - ctx.beginPath(); - var gradient = ctx.createRadialGradient(this.pos.x + 10, this.pos.y + 5, 0, this.pos.x + 40, this.pos.y + 15, 40); - gradient.addColorStop(0, 'rgba(137, 211, 234, 0.2)'); - gradient.addColorStop(1, 'rgba(137, 211, 234, 1)'); - ctx.strokeStyle = 'rgba(254, 254, 254, 0.8)'; - ctx.fillStyle = gradient; - ctx.rect(this.pos.x, this.pos.y, this.width, this.height); - ctx.fill(); - ctx.stroke(); - }; - this.test_hit = function () { - if (this.hit) { - return 0; - } - var hitx = this.test_hit_x(); - var hity = this.test_hit_y(); - if (!hitx || !hity) { - this.last_hitx = hitx; - this.last_hity = hity; - return 0; - } - if (this.last_hity) { - ball.vel.y = -ball.vel.y; - ball.pos.y += ball.vel.y; - ball.pos.x -= ball.vel.x; - } - if (this.last_hitx) { - ball.vel.x = -ball.vel.x; - ball.pos.x += ball.vel.x; - ball.pos.y -= ball.vel.y; - } - if (!this.last_hity && this.last_hitx) { - ball.vel.x = -ball.vel.x; - ball.pos.x += ball.vel.x; - ball.vel.y = -ball.vel.y; - ball.pos.y += ball.vel.y; - } - this.last_hitx = hitx; - this.last_hity = hity; - this.hit = true; - this.velr = (Math.random() * 0.04) - 0.02; - score++; - return 1; - }; - this.test_hit_x = function () { - if (this.left() > ball.right()) { - return 0; - } - if (this.right() < ball.left()) { - return 0; - } - return 1; - }; - this.test_hit_y = function () { - if (this.top() > ball.bottom()) { - return 0; - } - if (this.bottom() < ball.top()) { - return 0; - } - return 1; - }; - this.left = function () { - return this.pos.x; - }; - this.right = function () { - return this.pos.x + this.width; - }; - this.top = function () { - return this.pos.y; - }; - this.bottom = function () { - return this.pos.y + this.height; - }; - } - - var pressLeft = false; - var pressRight = false; - - document.addEventListener('keydown', (event) => { - const keyName = event.key; - if (keyName == "ArrowLeft") { - pressLeft = true; - } - if (keyName == "ArrowRight") { - pressRight = true; - } - }); - - document.addEventListener('keyup', (event) => { - const keyName = event.key; - if (keyName == "ArrowLeft") { - pressLeft = false; - } - if (keyName == "ArrowRight") { - pressRight = false; - } - if (keyName == " ") { - if (pause && !gameover) { - if (!startTime) { - startTime = Math.floor(Date.now() / 1000); - } - pause = false; - frame(); - } else { - pause = true; - } - } - }); - - var bricks = []; - - for (var h = 0; h < 6; h++) { - for (var w = 0; w < 18; w++) { - var brickid = (18 * h) + w; - bricks[brickid] = new brick(); - bricks[brickid].pos.x = 40 + (w * 40); - bricks[brickid].pos.y = 40 + (h * 20); - bricks[brickid].id = brickid; - } - } - var fires = []; - - for (var i = 0; i < 80; i++) { - fires[i] = new fire(); - } - var startFireCount = 1; - - var pause = true; - var gameover = false; - - var frame = function () { - if (score >= bricks.length) { - endGame(); - } - ctx.clearRect(0, 0, c.width, c.height); - for (var i = 0; i < bricks.length; i++) { - bricks[i].render(); - } - for (var i = 0; i < bricks.length; i++) { - if (bricks[i].test_hit()) { - break; - } - } - for (var i = 0; i < fires.length && i < startFireCount; i++) { - fires[i].render(); - } - if (startFireCount <= fires.length) { - startFireCount++; - } - paddle.move(); - paddle.render(); - paddle.test_hit(); - ball.render(); - - if (pause) { - if (!gameover) { - ctx.font = "50px Gugi"; - ctx.fillStyle = '#FFFFFF'; - ctx.textAlign = "center"; - ctx.fillText("Ready " + currUserName, c.width / 2, c.height / 2); - ctx.fillText("Press Space to Start", c.width / 2, c.height / 2 + 60); - } - } else { - requestAnimationFrame(frame); - } - }; - - var startTime = false; - document.fonts.load('50px Gugi').then(frame); - - var endGame = function () { - pause = true; - gameover = true; - submitScore(); - }; - - var refreshScoreBoard = function () { - var scores = JSON.parse(this.responseText); - console.log(scores); - var output = 'ScoreTimeName'; - for (var i = 0; i < scores.length; i++) { - output += '' + scores[i].score + '' + scores[i].time + 's' + scores[i].name + ''; - } - document.getElementById("leadertable").innerHTML = output; - }; - - var submitScore = function () { - var time_taken = Math.floor(Date.now() / 1000) - startTime; - var xhttp = new XMLHttpRequest(); - xhttp.addEventListener("load", getScoreBoard); - xhttp.open("POST", "/api/score/" + launchId + "/" + score + "/" + time_taken + "/", false); - xhttp.send(); - }; - - var getScoreBoard = function () { - var xhttp = new XMLHttpRequest(); - xhttp.addEventListener("load", refreshScoreBoard); - xhttp.open("GET", "/api/scoreboard/" + launchId + "/", true); - xhttp.send(); - }; - - getScoreBoard(); -} - -document.addEventListener("DOMContentLoaded", mainGame); diff --git a/examples/game/game/templates/check_cookie.html b/examples/game/game/templates/check_cookie.html deleted file mode 100644 index 797e689..0000000 --- a/examples/game/game/templates/check_cookie.html +++ /dev/null @@ -1,61 +0,0 @@ -{% load static %} - - - - -{{ page_title }} - - - - - -
-

Loading...

-
- - - diff --git a/examples/game/game/templates/game.html b/examples/game/game/templates/game.html deleted file mode 100644 index 231fe0e..0000000 --- a/examples/game/game/templates/game.html +++ /dev/null @@ -1,42 +0,0 @@ -{% load static %} - - - - -{{ page_title }} - - - - - - -{% if is_deep_link_launch %} -
-

Pick a Difficulty

- -
-{% endif %} -
-
-
-

Scoreboard

- -
-
- - - - -
-
- - diff --git a/examples/game/game/urls.py b/examples/game/game/urls.py deleted file mode 100644 index e6bf628..0000000 --- a/examples/game/game/urls.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.conf import settings -from django.conf.urls import url -from django.urls import include, path -from .views import check_cookies_allowed, login, launch, configure, score, scoreboard - -urlpatterns = [ - url(r'^check-cookies-allowed/$', check_cookies_allowed, name='check-cookies-allowed'), - url(r'^login/$', login, name='game-login'), - url(r'^launch/$', launch, name='game-launch'), - url(r'^configure/(?P[\w-]+)/(?P[\w-]+)/$', configure, name='game-configure'), - url(r'^api/score/(?P[\w-]+)/(?P[\w-]+)/(?P[\w-]+)/$', score, - name='game-api-score'), - url(r'^api/scoreboard/(?P[\w-]+)/$', scoreboard, name='game-api-scoreboard'), -] - -if settings.DEBUG: - import debug_toolbar - urlpatterns = [ - path('__debug__/', include(debug_toolbar.urls)), - ] + urlpatterns diff --git a/examples/game/game/views.py b/examples/game/game/views.py deleted file mode 100644 index 154a8cf..0000000 --- a/examples/game/game/views.py +++ /dev/null @@ -1,254 +0,0 @@ -import datetime -import os -import pprint -import uuid - -from django.core.cache import caches -from django.conf import settings -from django.http import HttpResponse, HttpResponseForbidden, JsonResponse -from django.shortcuts import render -from django.views.decorators.http import require_POST -from django.urls import reverse -from pylti1p3.contrib.django import DjangoOIDCLogin, DjangoMessageLaunch -from pylti1p3.deep_link_resource import DeepLinkResource -from pylti1p3.grade import Grade -from pylti1p3.lineitem import LineItem -from pylti1p3.tool_config import ToolConfJsonFile - - -PAGE_TITLE = 'Game Example' - - -class ExtendedDjangoMessageLaunch(DjangoMessageLaunch): - - def validate_nonce(self): - """ - Probably it is bug on "https://lti-ri.imsglobal.org": - site passes invalid "nonce" value during deep links launch. - Because of this in case of iss == http://imsglobal.org just skip nonce validation. - - """ - iss = self._get_iss() - deep_link_launch = self.is_deep_link_launch() - if iss == "http://imsglobal.org" and deep_link_launch: - return self - return super(ExtendedDjangoMessageLaunch, self).validate_nonce() - - -class DjangoFakeRequest(object): - - GET = {} - POST = {} - COOKIES = {} - session = None - _is_secure = False - - def __init__(self, GET, POST, COOKIES, session, is_secure): - self.GET = GET - self.POST = POST - self.COOKIES = COOKIES - self._is_secure = is_secure - self.session = session - - def is_secure(self): - return self._is_secure - - -def get_lti_config_path(): - return os.path.join(settings.BASE_DIR, '..', 'configs', 'game.json') - - -def get_launch_url(request): - target_link_uri = request.POST.get('target_link_uri', request.GET.get('target_link_uri')) - if not target_link_uri: - raise Exception('Missing "target_link_uri" param') - return target_link_uri - - -def check_cookies_allowed(request): - test_cookie_val = request.COOKIES.get('test_cookie', None) - request_ts = request.GET.get('ts', None) - cookie_sent = bool(request_ts and test_cookie_val and request_ts == test_cookie_val) - return JsonResponse({'cookies_allowed': cookie_sent}) - - -def login(request): - cache = caches['default'] - cookies_allowed = str(request.GET.get('cookies_allowed', '')) - if cookies_allowed: - login_unique_id = str(request.GET.get('login_unique_id', '')) - if not login_unique_id: - raise Exception('Missing "login_unique_id" param') - - login_data = cache.get(login_unique_id) - if not login_data: - raise Exception("Can't restore login data from cache") - - tool_conf = ToolConfJsonFile(get_lti_config_path()) - request_like_obj = DjangoFakeRequest( - login_data['GET'], login_data['POST'], - request.COOKIES, request.session, request.is_secure()) - oidc_login = DjangoOIDCLogin(request_like_obj, tool_conf) - target_link_uri = get_launch_url(request_like_obj) - return oidc_login.redirect(target_link_uri) - else: - login_unique_id = str(uuid.uuid4()) - cache.set(login_unique_id, { - 'GET': {k: v for k, v in request.GET.items()}, - 'POST': {k: v for k, v in request.POST.items()} - }, 3600) - return render(request, 'check_cookie.html', { - 'login_unique_id': login_unique_id, - 'same_site': getattr(settings, 'SESSION_COOKIE_SAMESITE'), - 'page_title': PAGE_TITLE - }) - - -def launch(request): - cache = caches['default'] - launch_unique_id = str(request.GET.get('launch_id', '')) - - # reload page in case if cookie is unavailable (chrome samesite issue) - if not request.session.session_key and not launch_unique_id: - launch_unique_id = str(uuid.uuid4()) - cache.set(launch_unique_id, { - 'GET': {k: v for k, v in request.GET.items()}, - 'POST': {k: v for k, v in request.POST.items()} - }, 3600) - current_url = request.build_absolute_uri() - if '?' in current_url: - current_url += '&' - else: - current_url += '?' - current_url = current_url + 'launch_id=' + launch_unique_id - return HttpResponse('' % current_url) - - request_like_obj = None - if request.method == "GET": - launch_data = cache.get(launch_unique_id) - if not launch_data: - raise Exception("Can't restore launch data from cache") - request_like_obj = DjangoFakeRequest( - launch_data['GET'], launch_data['POST'], - request.COOKIES, request.session, request.is_secure()) - - tool_conf = ToolConfJsonFile(get_lti_config_path()) - message_launch = ExtendedDjangoMessageLaunch(request_like_obj if request_like_obj else request, tool_conf) - message_launch_data = message_launch.get_launch_data() - pprint.pprint(message_launch_data) - - return render(request, 'game.html', { - 'page_title': PAGE_TITLE, - 'is_deep_link_launch': message_launch.is_deep_link_launch(), - 'launch_data': message_launch.get_launch_data(), - 'launch_id': message_launch.get_launch_id(), - 'curr_user_name': message_launch_data.get('name', ''), - 'curr_diff': message_launch_data.get('https://purl.imsglobal.org/spec/lti/claim/custom', {}) - .get('difficulty', 'normal')}) - - -def configure(request, launch_id, difficulty): - tool_conf = ToolConfJsonFile(get_lti_config_path()) - message_launch = ExtendedDjangoMessageLaunch.from_cache(launch_id, request, tool_conf) - - if not message_launch.is_deep_link_launch(): - return HttpResponseForbidden('Must be a deep link!') - - launch_url = request.build_absolute_uri(reverse('game-launch')) - - resource = DeepLinkResource() - resource.set_url(launch_url)\ - .set_custom_params({'difficulty': difficulty})\ - .set_title('Breakout ' + difficulty + ' mode!') - - html = message_launch.get_deep_link().output_response_form([resource]) - return HttpResponse(html) - - -@require_POST -def score(request, launch_id, earned_score, time_spent): - tool_conf = ToolConfJsonFile(get_lti_config_path()) - message_launch = ExtendedDjangoMessageLaunch.from_cache(launch_id, request, tool_conf) - - if not message_launch.has_ags(): - return HttpResponseForbidden("Don't have grades!") - - sub = message_launch.get_launch_data().get('sub') - timestamp = datetime.datetime.utcnow().isoformat() - earned_score = int(earned_score) - time_spent = int(time_spent) - - grades = message_launch.get_ags() - sc = Grade() - sc.set_score_given(earned_score)\ - .set_score_maximum(100)\ - .set_timestamp(timestamp)\ - .set_activity_progress('Completed')\ - .set_grading_progress('FullyGraded')\ - .set_user_id(sub) - - sc_line_item = LineItem() - sc_line_item.set_tag('score')\ - .set_score_maximum(100)\ - .set_label('Score') - - grades.put_grade(sc, sc_line_item) - - tm = Grade() - tm.set_score_given(time_spent)\ - .set_score_maximum(999)\ - .set_timestamp(timestamp)\ - .set_activity_progress('Completed')\ - .set_grading_progress('FullyGraded')\ - .set_user_id(sub) - - tm_line_item = LineItem() - tm_line_item.set_tag('time')\ - .set_score_maximum(999)\ - .set_label('Time Taken') - - result = grades.put_grade(tm, tm_line_item) - - return JsonResponse({'success': True, 'result': result.get('body')}) - - -def scoreboard(request, launch_id): - tool_conf = ToolConfJsonFile(get_lti_config_path()) - message_launch = ExtendedDjangoMessageLaunch.from_cache(launch_id, request, tool_conf) - - if not message_launch.has_nrps(): - return HttpResponseForbidden("Don't have names and roles!") - - if not message_launch.has_ags(): - return HttpResponseForbidden("Don't have grades!") - - ags = message_launch.get_ags() - - score_line_item = LineItem() - score_line_item.set_tag('score') \ - .set_score_maximum(100) \ - .set_label('Score') - scores = ags.get_grades(score_line_item) - - time_line_item = LineItem() - time_line_item.set_tag('time') \ - .set_score_maximum(999) \ - .set_label('Time Taken') - times = ags.get_grades(time_line_item) - - members = message_launch.get_nrps().get_members() - scoreboard_result = [] - - for sc in scores: - result = {'score': sc['resultScore']} - for tm in times: - if tm['userId'] == sc['userId']: - result['time'] = tm['resultScore'] - break - for member in members: - if member['user_id'] == sc['userId']: - result['name'] = member.get('name', 'Unknown') - break - scoreboard_result.append(result) - - return JsonResponse(scoreboard_result, safe=False) diff --git a/examples/game/game/wsgi.py b/examples/game/game/wsgi.py deleted file mode 100644 index b8fb25b..0000000 --- a/examples/game/game/wsgi.py +++ /dev/null @@ -1,7 +0,0 @@ -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "game.settings") - -application = get_wsgi_application() diff --git a/examples/game/manage.py b/examples/game/manage.py deleted file mode 100755 index fbc91ef..0000000 --- a/examples/game/manage.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -if __name__ == "__main__": - sys.path.extend([ - os.path.abspath('../'), # Docker usage - os.path.abspath('../../') # local usage - ]) - - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "game.settings") - try: - from django.core.management import execute_from_command_line - except ImportError: - # The above import may fail for some other reason. Ensure that the - # issue is really that Django is missing to avoid masking other - # exceptions on Python 2. - try: - import django - except ImportError: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) - raise - execute_from_command_line(sys.argv) diff --git a/examples/requirements.txt b/examples/requirements.txt deleted file mode 100644 index 933afa7..0000000 --- a/examples/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -django==3.0.4 -django-debug-toolbar==2.2 -jwcrypto==0.6.0 -pyjwt==1.7.1 -requests==2.22.0