diff --git a/.gitignore b/.gitignore index e67073c..a70aeeb 100644 --- a/.gitignore +++ b/.gitignore @@ -246,6 +246,9 @@ Control.ca-bundle Control.system-ca-bundle GitHub.sublime-settings +#SkoolOS +*.profile + # Visual Studio Code # .vscode/* !.vscode/settings.json diff --git a/.profile b/.profile deleted file mode 100644 index df779be..0000000 --- a/.profile +++ /dev/null @@ -1 +0,0 @@ -{'absences': 2, 'address': None, 'counselor': {'first_name': 'Sean', 'full_name': 'Sean Burke', 'id': 37, 'last_name': 'Burke', 'url': 'https://ion.tjhsst.edu/api/profile/37', 'user_type': 'counselor', 'username': 'SPBurke'}, 'display_name': 'Raffu Khondaker', 'emails': [], 'first_name': 'Raffu', 'full_name': 'Raffu Khondaker', 'grade': {'name': 'sophomore', 'number': 10}, 'graduation_year': 2022, 'id': 36508, 'ion_username': '2022rkhondak', 'is_announcements_admin': False, 'is_eighth_admin': False, 'is_student': True, 'is_teacher': False, 'last_name': 'Khondaker', 'middle_name': 'Al', 'nickname': '', 'phones': [], 'picture': 'https://ion.tjhsst.edu/api/profile/36508/picture', 'sex': 'Male', 'short_name': 'Raffu', 'title': None, 'tj_email': '2022rkhondak@tjhsst.edu', 'user_type': 'student', 'websites': []} \ No newline at end of file diff --git a/BackgroundService/bgservice.py b/BackgroundService/bgservice.py new file mode 100644 index 0000000..ed26c76 --- /dev/null +++ b/BackgroundService/bgservice.py @@ -0,0 +1,58 @@ +import os +import sys +import signal +import time +import event_processor + + +class SkoolOSDaemon: + """Constructor""" + def __init__(self, work_dir='/tmp'): + self.work_dir = work_dir + self.start_time = None + self.end_time = None + self.log_file = None + def __write_pid_file(self): + try: + dirName = "/tmp/skooloslogs" + # Create log Directory + os.mkdir(dirName) + except FileExistsError: + pass + pid = str(os.getpid()) + file_ = open('/tmp/skoolosdaemonpid', 'w') + file_.write(pid) + file_.close() + def readable_time(self, input_time): + return time.strftime("%A, %B %d, %Y %H:%M:%S", time.localtime(input_time)) + def start(self): + self.__write_pid_file() + self.start_time = time.time() + self.log_file = open('/tmp/skooloslogs/' + str(self.start_time), 'w') + self.log_file.write("Start time: \n" + self.readable_time(self.start_time) + "\n\n") + sys.stdout = self.log_file + event_processor.watch_dir(self.work_dir) + def stop(self): + event_processor.stop_watching() + self.end_time = time.time() + self.log_file.write("Stop time: \n" + self.readable_time(self.end_time)) + self.log_file.write("Total work time: " + + time.strftime("%H:%M:%S", time.gmtime(self.end_time - self.start_time))) + + + + +logger = None + +def Main(): + def signal_handler(signum, frame): + logger.stop() + signal.signal(signal.SIGINT, signal_handler) + # signal.signal(signal.SIGTERM, signal_handler) + global logger + logger = SkoolOSDaemon("/tmp") + logger.start() + + +if __name__ == "__main__": + Main() diff --git a/BackgroundService/event_processor.py b/BackgroundService/event_processor.py new file mode 100644 index 0000000..55c5e15 --- /dev/null +++ b/BackgroundService/event_processor.py @@ -0,0 +1,65 @@ +import time +import pyinotify + + +def readable_time(input_time): + return time.strftime("%A, %B %d, %Y %H:%M:%S", time.localtime(input_time)) + + +class EventProcessor(pyinotify.ProcessEvent): + _methods = ["IN_OPEN", + "IN_CREATE", + "IN_CLOSE_WRITE", + "IN_DELETE", + "IN_MOVED_TO", + "IN_MOVED_FROM", + ] + + +def __method_format(method): + return { + "IN_OPEN":"Opened a file", + "IN_CREATE":"Created a file", + "IN_CLOSE_WRITE":"Wrote to a file", + "IN_DELETE":"Deleted a file", + "IN_MOVED_TO":"Moved a file or directory in from elsewhere", + "IN_MOVED_FROM":"Moved a file or directory elsewhere", + }.get(method, "Unknown event") + + +def __process_generator(cls, method): + def _method_name(self, event): + description = "Event description: {}\n" \ + "Path name: {}\n" \ + "Event Name: {}\n" \ + "Timestamp: {}\n".format(__method_format(method), + event.pathname, + event.maskname, + readable_time(time.time()) + ) + if "IN_DELETE" in description: + description += "WARNING: Unexpected file deletion\n" + if "IN_MOVED_TO" in description: + description += "WARNING: Unexpected file add to work\n" + if "IN_MOVED_FROM" in description: + description += "WARNING: Unexpected file moved out of directory\n" + print(description) + _method_name.__name__ = "process_{}".format(method) + setattr(cls, _method_name.__name__, _method_name) + + +EVENT_NOTIFIER = None + + +def watch_dir(dir_to_watch): + global EVENT_NOTIFIER + for method in EventProcessor._methods: + __process_generator(EventProcessor, method) + watch_manager = pyinotify.WatchManager() + EVENT_NOTIFIER = pyinotify.ThreadedNotifier(watch_manager, EventProcessor()) + watch_manager.add_watch(dir_to_watch, pyinotify.ALL_EVENTS, rec=True) + EVENT_NOTIFIER.loop() + + +def stop_watching(): + EVENT_NOTIFIER.stop() diff --git a/BackgroundService/test.py b/BackgroundService/test.py new file mode 100644 index 0000000..b797228 --- /dev/null +++ b/BackgroundService/test.py @@ -0,0 +1,19 @@ +from bgservice import SkoolOSDaemon as sod +import threading + +logger = sod() + + +if __name__ == "__main__": + line=1 + print(line) + line+=1 + logger.start() + print(line) + line+=1 + input("Enter any key when you are done modifyng the /tmp/ directory") + print(line) + line+=1 + logger.stop() + print(line) + line+=1 diff --git a/CLI/commands.py b/CLI/commands.py index ed1c1f3..39ed5f7 100644 --- a/CLI/commands.py +++ b/CLI/commands.py @@ -5,4 +5,144 @@ import os import argparse - \ No newline at end of file +''' +my_parser = argparse.ArgumentParser(prog='skool', description='Let SkoolOS control your system', epilog="Try again") +my_parser.add_argument('--init', action="store_true") #returns true if run argument +args = my_parser.parse_args() + +update() +outputs = vars(args) +if(outputs['init']): + start() +''' + +#already ccrerrated account through website, has to login +def update(): + #get data from database + return + +def yesorno(question): + questions = [ + { + 'type': 'input', + 'name': 'response', + 'message': question, + }, + ] + answers = prompt(questions) + if(answers["response"] == "y"): + return True + return False + +def login(): + #enter username + #enter password + questions = [ + { + 'type': 'input', + 'name': 'webmail', + 'message': 'What\'s TJ Webmail', + }, + { + 'type': 'password', + 'name': 'password', + 'message': 'Password?', + }, + ] + user = prompt(questions) + #reading from json of users (replace w GET to database) to check if user is registered + with open('users.json', 'r') as json_file: + data = json.load(json_file) + for i in range(len(data)): + if user["webmail"] == data[i]["webmail"]: + if(user["password"] == data[i]["password"]): + print("Logged in!") + return data[i] + else: + print("Password incorrect. Try again.") + return None + print("User not found. Please Try again") + return None + +#did not create account through website, has to signup/login +def signup(): + questions = [ + { + 'type': 'input', + 'name': 'first-name', + 'message': 'What\'s your first name', + }, + { + 'type': 'input', + 'name': 'last-name', + 'message': 'What\'s your last name?', + }, + { + 'type': 'list', + 'name': 'grade', + 'message': 'Grade?', + 'choices':["9","10","11","12"] + }, + { + 'type': 'input', + 'name': 'webmail', + 'message': 'What\'s your TJ Webmail?', + }, + { + 'type': 'password', + 'name': 'password', + 'message': 'Password?', + }, + ] + user = prompt(questions) + for i in user: + if user[i] == "": + print("Some forms were left blank. Try again.\n") + return None + if len(user["password"]) < 6: + print("Password is too short. Try again.") + return None + if (("@tjhsst.edu" in user['webmail']) == False): + print("Webmail entered was not a @tjhhsst.edu. Try again.") + return None + + user["classes"] = [] + with open('users.json', 'r') as json_file: + data = json.load(json_file) + data.append(user) + open("users.json", "w").write(str(json.dumps(data))) + return user + +def relogin(): + questions = [ + { + 'type': 'list', + 'name': 'choice', + 'message': '', + 'choices':["Continue as current user","Login into new user","Sign up into new account"] + }, + ] + answer = prompt(questions) + + +def setup(user): + #Read classes/assignenments and setup directory: + #SkoolOS/Math/Week1 + for c in user["classes"]: + os.makedirs(c) + for a in user["classes"][c]: + os.makedirs(c + "/" + a) + +def start(): + if(os.path.exists(".login.txt") == False): + b = yesorno("Do you have a SkoolOS account?(y/N)") + if(b): + user = login() + if(user != None): + setup(user) + open(".login.txt", "w").write(str(user)) + else: + user = signup() + if(user != None): + open(".login.txt").write(str(user)) + diff --git a/CLI/oauth/index.html b/CLI/oauth/index.html index 62f3082..9ffd6ef 100644 --- a/CLI/oauth/index.html +++ b/CLI/oauth/index.html @@ -14,7 +14,7 @@
- + Sign in with Ion diff --git a/CLI/skoolos.py b/CLI/skoolos.py index 57f3ae3..9477806 100644 --- a/CLI/skoolos.py +++ b/CLI/skoolos.py @@ -1,6 +1,5 @@ import sys from urllib.parse import urlparse - import requests from requests_oauthlib import OAuth2Session from selenium import webdriver @@ -11,10 +10,15 @@ import socketserver from threading import Thread from werkzeug.urls import url_decode import pprint +from PyInquirer import prompt, print_json +import json +import os +import argparse +from cryptography.fernet import Fernet client_id = r'QeZPBSKqdvWFfBv1VYTSv9iFGz5T9pVJtNUjbEr6' client_secret = r'0Wl3hAIGY9SvYOqTOLUiLNYa4OlCgZYdno9ZbcgCT7RGQ8x2f1l2HzZHsQ7ijC74A0mrOhhCVeZugqAmOADHIv5fHxaa7GqFNtQr11HX9ySTw3DscKsphCVi5P71mlGY' -redirect_uri = 'http://localhost:8000/' +redirect_uri = 'http://localhost:8000/callback/' token_url = 'https://ion.tjhsst.edu/oauth/token/' scope = ["read"] @@ -30,16 +34,21 @@ def main(): print("") if not os.path.exists(".profile"): - print(76546789876545678765) + input("Welcome to SkoolOS. Press any key to create an account") authenticate() - print(open(".profile", "r").read()) else: - print(open(".profile", "r").read()) + file = open('key.key', 'rb') + key = file.read() # The key will be type bytes + file.close() + f = Fernet(key) + file = open('.profile', 'rb') + p = file.read() # The key will be type bytes + file.close() + # while True: # pass - def authenticate(): oauth = OAuth2Session(client_id=client_id, redirect_uri=redirect_uri, scope=scope) authorization_url, state = oauth.authorization_url("https://ion.tjhsst.edu/oauth/authorize/") @@ -48,12 +57,14 @@ def authenticate(): #Linux: chromdriver-linux #Macos: chromdriver-mac #Windows: chromdriver.exe - path = os.path.join(cdir, "chromedriver-mac") - print(path) - browser = webdriver.Chrome(path) - browser = webdriver.Safari() + if('CLI' in os.getcwd()): + path = os.path.join(os.getcwd(), '../','chromedriver-mac') + else: + path = os.path.join(os.getcwd(), 'chromedriver-mac') + browser = webdriver.Chrome(path) web_dir = os.path.join(os.path.dirname(__file__), 'oauth') + print(web_dir) os.chdir(web_dir) if os.path.exists("index.html"): os.remove("index.html") @@ -71,24 +82,29 @@ def authenticate(): browser.get("localhost:8000/") - while "http://localhost:8000/?code" not in browser.current_url: + while "http://localhost:8000/callback/?code" not in browser.current_url: time.sleep(0.25) url = browser.current_url - gets = url_decode(url.replace("http://localhost:8000/?", "")) + gets = url_decode(url.replace("http://localhost:8000/callback/?", "")) + while "http://localhost:8000/callback/?code" not in browser.current_url: + time.sleep(0.25) + + url = browser.current_url + gets = url_decode(url.replace("http://localhost:8000/callback/?", "")) code = gets.get("code") if state == gets.get("state"): state = gets.get("state") print("states good") browser.quit() - print(code) + #print(code) print(state) payload = {'grant_type': 'authorization_code', 'code': code, 'redirect_uri': redirect_uri, 'client_id': client_id, 'client_secret': client_secret, 'csrfmiddlewaretoken': state} token = requests.post("https://ion.tjhsst.edu/oauth/token/", data=payload).json() - print(token) + #print(token) headers = {'Authorization': f"Bearer {token['access_token']}"} # And finally get the user's profile! @@ -101,10 +117,18 @@ def authenticate(): last_name = profile['last_name'] os.chdir(cdir) - profileFile = open(".profile", "w") - #profileFile.write(profile.text()) - profileFile.write(str(profile)) - profileFile.close() + # key = Fernet.generate_key() + # file = open('key.key', 'wb') + # file.write(key) # The key is type bytes still + # file.close() + # p = str(profile).encode() + # f = Fernet(key) + # encrypted = f.encrypt(p) + + # profileFile = open(".profile", "wb") + # #profileFile.write(profile.text()) + # profileFile.write(encrypted) + # profileFile.close() sys.exit diff --git a/CLI/s-git.py b/CLI/student.py similarity index 97% rename from CLI/s-git.py rename to CLI/student.py index 4a007af..711c99e 100644 --- a/CLI/s-git.py +++ b/CLI/student.py @@ -386,10 +386,17 @@ class Student: command('git checkout master') os.chdir(cdir) -data = getStudent("2022rkhondak") -s = Student(data) -#s.viewClass("APLit_eharris1") -#s.updateClass("APLit_eharris1") -#s.update() -s.exitCLI() +# data = getStudent("2022rkhondak") +# s = Student(data) +# #s.viewClass("APLit_eharris1") +# #s.updateClass("APLit_eharris1") +# #s.update() +# s.exitCLI() +def main(): + print("noooo") + pass + +if __name__ == "__main__": + # stuff only to run when not called via 'import' here + main() diff --git a/CLI/t-git.py b/CLI/teacher.py similarity index 99% rename from CLI/t-git.py rename to CLI/teacher.py index 4e81255..d34ae79 100644 --- a/CLI/t-git.py +++ b/CLI/teacher.py @@ -659,8 +659,8 @@ t = Teacher(data) #ar = ['2022rkhondak','2022inafi','2023rumareti'] #extra = t.reqAddStudentList(ar, "APLit_eharris1") #print(extra) -t.getStudents('2022rkhondak') -t.getChanges('2022rkhondak','APLit_eharris1', 10) +# t.getStudents('2022rkhondak') +# t.getChanges('2022rkhondak','APLit_eharris1', 10) ''' TO-DO diff --git a/Website/api/admin.py b/Website/api/admin.py index 8c38f3f..8446cf4 100644 --- a/Website/api/admin.py +++ b/Website/api/admin.py @@ -1,3 +1,16 @@ from django.contrib import admin +from .models import ( + DefFiles, + Assignment, + Classes, + Teacher, + Student +) # Register your models here. + +admin.site.register(Teacher) +admin.site.register(Student) +admin.site.register(DefFiles) +admin.site.register(Assignment) +admin.site.register(Classes) diff --git a/Website/api/maker.py b/Website/api/maker.py index dd31bb6..fb383ed 100644 --- a/Website/api/maker.py +++ b/Website/api/maker.py @@ -1,128 +1,128 @@ -from datetime import datetime - -f1 = DefFiles( - name="instructions.txt" -) -f1.save() -f2 = DefFiles( - name="instructions.txt" -) -f2.save() -f3 = DefFiles( - name="sample.txt" -) -f3.save() -f4 = DefFiles( - name="rubric.txt" -) -f4.save() - -a1 = Assignment.objects.get(pk=1) -a1.files.add(f1) -a1.save() -a2 = Assignment.objects.get(pk=2) -a2.files.add(f2) -a2.save() -a3 = Assignment.objects.get(pk=3) -a3.files.add(f3) -a3.files.add(f4) -a3.save() - -#################################### - -from api.models import Assignment, Student, Classes, Teacher, DefFiles -from datetime import datetime - -f1 = DefFiles( - name="instructions.txt" -) -f1.save() -f2 = DefFiles( - name="instructions.txt" -) -f2.save() -f3 = DefFiles( - name="sample.txt" -) -f3.save() -f4 = DefFiles( - name="rubric.txt" -) -f4.save() - -A1 = Assignment( - name='Week1_HW', - due_date=datetime.now(), -) -A1.save() -A1.files.add(f1) -A1.save() - -A2 = Assignment( - name='Week2_HW', - due_date=datetime.now(), - -) -A2.save() -A2.files.add(f2) -A2.save() - -A3 = Assignment( - name='Journal1', - due_date=datetime.now(), -) -A3.save() -A3.files.add(f3) -A3.files.add(f4) -A3.save() - -#classes -math = Classes( - name='Math5', - -) -math.save() -math.assignments.add(A1) -math.assignments.add(A2) -math.save() - -english = Classes( - name='English', -) -english.save() -english.assignments.add(A3) -english.save() - -#students -raffu = Student( - first_name = "Raffu", - last_name = "Khondaker", - student_id = 1579460, - ion_user="2022rkhondak", - webmail = "2022rkhondak@tjhsst.edu", - grade = 10, - repo="https://github.com/therealraffi/2022rkhondak.git", -) -raffu.save() -raffu.classes.add(math) -raffu.classes.add(english) -raffu.save() - -#teachers -ng = Teacher( - first_name = "Errin", - last_name = "Harris", - ion_user="eharris1" -) -ng.save() -ng.classes.add(math) -ng.save() - -chao = Teacher( - first_name = "Abagail", - last_name = "Bailey", - ion_user="AKBailey" -) -chao.save() -chao.classes.add(english) -chao.save() +# from datetime import datetime +# +# f1 = DefFiles( +# name="instructions.txt" +# ) +# f1.save() +# f2 = DefFiles( +# name="instructions.txt" +# ) +# f2.save() +# f3 = DefFiles( +# name="sample.txt" +# ) +# f3.save() +# f4 = DefFiles( +# name="rubric.txt" +# ) +# f4.save() +# +# a1 = Assignment.objects.get(pk=1) +# a1.files.add(f1) +# a1.save() +# a2 = Assignment.objects.get(pk=2) +# a2.files.add(f2) +# a2.save() +# a3 = Assignment.objects.get(pk=3) +# a3.files.add(f3) +# a3.files.add(f4) +# a3.save() +# +# #################################### +# +# from api.models import Assignment, Student, Classes, Teacher, DefFiles +# from datetime import datetime +# +# f1 = DefFiles( +# name="instructions.txt" +# ) +# f1.save() +# f2 = DefFiles( +# name="instructions.txt" +# ) +# f2.save() +# f3 = DefFiles( +# name="sample.txt" +# ) +# f3.save() +# f4 = DefFiles( +# name="rubric.txt" +# ) +# f4.save() +# +# A1 = Assignment( +# name='Week1_HW', +# due_date=datetime.now(), +# ) +# A1.save() +# A1.files.add(f1) +# A1.save() +# +# A2 = Assignment( +# name='Week2_HW', +# due_date=datetime.now(), +# +# ) +# A2.save() +# A2.files.add(f2) +# A2.save() +# +# A3 = Assignment( +# name='Journal1', +# due_date=datetime.now(), +# ) +# A3.save() +# A3.files.add(f3) +# A3.files.add(f4) +# A3.save() +# +# #classes +# math = Classes( +# name='Math5', +# +# ) +# math.save() +# math.assignments.add(A1) +# math.assignments.add(A2) +# math.save() +# +# english = Classes( +# name='English', +# ) +# english.save() +# english.assignments.add(A3) +# english.save() +# +# #students +# raffu = Student( +# first_name = "Raffu", +# last_name = "Khondaker", +# student_id = 1579460, +# ion_user="2022rkhondak", +# webmail = "2022rkhondak@tjhsst.edu", +# grade = 10, +# repo="https://github.com/therealraffi/2022rkhondak.git", +# ) +# raffu.save() +# raffu.classes.add(math) +# raffu.classes.add(english) +# raffu.save() +# +# #teachers +# ng = Teacher( +# first_name = "Errin", +# last_name = "Harris", +# ion_user="eharris1" +# ) +# ng.save() +# ng.classes.add(math) +# ng.save() +# +# chao = Teacher( +# first_name = "Abagail", +# last_name = "Bailey", +# ion_user="AKBailey" +# ) +# chao.save() +# chao.classes.add(english) +# chao.save() diff --git a/Website/api/migrations/0001_initial.py b/Website/api/migrations/0001_initial.py index 056367b..96b239b 100644 --- a/Website/api/migrations/0001_initial.py +++ b/Website/api/migrations/0001_initial.py @@ -1,6 +1,8 @@ -# Generated by Django 3.0.7 on 2020-06-12 01:34 +# Generated by Django 3.0.7 on 2020-06-14 19:14 +from django.conf import settings from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): @@ -8,33 +10,10 @@ class Migration(migrations.Migration): initial = True dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ - migrations.CreateModel( - name='Assignment', - fields=[ - ('name', models.CharField(max_length=100, primary_key=True, serialize=False)), - ('due_date', models.DateTimeField()), - ('files', models.CharField(blank=True, default='', max_length=100)), - ('path', models.CharField(max_length=100)), - ('classes', models.CharField(max_length=100)), - ('teacher', models.CharField(max_length=100)), - ], - ), - migrations.CreateModel( - name='Classes', - fields=[ - ('name', models.CharField(max_length=100, primary_key=True, serialize=False)), - ('repo', models.URLField(default='')), - ('path', models.CharField(default='', max_length=100)), - ('teacher', models.CharField(default='', max_length=100)), - ('assignments', models.CharField(default='', max_length=100)), - ('default_file', models.CharField(default='', max_length=100)), - ('confirmed', models.TextField(blank=True, default='')), - ('unconfirmed', models.TextField(blank=True, default='')), - ], - ), migrations.CreateModel( name='DefFiles', fields=[ @@ -46,23 +25,6 @@ class Migration(migrations.Migration): ('teacher', models.CharField(max_length=100)), ], ), - migrations.CreateModel( - name='Student', - fields=[ - ('created', models.DateTimeField(auto_now_add=True)), - ('first_name', models.CharField(max_length=100)), - ('last_name', models.CharField(max_length=100)), - ('student_id', models.IntegerField()), - ('ion_user', models.CharField(max_length=100, primary_key=True, serialize=False)), - ('email', models.CharField(blank=True, default='', max_length=100)), - ('grade', models.IntegerField()), - ('git', models.CharField(max_length=100)), - ('repo', models.URLField(blank=True, default='')), - ('classes', models.CharField(blank=True, default='', max_length=100)), - ('added_to', models.CharField(blank=True, default='', max_length=100)), - ('completed', models.TextField(blank=True, default='')), - ], - ), migrations.CreateModel( name='Teacher', fields=[ @@ -73,6 +35,41 @@ class Migration(migrations.Migration): ('ion_user', models.CharField(max_length=100, primary_key=True, serialize=False)), ('git', models.CharField(max_length=100)), ('email', models.CharField(blank=True, default='', max_length=100)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Student', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('website', models.CharField(blank=True, default='https://google.com', max_length=150)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Classes', + fields=[ + ('name', models.CharField(max_length=100, primary_key=True, serialize=False)), + ('repo', models.URLField(blank=True, default='')), + ('path', models.CharField(default='', max_length=100)), + ('teacher', models.CharField(default='', max_length=100)), + ('assignments', models.TextField(blank=True, default='')), + ('default_file', models.CharField(blank=True, default='', max_length=100)), + ('confirmed', models.TextField(blank=True, default='')), + ('unconfirmed', models.TextField(blank=True, default='')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Assignment', + fields=[ + ('name', models.CharField(max_length=100, primary_key=True, serialize=False)), + ('due_date', models.DateTimeField()), + ('files', models.CharField(blank=True, default='', max_length=100)), + ('path', models.CharField(max_length=100)), + ('classes', models.CharField(max_length=100)), + ('teacher', models.CharField(max_length=100)), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assignments', to=settings.AUTH_USER_MODEL)), ], ), ] diff --git a/Website/api/migrations/0002_auto_20200612_0135.py b/Website/api/migrations/0002_auto_20200612_0135.py deleted file mode 100644 index 652eea2..0000000 --- a/Website/api/migrations/0002_auto_20200612_0135.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.7 on 2020-06-12 01:35 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='assignment', - name='name', - field=models.CharField(max_length=100), - ), - migrations.AlterField( - model_name='assignment', - name='path', - field=models.CharField(max_length=100, primary_key=True, serialize=False), - ), - ] diff --git a/Website/api/migrations/0002_auto_20200614_2034.py b/Website/api/migrations/0002_auto_20200614_2034.py new file mode 100644 index 0000000..f6c6f87 --- /dev/null +++ b/Website/api/migrations/0002_auto_20200614_2034.py @@ -0,0 +1,52 @@ +# Generated by Django 3.0.7 on 2020-06-14 20:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='student', + name='website', + ), + migrations.AddField( + model_name='student', + name='added_to', + field=models.CharField(blank=True, default='', max_length=100), + ), + migrations.AddField( + model_name='student', + name='classes', + field=models.CharField(blank=True, default='', max_length=100), + ), + migrations.AddField( + model_name='student', + name='completed', + field=models.TextField(blank=True, default=''), + ), + migrations.AddField( + model_name='student', + name='git', + field=models.CharField(blank=True, default='https://github.com/', max_length=100), + ), + migrations.AddField( + model_name='student', + name='grade', + field=models.IntegerField(blank=True, default=9), + ), + migrations.AddField( + model_name='student', + name='repo', + field=models.URLField(blank=True, default=''), + ), + migrations.AddField( + model_name='student', + name='student_id', + field=models.IntegerField(blank=True, default=0), + ), + ] diff --git a/Website/api/migrations/0003_auto_20200612_0135.py b/Website/api/migrations/0003_auto_20200612_0135.py deleted file mode 100644 index 5b95bdd..0000000 --- a/Website/api/migrations/0003_auto_20200612_0135.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.7 on 2020-06-12 01:35 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0002_auto_20200612_0135'), - ] - - operations = [ - migrations.AlterField( - model_name='assignment', - name='name', - field=models.CharField(max_length=100, primary_key=True, serialize=False), - ), - migrations.AlterField( - model_name='assignment', - name='path', - field=models.CharField(max_length=100), - ), - ] diff --git a/Website/api/migrations/0003_auto_20200614_2044.py b/Website/api/migrations/0003_auto_20200614_2044.py new file mode 100644 index 0000000..d5a7a69 --- /dev/null +++ b/Website/api/migrations/0003_auto_20200614_2044.py @@ -0,0 +1,43 @@ +# Generated by Django 3.0.7 on 2020-06-14 20:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0002_auto_20200614_2034'), + ] + + operations = [ + migrations.RemoveField( + model_name='student', + name='student_id', + ), + migrations.RemoveField( + model_name='teacher', + name='created', + ), + migrations.RemoveField( + model_name='teacher', + name='email', + ), + migrations.RemoveField( + model_name='teacher', + name='first_name', + ), + migrations.RemoveField( + model_name='teacher', + name='last_name', + ), + migrations.AlterField( + model_name='student', + name='git', + field=models.CharField(blank=True, default='', max_length=100), + ), + migrations.AlterField( + model_name='student', + name='grade', + field=models.IntegerField(blank=True), + ), + ] diff --git a/Website/api/migrations/0004_auto_20200612_0813.py b/Website/api/migrations/0004_auto_20200612_0813.py deleted file mode 100644 index c713dcc..0000000 --- a/Website/api/migrations/0004_auto_20200612_0813.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.0.7 on 2020-06-12 08:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0003_auto_20200612_0135'), - ] - - operations = [ - migrations.AlterField( - model_name='classes', - name='assignments', - field=models.TextField(blank=True, default=''), - ), - migrations.AlterField( - model_name='classes', - name='default_file', - field=models.CharField(blank=True, default='', max_length=100), - ), - migrations.AlterField( - model_name='classes', - name='repo', - field=models.URLField(blank=True, default=''), - ), - ] diff --git a/Website/api/migrations/0004_auto_20200614_2107.py b/Website/api/migrations/0004_auto_20200614_2107.py new file mode 100644 index 0000000..75e2a7c --- /dev/null +++ b/Website/api/migrations/0004_auto_20200614_2107.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.7 on 2020-06-14 21:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0003_auto_20200614_2044'), + ] + + operations = [ + migrations.AlterField( + model_name='student', + name='grade', + field=models.IntegerField(blank=True, default=0), + ), + ] diff --git a/Website/api/models.py b/Website/api/models.py index 6c8cb7d..e8bb2b3 100644 --- a/Website/api/models.py +++ b/Website/api/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.contrib.auth.models import User class DefFiles(models.Model): name=models.CharField(max_length=100) @@ -8,6 +9,8 @@ class DefFiles(models.Model): teacher=models.CharField(max_length=100) class Assignment(models.Model): + owner = models.ForeignKey('auth.User', related_name='assignments', on_delete=models.CASCADE) + name=models.CharField(max_length=100, primary_key=True) due_date=models.DateTimeField() # files = models.ManyToManyField(DefFiles) @@ -19,6 +22,8 @@ class Assignment(models.Model): return '%s' % (self.name) class Classes(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + name = models.CharField(primary_key=True, max_length=100) repo=models.URLField(default="", blank=True) path=models.CharField(max_length=100, default="") @@ -34,31 +39,26 @@ class Classes(models.Model): return super(Classes, self).save(*args, **kwargs) class Teacher(models.Model): - created = models.DateTimeField(auto_now_add=True) - first_name = models.CharField(max_length=100) - last_name = models.CharField(max_length=100) + user = models.OneToOneField(User, on_delete=models.CASCADE) # classes = models.ManyToManyField(Classes, default="") classes=models.CharField(max_length=100, default="", blank=True) ion_user=models.CharField(primary_key=True, max_length=100) git=models.CharField(max_length=100) - email=models.CharField(max_length=100, default="", blank=True) + + def save(self, *args, **kwargs): + super(Teacher, self).save(*args, **kwargs) class Student(models.Model): - created = models.DateTimeField(auto_now_add=True) - first_name = models.CharField(max_length=100) - last_name = models.CharField(max_length=100) - student_id = models.IntegerField() - ion_user=models.CharField(primary_key=True, max_length=100) - email=models.CharField(max_length=100, default="", blank=True) - grade = models.IntegerField() - git=models.CharField(max_length=100) + user = models.OneToOneField(User, on_delete=models.CASCADE) + grade = models.IntegerField(default=0, blank=True) + git=models.CharField(default="", max_length=100, blank=True) repo=models.URLField(default="", blank=True) classes=models.CharField(max_length=100, default="", blank=True) added_to=models.CharField(max_length=100, default="", blank=True) completed=models.TextField(default="", blank=True) - + def save(self, *args, **kwargs): - return super(Student, self).save(*args, **kwargs) - - + super(Student, self).save(*args, **kwargs) + def __str__(self): + return f"{self.user.username}'s Profile" diff --git a/Website/api/permissions.py b/Website/api/permissions.py new file mode 100644 index 0000000..2d64429 --- /dev/null +++ b/Website/api/permissions.py @@ -0,0 +1,25 @@ +from rest_framework import permissions + + +class IsOwnerOrReadOnly(permissions.BasePermission): + """ + Custom permission to only allow owners of an object to edit it. + """ + + def has_object_permission(self, request, view, obj): + # Read permissions are allowed to any request, + # so we'll always allow GET, HEAD or OPTIONS requests. + if request.method in permissions.SAFE_METHODS: + return True + + # Write permissions are only allowed to the owner of the snippet. + return obj.owner == request.user or request.user.is_superuser + +class isTeacher(permissions.BasePermission): + #only teachers can make classes and assignmenst + def has_object_permission(self, request, view, obj): + if request.method in permissions.SAFE_METHODS: + return True + + # Write permissions are only allowed to the owner of the snippet. + return request.user.groups.filter(name__in=['teachers']).exists() or request.user.is_superuser diff --git a/Website/api/serializers.py b/Website/api/serializers.py index 0ff5ae6..2a05750 100644 --- a/Website/api/serializers.py +++ b/Website/api/serializers.py @@ -1,37 +1,55 @@ from django.contrib.auth.models import User, Group from .models import Student, Teacher, Classes, Assignment, DefFiles from rest_framework import serializers, permissions +from django.contrib.auth.models import User +from .permissions import IsOwnerOrReadOnly,isTeacher + +class UserSerializer(serializers.HyperlinkedModelSerializer): + students = serializers.PrimaryKeyRelatedField(many=True, queryset=Student.objects.all()) + teachers = serializers.PrimaryKeyRelatedField(many=True, queryset=Teacher.objects.all()) -class DefFilesSerializer(serializers.HyperlinkedModelSerializer): - permissions_classes = [permissions.IsAuthenticatedOrReadOnly] class Meta: - model = DefFiles - fields = ['name', 'path','assignment','classes', "teacher",'url', 'id'] + model = User + fields = ['id', 'username'] + +# class DefFilesSerializer(serializers.HyperlinkedModelSerializer): +# class Meta: +# model = DefFiles +# fields = ['name', 'path','assignment','classes', "teacher",'url', 'id'] class AssignmentSerializer(serializers.HyperlinkedModelSerializer): #permissions_classes = [permissions.IsAuthenticatedOrReadOnly] # files = DefFilesSerializer(many=True, read_only=True,allow_null=True) + owner = serializers.ReadOnlyField(source='owner.username') + class Meta: model = Assignment - fields = ['url','name', 'due_date', 'path' , "classes","teacher"] + # fields = ['url','name', 'due_date', 'path' , "classes","teacher",'owner'] + fields = ['name', 'due_date', 'path' , "classes","teacher",'owner'] class ClassesSerializer(serializers.HyperlinkedModelSerializer): # assignments = AssignmentSerializer(many=True, read_only=True,allow_null=True) # default_file=DefFilesSerializer(many=True, read_only=True,allow_null=True) + owner = serializers.ReadOnlyField(source='owner.username') class Meta: model = Classes - fields = ['url', 'name', 'repo','path', "teacher",'assignments',"default_file", 'confirmed', 'unconfirmed'] + # fields = ['url','name', 'repo','path', "teacher",'assignments',"default_file", 'confirmed', 'unconfirmed','owner'] + fields = ['name', 'repo','path', "teacher",'assignments',"default_file", 'confirmed', 'unconfirmed','owner'] class StudentSerializer(serializers.HyperlinkedModelSerializer): # classes = ClassesSerializer(many=True, read_only=True,allow_null=True) + owner = serializers.ReadOnlyField(source='owner.username') class Meta: model = Student - fields = ['url', 'first_name', 'last_name', 'grade','email','student_id', 'git','ion_user','classes','added_to','completed', 'repo'] + # fields = ['url','first_name', 'last_name', 'grade','email','student_id', 'git','ion_user','classes','added_to','completed', 'repo','owner'] + fields = ['first_name', 'last_name', 'grade','email','student_id', 'git','ion_user','classes','added_to','completed', 'repo','owner'] class TeacherSerializer(serializers.ModelSerializer): # classes = ClassesSerializer(many=True, read_only=True,allow_null=True) + owner = serializers.ReadOnlyField(source='owner.username') class Meta: model = Teacher - fields = ['url', 'first_name', 'last_name','git','ion_user', 'email','classes'] + # fields = ['url','first_name', 'last_name','git','ion_user', 'email','classes','owner'] + fields = ['first_name', 'last_name','git','ion_user', 'email','classes','owner'] diff --git a/Website/api/signals.py b/Website/api/signals.py new file mode 100644 index 0000000..fe8deab --- /dev/null +++ b/Website/api/signals.py @@ -0,0 +1,5 @@ +from django.contrib.auth.models import User +from django.db.models.signals import post_save +from django.dispatch import receiver +from .models import Student, Teacher +) diff --git a/Website/api/urls.py b/Website/api/urls.py index e69de29..f130d15 100644 --- a/Website/api/urls.py +++ b/Website/api/urls.py @@ -0,0 +1,16 @@ +from django.urls import path +from rest_framework.urlpatterns import format_suffix_patterns +from . import views + +urlpatterns = [ + path('students/', views.StudentList.as_view()), + path('students//', views.StudentDetail.as_view()), + path('teachers/', views.TeacherList.as_view()), + path('teachers//', views.TeacherDetail.as_view()), + path('assignments/', views.AssignmentList.as_view()), + path('assignments//', views.AssignmentDetail.as_view()), + path('classes/', views.ClassesList.as_view()), + path('classes//', views.ClassesDetail.as_view()), +] + +urlpatterns = format_suffix_patterns(urlpatterns) \ No newline at end of file diff --git a/Website/api/views-back.py b/Website/api/views-back.py index 5c936f9..011d81a 100644 --- a/Website/api/views-back.py +++ b/Website/api/views-back.py @@ -1,131 +1,89 @@ -# class StudentList(APIView): -# """ -# List all snippets, or create a new snippet. -# """ -# def get(self, request, format=None): -# snippets = Student.objects.all() -# serializer = StudentSerializer(snippets, many=True) -# return response.Response(serializer.data) +from .models import Student, Teacher, Classes, Assignment, DefFiles +from .serializers import StudentSerializer, TeacherSerializer, ClassesSerializer, AssignmentSerializer, UserSerializer +from rest_framework import generics, viewsets, permissions, response, status +from django.http import Http404 +from rest_framework.views import APIView +from django.contrib.auth.models import User +from .permissions import isTeacher, IsOwnerOrReadOnly +from django.shortcuts import render, redirect +from rest_framework.parsers import JSONParser +from django.http.response import JsonResponse +from rest_framework.response import Response +from rest_framework import mixins -# def post(self, request, format=None): -# serializer = StudentSerializer(data=request.data) -# if serializer.is_valid(): -# serializer.save() -# return response.Response(serializer.data, status=status.HTTP_201_CREATED) -# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# class StudentDetail(APIView): -# """ -# Retrieve, update or delete a snippet instance. -# """ -# def get_object(self, pk): -# try: -# return Student.objects.get(pk=pk) -# except Student.DoesNotExist: -# raise Http404 +class StudentList(generics.ListCreateAPIView): + queryset = Student.objects.all() + serializer_class = StudentSerializer + def perform_create(self, serializer): + serializer.save(owner=self.request.user) -# def get(self, request, pk, format=None): -# snippet = self.get_object(pk) -# serializer = StudentSerializer(snippet) -# return response.Response(serializer.data) +class StudentDetail(generics.RetrieveAPIView): + queryset = Student.objects.all() + serializer_class = StudentSerializer + permissions_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnly] -# def put(self, request, pk, format=None): -# snippet = self.get_object(pk) -# serializer = StudentSerializer(snippet, data=request.data) -# if serializer.is_valid(): -# serializer.save() -# return response.Response(serializer.data) -# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) +class TeacherList(generics.ListCreateAPIView): + queryset = Teacher.objects.all() + serializer_class = TeacherSerializer + def perform_create(self, serializer): + if(self.request.user.groups.filter(name__in=['teachers']).exists() or self.request.user.is_superuser): + serializer.save(owner=self.request.user) + else: + print("UNAUTHORIZED POST") + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# def delete(self, request, pk, format=None): -# snippet = self.get_object(pk) -# snippet.delete() -# return response.Response(status=status.HTTP_204_NO_CONTENT) +class TeacherDetail(generics.RetrieveAPIView): + queryset = Teacher.objects.all() + serializer_class = TeacherSerializer + permissions_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnly] -# class TeacherList(APIView): -# """ -# List all snippets, or create a new snippet. -# """ -# def get(self, request, format=None): -# snippets = Teacher.objects.all() -# serializer = TeacherSerializer(snippets, many=True) -# return response.Response(serializer.data) - -# def post(self, request, format=None): -# serializer = TeacherSerializer(data=request.data) -# if serializer.is_valid(): -# serializer.save() -# return response.Response(serializer.data, status=status.HTTP_201_CREATED) -# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - -# class TeacherDetail(APIView): -# """ -# Retrieve, update or delete a snippet instance. -# """ -# def get_object(self, pk): -# try: -# return Teacher.objects.get(pk=pk) -# except Teacher.DoesNotExist: -# raise Http404 - -# def get(self, request, pk, format=None): -# snippet = self.get_object(pk) -# serializer = TeacherSerializer(snippet) -# return response.Response(serializer.data) - -# def put(self, request, pk, format=None): -# snippet = self.get_object(pk) -# serializer = TeacherSerializer(snippet, data=request.data) -# if serializer.is_valid(): -# serializer.save() -# return response.Response(serializer.data) -# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - -# def delete(self, request, pk, format=None): -# snippet = self.get_object(pk) -# snippet.delete() -# return response.Response(status=status.HTTP_204_NO_CONTENT) -# class ClassesList(APIView): -# """ -# List all snippets, or create a new snippet. -# """ -# def get(self, request, format=None): -# snippets = Classes.objects.all() -# serializer = ClassesSerializer(snippets, many=True) -# return response.Response(serializer.data) +class ClassesList(generics.ListCreateAPIView): + queryset = Classes.objects.all() + serializer_class = ClassesSerializer + #permissions_classes = [isTeacher] + def perform_create(self, serializer): + if(self.request.user.groups.filter(name__in=['teachers']).exists() or self.request.user.is_superuser): + serializer.save(owner=self.request.user) + else: + print("UNAUTHORIZED POST") + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# def post(self, request, format=None): -# serializer = ClassesSerializer(data=request.data) -# if serializer.is_valid(): -# serializer.save() -# return response.Response(serializer.data, status=status.HTTP_201_CREATED) -# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) +# class ClassesDetail(generics.RetrieveAPIView): +# queryset = Classes.objects.all() +# serializer_class = ClassesSerializer +# # permissions_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnly] -# class ClassesDetail(APIView): -# """ -# Retrieve, update or delete a snippet instance. -# """ -# def get_object(self, pk): -# try: -# return Classes.objects.get(pk=pk) -# except Classes.DoesNotExist: -# raise Http404 +class ClassesDetail(mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, + generics.GenericAPIView): + queryset = Classes.objects.all() + serializer_class = ClassesSerializer -# def get(self, request, pk, format=None): -# snippet = self.get_object(pk) -# serializer = ClassesSerializer(snippet) -# return response.Response(serializer.data) + def get(self, request, *args, **kwargs): + return self.retrieve(request, *args, **kwargs) -# def put(self, request, pk, format=None): -# snippet = self.get_object(pk) -# serializer = ClassesSerializer(snippet, data=request.data) -# if serializer.is_valid(): -# serializer.save() -# return response.Response(serializer.data) -# return response.Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + def put(self, request, *args, **kwargs): + print(self.owner) + if(request.user == self.owner): + return self.update(request, *args, **kwargs) -# def delete(self, request, pk, format=None): -# snippet = self.get_object(pk) -# snippet.delete() -# return response.Response(status=status.HTTP_204_NO_CONTENT) \ No newline at end of file + def delete(self, request, *args, **kwargs): + return self.destroy(request, *args, **kwargs) + +class AssignmentList(generics.ListCreateAPIView): + queryset = Assignment.objects.all() + serializer_class = AssignmentSerializer + def perform_create(self, serializer): + if(self.request.user.groups.filter(name__in=['teachers']).exists() or self.request.user.is_superuser): + serializer.save(owner=self.request.user) + else: + print("UNAUTHORIZED POST") + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +class AssignmentDetail(generics.RetrieveAPIView): + queryset = Assignment.objects.all() + serializer_class = AssignmentSerializer + permissions_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnly] diff --git a/Website/api/views.py b/Website/api/views.py index 439c30b..3876c0a 100644 --- a/Website/api/views.py +++ b/Website/api/views.py @@ -1,8 +1,21 @@ from .models import Student, Teacher, Classes, Assignment, DefFiles -from .serializers import StudentSerializer, TeacherSerializer, ClassesSerializer, AssignmentSerializer, DefFilesSerializer +from .serializers import StudentSerializer, TeacherSerializer, ClassesSerializer, AssignmentSerializer, UserSerializer from rest_framework import generics, viewsets, permissions, response, status from django.http import Http404 from rest_framework.views import APIView +from django.contrib.auth.models import User +from .permissions import isTeacher, IsOwnerOrReadOnly +from django.shortcuts import render, redirect +from rest_framework.parsers import JSONParser +from rest_framework.response import Response + + + +class UserViewSet(viewsets.ModelViewSet): + queryset = User.objects.all() + serializer_class = UserSerializer + permission_classes = [permissions.IsAuthenticated] + class StudentViewSet(viewsets.ModelViewSet): """ @@ -10,8 +23,10 @@ class StudentViewSet(viewsets.ModelViewSet): """ queryset = Student.objects.all() serializer_class = StudentSerializer - permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + permission_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnly] + def perform_create(self, serializer): + serializer.save(owner=self.request.user) class TeacherViewSet(viewsets.ModelViewSet): """ @@ -19,8 +34,10 @@ class TeacherViewSet(viewsets.ModelViewSet): """ queryset = Teacher.objects.all() serializer_class = TeacherSerializer - permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + permission_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnly] + def perform_create(self, serializer): + serializer.save(owner=self.request.user) class ClassesViewSet(viewsets.ModelViewSet): """ @@ -28,7 +45,14 @@ class ClassesViewSet(viewsets.ModelViewSet): """ queryset = Classes.objects.all() serializer_class = ClassesSerializer - permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + permission_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnly] + + def perform_create(self, serializer): + if(self.request.user.groups.filter(name__in=['teachers']).exists() or self.request.user.is_superuser): + serializer.save(owner=self.request.user) + else: + print("UNAUTHORIZED POST") + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class AssignmentViewSet(viewsets.ModelViewSet): @@ -37,12 +61,19 @@ class AssignmentViewSet(viewsets.ModelViewSet): """ queryset = Assignment.objects.all() serializer_class = AssignmentSerializer - permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + permission_classes = [permissions.IsAuthenticated, isTeacher, IsOwnerOrReadOnly] -class DefFilesViewSet(viewsets.ModelViewSet): - """ - API endpoint that allows users to be viewed or edited. - """ - queryset = DefFiles.objects.all() - serializer_class = DefFilesSerializer - permissions_classes = [permissions.IsAuthenticatedOrReadOnly] + def perform_create(self, serializer): + if(self.request.user.groups.filter(name__in=['teachers']).exists() or self.request.user.is_superuser): + serializer.save(owner=self.request.user) + else: + print("UNAUTHORIZED POST") + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +# class DefFilesViewSet(viewsets.ModelViewSet): +# """ +# API endpoint that allows users to be viewed or edited. +# """ +# queryset = DefFiles.objects.all() +# serializer_class = DefFilesSerializer +# permissions_classes = [permissions.IsAuthenticatedOrReadOnly] diff --git a/CLI/.profile b/Website/config/__init__.py similarity index 100% rename from CLI/.profile rename to Website/config/__init__.py diff --git a/Website/skoolos/asgi.py b/Website/config/asgi.py similarity index 82% rename from Website/skoolos/asgi.py rename to Website/config/asgi.py index 9577b84..561756d 100644 --- a/Website/skoolos/asgi.py +++ b/Website/config/asgi.py @@ -11,6 +11,6 @@ import os from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'skoolos.settings') +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') application = get_asgi_application() diff --git a/Website/skoolos/settings.py b/Website/config/settings.py similarity index 94% rename from Website/skoolos/settings.py rename to Website/config/settings.py index b099675..17603a8 100644 --- a/Website/skoolos/settings.py +++ b/Website/config/settings.py @@ -31,6 +31,7 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ + 'skoolos.apps.SkoolosConfig', 'users.apps.UsersConfig', 'django.contrib.admin', 'django.contrib.auth', @@ -41,8 +42,7 @@ INSTALLED_APPS = [ 'rest_framework', 'api', 'crispy_forms', - - + 'django_forms_bootstrap', ] REST_FRAMEWORK = { @@ -63,7 +63,7 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -ROOT_URLCONF = 'skoolos.urls' +ROOT_URLCONF = 'config.urls' TEMPLATES = [ { @@ -81,7 +81,7 @@ TEMPLATES = [ }, ] -WSGI_APPLICATION = 'skoolos.wsgi.application' +WSGI_APPLICATION = 'config.wsgi.application' # Database @@ -135,4 +135,6 @@ STATIC_URL = '/static/' CRISPY_TEMPLATE_PACK = 'bootstrap4' -LOGIN_REDIRECT_URL = '/' \ No newline at end of file +LOGIN_REDIRECT_URL = '/' + +LOGIN_URL = '/login' diff --git a/Website/config/urls.py b/Website/config/urls.py new file mode 100644 index 0000000..420beb6 --- /dev/null +++ b/Website/config/urls.py @@ -0,0 +1,31 @@ +from django.urls import path +from rest_framework import routers +from api import views as api_views +from django.contrib import admin +from django.conf.urls import include +from django.contrib.auth import views as auth_views + +router = routers.DefaultRouter() +router.register(r'students', api_views.StudentViewSet) +router.register(r'teachers', api_views.TeacherViewSet) +router.register(r'assignments', api_views.AssignmentViewSet) +router.register(r'classes', api_views.ClassesViewSet) +# router.register(r'files', api_views.DefFilesViewSet) +router.register(r'users', api_views.UserViewSet) + +from users import views as user_views +from users.forms import LoginForm + +# Wire up our API using automatic URL routing. +# Additionally, we include login URLs for the browsable API. +urlpatterns = [ + path('', include('skoolos.urls')), + path('api/', include(router.urls)), + path('api-auth/', include('rest_framework.urls')), + path('admin/', admin.site.urls), + path('login/', auth_views.LoginView.as_view(template_name='users/login.html', authentication_form=LoginForm), name='login'), + path('logout/', user_views.logout, name='logout'), + path('register/', user_views.register, name='register'), + path('create_account/', user_views.create_account, name='create_account'), + path('callback/', user_views.callback, name='callback'), +] diff --git a/Website/skoolos/wsgi.py b/Website/config/wsgi.py similarity index 82% rename from Website/skoolos/wsgi.py rename to Website/config/wsgi.py index f17107f..6b29610 100644 --- a/Website/skoolos/wsgi.py +++ b/Website/config/wsgi.py @@ -11,6 +11,6 @@ import os from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'skoolos.settings') +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') application = get_wsgi_application() diff --git a/Website/manage.py b/Website/manage.py index f17dda4..58c5f10 100755 --- a/Website/manage.py +++ b/Website/manage.py @@ -5,7 +5,7 @@ import sys def main(): - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'skoolos.settings') + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/Website/skoolos/admin.py b/Website/skoolos/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/Website/skoolos/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/Website/skoolos/apps.py b/Website/skoolos/apps.py new file mode 100644 index 0000000..6d65d67 --- /dev/null +++ b/Website/skoolos/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SkoolosConfig(AppConfig): + name = 'skoolos' diff --git a/Website/skoolos/models.py b/Website/skoolos/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/Website/skoolos/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/Website/skoolos/static/skoolos/Segoe UI Bold Italic.ttf b/Website/skoolos/static/skoolos/Segoe UI Bold Italic.ttf new file mode 100644 index 0000000..d134de8 Binary files /dev/null and b/Website/skoolos/static/skoolos/Segoe UI Bold Italic.ttf differ diff --git a/Website/skoolos/static/skoolos/Segoe UI Bold.ttf b/Website/skoolos/static/skoolos/Segoe UI Bold.ttf new file mode 100644 index 0000000..5723e8b Binary files /dev/null and b/Website/skoolos/static/skoolos/Segoe UI Bold.ttf differ diff --git a/Website/skoolos/static/skoolos/Segoe UI Italic.ttf b/Website/skoolos/static/skoolos/Segoe UI Italic.ttf new file mode 100644 index 0000000..07fdf85 Binary files /dev/null and b/Website/skoolos/static/skoolos/Segoe UI Italic.ttf differ diff --git a/Website/skoolos/static/skoolos/Segoe UI.ttf b/Website/skoolos/static/skoolos/Segoe UI.ttf new file mode 100644 index 0000000..46b3b99 Binary files /dev/null and b/Website/skoolos/static/skoolos/Segoe UI.ttf differ diff --git a/Website/skoolos/static/skoolos/futura.ttf b/Website/skoolos/static/skoolos/futura.ttf new file mode 100644 index 0000000..1ef199e Binary files /dev/null and b/Website/skoolos/static/skoolos/futura.ttf differ diff --git a/Website/skoolos/static/skoolos/styles.css b/Website/skoolos/static/skoolos/styles.css new file mode 100644 index 0000000..8eb8163 --- /dev/null +++ b/Website/skoolos/static/skoolos/styles.css @@ -0,0 +1,100 @@ +@font-face { + font-family: Futura; + src: url(futura.ttf); +} + +@font-face { + font-family: 'Segoe UI'; + src: url('Segoe UI.ttf'); +} + +body { + background: #fafafa; + color: #333333; + margin-top: 5rem; + font-family: 'Segoe UI'; +} + +h1, h2, h3, h4, h5, h6 { + color: #444444; +} + +ul { + margin: 0; +} + +.bg-steel { + background: #3cba54; +} + +.site-header .navbar-nav .nav-link { + color: #cbd5db; +} + +.site-header .navbar-nav .nav-link:hover { + color: #ffffff; +} + +.site-header .navbar-nav .nav-link.active { + font-weight: 500; +} + +.content-section { + background: #ffffff; + padding: 10px 20px; + border: 1px solid #dddddd; + border-radius: 3px; + margin-bottom: 20px; +} + +.article-title { + color: #444444; +} + +a.article-title:hover { + color: #428bca; + text-decoration: none; +} + +.article-content { + white-space: pre-line; +} + +.article-img { + height: 65px; + width: 65px; + margin-right: 16px; +} + +.article-metadata { + padding-bottom: 1px; + margin-bottom: 4px; + border-bottom: 1px solid #e3e3e3 +} + +.article-metadata a:hover { + color: #333; + text-decoration: none; +} + +.article-svg { + width: 25px; + height: 25px; + vertical-align: middle; +} + +.account-img { + height: 125px; + width: 125px; + margin-right: 20px; + margin-bottom: 16px; +} + +.account-heading { + font-size: 2.5rem; +} + +.nounderline { + text-decoration: none !important; + color: black !important; +} diff --git a/Website/skoolos/templates/skoolos/base.html b/Website/skoolos/templates/skoolos/base.html new file mode 100644 index 0000000..ce0d628 --- /dev/null +++ b/Website/skoolos/templates/skoolos/base.html @@ -0,0 +1,52 @@ +{% load static %} + + + + + + + + + + + + + + + SkoolOS + + + + + + {% block content %}{% endblock %} + + + + + + diff --git a/Website/skoolos/templates/skoolos/home.html b/Website/skoolos/templates/skoolos/home.html new file mode 100644 index 0000000..05a211e --- /dev/null +++ b/Website/skoolos/templates/skoolos/home.html @@ -0,0 +1,3 @@ +{% extends "skoolos/base.html" %} +{% block content %} +{% endblock content %} diff --git a/Website/skoolos/tests.py b/Website/skoolos/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/Website/skoolos/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/Website/skoolos/urls.py b/Website/skoolos/urls.py index a1e34a3..a8ffe34 100644 --- a/Website/skoolos/urls.py +++ b/Website/skoolos/urls.py @@ -1,23 +1,9 @@ from django.urls import path -from rest_framework import routers -from api import views as api_views -from django.contrib import admin -from django.conf.urls import include -from django.contrib.auth import views as auth_views -router = routers.DefaultRouter() -router.register(r'students', api_views.StudentViewSet) -router.register(r'teachers', api_views.TeacherViewSet) -router.register(r'assignments', api_views.AssignmentViewSet) -router.register(r'classes', api_views.ClassesViewSet) -router.register(r'files', api_views.DefFilesViewSet) +from . import views - -# Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ - path('api/', include(router.urls)), - path('api-auth/', include('rest_framework.urls')), - path('admin/', admin.site.urls), - path('login/', auth_views.LoginView.as_view(template_name="users/login.html"), name='login') -] \ No newline at end of file + path('', views.home, name='home'), + path('profile/', views.profile, name='profile') +] diff --git a/Website/skoolos/views.py b/Website/skoolos/views.py new file mode 100644 index 0000000..3a3409b --- /dev/null +++ b/Website/skoolos/views.py @@ -0,0 +1,12 @@ +from django.shortcuts import render +from django.contrib.auth.decorators import login_required + +# Create your views here. + +@login_required() +def home (request): + return render(request, "skoolos/home.html") + +@login_required() +def profile (request): + pass diff --git a/Website/skoolsite/urls.py b/Website/skoolsite/urls.py deleted file mode 100644 index e69de29..0000000 diff --git a/Website/users/admin.py b/Website/users/admin.py index 8c38f3f..73093bd 100644 --- a/Website/users/admin.py +++ b/Website/users/admin.py @@ -1,3 +1,5 @@ from django.contrib import admin +from .models import Token # Register your models here. +admin.site.register(Token) diff --git a/Website/users/forms.py b/Website/users/forms.py new file mode 100644 index 0000000..7f4a2f1 --- /dev/null +++ b/Website/users/forms.py @@ -0,0 +1,32 @@ +from django import forms +from django.contrib.auth.models import User +from django.contrib.auth.forms import AuthenticationForm + + +class LoginForm(AuthenticationForm): + username = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Username'})) + password = forms.CharField(widget=forms.PasswordInput(attrs={'placeholder':'Password'})) + +class UserCreationForm(forms.ModelForm): + + username = forms.CharField(disabled=True) + email = forms.EmailField(disabled=True) + first_name = forms.CharField(disabled=True) + last_name = forms.CharField(disabled=True) + isStudent = forms.BooleanField(widget = forms.HiddenInput()) + git = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Git Username'})) + password = forms.CharField(widget=forms.PasswordInput(attrs={'placeholder': 'Password'})) + confirm_password = forms.CharField(widget=forms.PasswordInput(attrs={'placeholder': 'Confirm Password'})) + token = forms.CharField(widget = forms.HiddenInput()) + + def __init__(self, *args, **kwargs): + super(UserCreationForm, self).__init__(*args, **kwargs) + + def clean(self): + cleaned_data = super(UserCreationForm, self).clean() + + return cleaned_data + + class Meta: + model = User + fields = ('username', 'email', 'first_name', 'last_name', 'git', 'password', 'confirm_password') diff --git a/Website/users/migrations/0001_initial.py b/Website/users/migrations/0001_initial.py new file mode 100644 index 0000000..1c06dee --- /dev/null +++ b/Website/users/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 3.0.7 on 2020-06-14 19:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Token', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('username', models.TextField()), + ('email', models.TextField()), + ('first_name', models.TextField()), + ('last_name', models.TextField()), + ('isStudent', models.BooleanField()), + ('token', models.CharField(max_length=255)), + ], + ), + ] diff --git a/Website/users/migrations/0002_auto_20200614_2044.py b/Website/users/migrations/0002_auto_20200614_2044.py new file mode 100644 index 0000000..2f6941c --- /dev/null +++ b/Website/users/migrations/0002_auto_20200614_2044.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.7 on 2020-06-14 20:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='token', + name='isStudent', + field=models.BooleanField(default=True), + ), + ] diff --git a/Website/users/migrations/0003_token_grade.py b/Website/users/migrations/0003_token_grade.py new file mode 100644 index 0000000..4b0788c --- /dev/null +++ b/Website/users/migrations/0003_token_grade.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.7 on 2020-06-14 21:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0002_auto_20200614_2044'), + ] + + operations = [ + migrations.AddField( + model_name='token', + name='grade', + field=models.IntegerField(default=9), + ), + ] diff --git a/Website/users/models.py b/Website/users/models.py index 71a8362..3dcdefa 100644 --- a/Website/users/models.py +++ b/Website/users/models.py @@ -1,3 +1,22 @@ from django.db import models +from uuid import uuid4 + # Create your models here. + +class Token(models.Model): + username = models.TextField() + email = models.TextField() + first_name = models.TextField() + last_name = models.TextField() + grade = models.IntegerField(default=9) + isStudent = models.BooleanField(default=True) + token = models.CharField(max_length=255) + + def save(self, *args, **kwargs): + if not self.token: + self.token = uuid4() + return super(Token, self).save(*args, **kwargs) + + def __str__(self): + return f"{self.username}'s Token"; diff --git a/Website/users/pwd.py b/Website/users/pwd.py new file mode 100644 index 0000000..a91574a --- /dev/null +++ b/Website/users/pwd.py @@ -0,0 +1,6 @@ +import os + +pwd = "heyyy" +path = os.getcwd() +p = os.path.join(path, '../../', 'pwd.txt') +open(p, 'w') \ No newline at end of file diff --git a/Website/users/static/users/styles.css b/Website/users/static/users/styles.css index 7aa3c45..f0b3e1c 100644 --- a/Website/users/static/users/styles.css +++ b/Website/users/static/users/styles.css @@ -99,6 +99,10 @@ body { -moz-osx-font-smoothing: grayscale; } +input:disabled { + background: #d9d9d9; +} + .errorlist { -} \ No newline at end of file +} diff --git a/Website/users/templates/users/base.html b/Website/users/templates/users/base.html index f4dd9e6..f621b1e 100644 --- a/Website/users/templates/users/base.html +++ b/Website/users/templates/users/base.html @@ -18,7 +18,6 @@ {% block content %}{% endblock %} -a diff --git a/Website/users/templates/users/create_account.html b/Website/users/templates/users/create_account.html new file mode 100644 index 0000000..f3f814d --- /dev/null +++ b/Website/users/templates/users/create_account.html @@ -0,0 +1,22 @@ +{% extends "users/base.html" %} +{% load bootstrap_tags %} + +{% block content %} + +{% endblock %} diff --git a/Website/users/templates/users/login.html b/Website/users/templates/users/login.html index 9f71406..185c43b 100644 --- a/Website/users/templates/users/login.html +++ b/Website/users/templates/users/login.html @@ -1,16 +1,38 @@ {% extends "users/base.html" %} -{% load crispy_forms_tags %} +{% load bootstrap_tags %} {% block content %} -
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/Website/users/templates/users/register.html b/Website/users/templates/users/register.html index aefb3cb..2712f93 100644 --- a/Website/users/templates/users/register.html +++ b/Website/users/templates/users/register.html @@ -3,13 +3,20 @@ {% block content %}
- +
+ {% if messages %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endif %} + + + Register with Ion + +
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/Website/users/views.py b/Website/users/views.py index 91ea44a..fa848da 100644 --- a/Website/users/views.py +++ b/Website/users/views.py @@ -1,3 +1,148 @@ -from django.shortcuts import render +import json +import requests + +from django.shortcuts import render, redirect + +from requests_oauthlib import OAuth2Session +from django.contrib import messages + +from .models import Token +from api.models import Student, Teacher + +from .forms import UserCreationForm + +from django.contrib.auth import authenticate +from django.contrib.auth import login as auth_login +from django.contrib.auth import logout as auth_logout +from django.contrib.auth.models import User +from django.contrib.auth.decorators import login_required +import os # Create your views here. +# Thanks Django, what would I do without this comment + +client_id = r'QeZPBSKqdvWFfBv1VYTSv9iFGz5T9pVJtNUjbEr6' +client_secret = r'0Wl3hAIGY9SvYOqTOLUiLNYa4OlCgZYdno9ZbcgCT7RGQ8x2f1l2HzZHsQ7ijC74A0mrOhhCVeZugqAmOADHIv5fHxaa7GqFNtQr11HX9ySTw3DscKsphCVi5P71mlGY' +redirect_uri = 'http://localhost:8000/callback/' +token_url = 'https://ion.tjhsst.edu/oauth/authorize/' +scope=["read"] + +def register(request): + oauth = OAuth2Session(client_id=client_id, redirect_uri=redirect_uri, scope=scope) + authorization_url, state = oauth.authorization_url("https://ion.tjhsst.edu/oauth/authorize/") + + return render(request,"users/register.html", {"authorization_url": authorization_url}) + +def callback (request): + if request.method == "GET": + code = request.GET.get('code') + state = request.GET.get("state") + # Then if we get a response from Ion with the authorization code + if code is not None and state is not None: + print ("made it") + # We send it back to fetch the acess_token + payload = {'grant_type':'authorization_code','code': code,'redirect_uri':redirect_uri,'client_id':client_id,'client_secret':client_secret, 'csrfmiddlewaretoken': state} + token = requests.post("https://ion.tjhsst.edu/oauth/token/", data=payload).json() + headers = {'Authorization': f"Bearer {token['access_token']}"} + print(token) + + # And finally get the user's profile! + profile = requests.get("https://ion.tjhsst.edu/api/profile", headers=headers).json() + print(profile) + username = profile['ion_username'] + email = profile['tj_email'] + first_name = profile['first_name'] + last_name = profile['last_name'] + isStudent = profile['is_student'] + grade = profile['grade']['number'] + + if User.objects.filter(username=username).count() != 0: + messages.success(request, "This user already exists!") + return redirect('/login/') + else: + token = Token(username = username, email = email, first_name = first_name, last_name = last_name, isStudent = isStudent, grade=grade) + token.save() + tokenHash = token.token + print(f'/create_account/?token={tokenHash}') + return redirect(f'/create_account/?token={tokenHash}') + + + messages.warning(request, "Invalid Callback Response") + return redirect('/register/') + + +def create_account (request): + if request.method == "POST": + print("POSTPOSTPOSTPOSTPOSTPOSTPOSTPOST") + form = UserCreationForm(request.POST) + print(form.is_valid()) + print(request.POST) + cleaned_data = form.clean() + if cleaned_data.get('password') == cleaned_data.get('confirm_password'): + token = Token.objects.get(token=cleaned_data.get('token')) + username = token.username + email = token.email + first_name = token.first_name + last_name = token.last_name + isStudent = token.isStudent + grade = token.grade + git = cleaned_data.get('git') + password = cleaned_data.get('password') + + + + user = User.objects.create_user(username=username, + email=email, + first_name=first_name, + last_name=last_name, + password=password) + user.save() + token.delete() + + if isStudent: + profile = Student(user=user, git=git, grade=grade) + else: + profile = Teacher(user=user, git=git) + + profile.save() + + print (user) + messages.success(request, "Your SkoolOS account has successfully been created") + return redirect(f'/login/?username={username}') + else: + print(form.errors) + Token.objects.get(token=request.GET.get('token')).delete() + messages.warning(request, "Passwords did not match!") + return redirect('/register/') + + if request.method == "GET" and Token.objects.filter(token=request.GET.get('token')).count() == 1: + print("GETGETGETGETGETGET") + token = Token.objects.get(token=request.GET.get('token')) + username = token.username + email = token.email + first_name = token.first_name + last_name = token.last_name + isStudent = token.isStudent + grade = token.grade + + initial = { + 'username': username, + 'email': email, + 'first_name': first_name, + 'last_name': last_name, + 'grade': grade, + 'isStudent': isStudent, + 'token': token.token, + } + form = UserCreationForm(initial=initial) + return render(request, 'users/create_account.html', {'form': form}) + + messages.warning(request, "Invalid token") + return redirect('/register/') + + +@login_required +def logout(request): + auth_logout(request) + messages.success(request, "You've been logged out!") + return redirect(request, "/login") diff --git a/chromedriver-linux b/chromedriver-linux new file mode 100755 index 0000000..7d7041d Binary files /dev/null and b/chromedriver-linux differ diff --git a/chromedriver.exe b/chromedriver.exe new file mode 100644 index 0000000..4850c8d Binary files /dev/null and b/chromedriver.exe differ diff --git a/eharris1/APLit_eharris1/BookReport_APLit/guidelines.txt b/eharris1/APLit_eharris1/BookReport_APLit/guidelines.txt deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/APLit_eharris1/Cuisine_APLit/instruct.txt b/eharris1/APLit_eharris1/Cuisine_APLit/instruct.txt deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/APLit_eharris1/Cuisine_APLit/rubric.txt b/eharris1/APLit_eharris1/Cuisine_APLit/rubric.txt deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/APLit_eharris1/Essay1_APLit/practice_essay.txt b/eharris1/APLit_eharris1/Essay1_APLit/practice_essay.txt deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/APLit_eharris1/Essay2_APLit/rubric.txt b/eharris1/APLit_eharris1/Essay2_APLit/rubric.txt deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/APLit_eharris1/Lab3_APLit_eharris1/lab3.txt b/eharris1/APLit_eharris1/Lab3_APLit_eharris1/lab3.txt deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/APLit_eharris1/README.md b/eharris1/APLit_eharris1/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/APLit_eharris1/Test1_APLit/instruct.txt b/eharris1/APLit_eharris1/Test1_APLit/instruct.txt deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/English11_eharris1/Entry1_English11/rubric.txt b/eharris1/English11_eharris1/Entry1_English11/rubric.txt deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/English11_eharris1/Entry1_English11/sample_entry.txt b/eharris1/English11_eharris1/Entry1_English11/sample_entry.txt deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/English11_eharris1/Entry2_English11/instruct.txt b/eharris1/English11_eharris1/Entry2_English11/instruct.txt deleted file mode 100644 index e69de29..0000000 diff --git a/eharris1/English11_eharris1/Journal1_English11/entry.txt b/eharris1/English11_eharris1/Journal1_English11/entry.txt deleted file mode 100644 index 8b13789..0000000 --- a/eharris1/English11_eharris1/Journal1_English11/entry.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/eharris1/English11_eharris1/README.md b/eharris1/English11_eharris1/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/requirements.txt b/requirements.txt index 1110b47..9a6e972 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,7 @@ oauthlib==3.1.0 prompt-toolkit==1.0.14 pyclipper==1.1.0.post3 Pygments==2.6.1 +pyinotify==0.9.6 PyInquirer==1.0.3 pyperclip==1.8.0 pytz==2020.1 diff --git a/runtest.py b/runtest.py deleted file mode 100644 index 837cd82..0000000 --- a/runtest.py +++ /dev/null @@ -1,2 +0,0 @@ -for i in range(0, 100000): - print(i) \ No newline at end of file diff --git a/skoolos.py b/skoolos.py new file mode 100644 index 0000000..f95da87 --- /dev/null +++ b/skoolos.py @@ -0,0 +1,242 @@ +import sys +from urllib.parse import urlparse +import requests +from requests_oauthlib import OAuth2Session +from selenium import webdriver +import os.path +import time +import http.server +import socketserver +from threading import Thread +from werkzeug.urls import url_decode +import pprint +from PyInquirer import prompt, print_json +import json +import os +import argparse + +client_id = r'QeZPBSKqdvWFfBv1VYTSv9iFGz5T9pVJtNUjbEr6' +client_secret = r'0Wl3hAIGY9SvYOqTOLUiLNYa4OlCgZYdno9ZbcgCT7RGQ8x2f1l2HzZHsQ7ijC74A0mrOhhCVeZugqAmOADHIv5fHxaa7GqFNtQr11HX9ySTw3DscKsphCVi5P71mlGY' +redirect_uri = 'http://localhost:8000/callback/' +token_url = 'https://ion.tjhsst.edu/oauth/token/' +scope = ["read"] +USER = "" +PWD = "" + +def main(): + print("") + print("░██████╗██╗░░██╗░█████╗░░█████╗░██╗░░░░░  ░█████╗░░██████╗") + print("██╔════╝██║░██╔╝██╔══██╗██╔══██╗██║░░░░░  ██╔══██╗██╔════╝") + print("╚█████╗░█████═╝░██║░░██║██║░░██║██║░░░░░  ██║░░██║╚█████╗░") + print("░╚═══██╗██╔═██╗░██║░░██║██║░░██║██║░░░░░  ██║░░██║░╚═══██╗") + print("██████╔╝██║░╚██╗╚█████╔╝╚█████╔╝███████╗  ╚█████╔╝██████╔╝") + print("╚═════╝░╚═╝░░╚═╝░╚════╝░░╚════╝░╚══════╝  ░╚════╝░╚═════╝░") + print("") + + if not os.path.exists(".profile"): + try: + URL = "http://127.0.0.1:8000/api/" + r = requests.get(url = URL, auth=('raffukhondaker','hackgroup1')) + print("End service at http://127.0.0.1:8000/ before continuing.") + sys.exit(0) + except: + pass + input("Welcome to SkoolOS. Press any key to create an account") + authenticate() + else: + try: + URL = "http://127.0.0.1:8000/api/" + r = requests.get(url = URL, auth=('raffukhondaker','hackgroup1')) + except: + print("Start Django server first") + sys.exit(0) + f = open('.profile','r') + data = json.loads(f.read()) + f.close() + PWD = data['password'] + USER = data['username'] + if(data['is_student']): + studentCLI() + else: + teacherCLI() + + + + # while True: + # pass +def studentCLI(): + from CLI import student + data = getStudent(USER) + print(data) + student = student.Student(data) + print(student) + +def teacherCLI(): + from CLI.teacher import Teacher + print("fail") + +def getStudent(ion_user): + URL = "http://127.0.0.1:8000/api/students/" + ion_user + "/" + r = requests.get(url = URL, auth=('raffukhondaker','hackgroup1')) + if(r.status_code == 200): + data = r.json() + return data + elif(r.status_code == 404): + return None + print("Make new account!") + elif(r.status_code == 403): + return None + print("Invalid username/password") + else: + return None + print(r.status_code) + +def getDB(url): + r = requests.get(url = url, auth=('raffukhondaker','hackgroup1')) + print("GET:" + str(r.status_code)) + return(r.json()) + +def postDB(data, url): + r = requests.post(url = url, data=data, auth=('raffukhondaker','hackgroup1')) + print("POST:" + str(r.status_code)) + return(r.json()) + +def putDB(data, url): + r = requests.put(url = url, data=data, auth=('raffukhondaker','hackgroup1')) + print("PUT:" + str(r.status_code)) + return(r.json()) + +def delDB(url): + r = requests.delete(url = url, auth=('raffukhondaker','hackgroup1')) + print("DELETE:" + str(r.status_code)) + return None + +def makePass(): + questions = [ + { + 'type': 'password', + 'name': 'pwd', + 'message': 'Enter SkoolOS Password (NOT ION PASSWORD): ', + }, + ] + pwd = prompt(questions)['pwd'] + while(len(pwd) < 7): + print("Password too short (Must be over 6 characters)") + pwd = prompt(questions)['pwd'] + conf = [ + { + 'type': 'password', + 'name': 'pwd', + 'message': 'Re-enter password: ', + }, + ] + pwd2 = prompt(conf)['pwd'] + while(not pwd == pwd2): + print("Passwords do not match.") + pwd2 = prompt(conf)['pwd'] + else: + print("PASSWORD SAVED") + return pwd + +def authenticate(): + oauth = OAuth2Session(client_id=client_id, redirect_uri=redirect_uri, scope=scope) + authorization_url, state = oauth.authorization_url("https://ion.tjhsst.edu/oauth/authorize/") + + cdir = os.getcwd() + #Linux: chromdriver-linux + #Macos: chromdriver-mac + #Windows: chromdriver.exe + if('CLI' in os.getcwd()): + path = os.path.join(os.getcwd(), '../','chromedriver-mac') + else: + path = os.path.join(os.getcwd(), 'chromedriver-mac') + + browser = webdriver.Chrome(path) + web_dir = os.path.join(os.getcwd(), 'CLI', 'oauth') + print(web_dir) + os.chdir(web_dir) + if os.path.exists("index.html"): + os.remove("index.html") + + template = open("template.html", "r") + index = open("index.html", "w") + for line in template: + index.write(line.replace('AUTH_URL', authorization_url)) + template.close() + index.close() + + server = Thread(target=create_server) + server.daemon = True + server.start() + + browser.get("localhost:8000/") + + while "http://localhost:8000/callback/?code" not in browser.current_url: + time.sleep(0.25) + + url = browser.current_url + gets = url_decode(url.replace("http://localhost:8000/callback/?", "")) + while "http://localhost:8000/callback/?code" not in browser.current_url: + time.sleep(0.25) + + url = browser.current_url + gets = url_decode(url.replace("http://localhost:8000/callback/?", "")) + code = gets.get("code") + if state == gets.get("state"): + state = gets.get("state") + print("states good") + browser.quit() + + #print(code) + print(state) + + payload = {'grant_type': 'authorization_code', 'code': code, 'redirect_uri': redirect_uri, 'client_id': client_id, + 'client_secret': client_secret, 'csrfmiddlewaretoken': state} + token = requests.post("https://ion.tjhsst.edu/oauth/token/", data=payload).json() + #print(token) + headers = {'Authorization': f"Bearer {token['access_token']}"} + + # And finally get the user's profile! + profile = requests.get("https://ion.tjhsst.edu/api/profile", headers=headers).json() + + #pprint.pprint(profile) + username = profile['ion_username'] + email = profile['tj_email'] + first_name = profile['first_name'] + last_name = profile['last_name'] + is_student = profile['is_student'] + password = "" + #password creation + + profile = { + 'username':username, + 'email':email, + 'first_name':first_name, + 'last_name':last_name, + 'is_student':is_student, + 'password':password, + } + os.chdir(cdir) + + profileFile = open(".profile", "w") + profileFile.write(json.dumps(profile)) + profileFile.close() + + #try to make password + password = makePass() + profile['password'] = password + profileFile = open(".profile", "w") + profileFile.write(json.dumps(profile)) + profileFile.close() + + sys.exit + +def create_server(): + port = 8000 + handler = http.server.SimpleHTTPRequestHandler + httpd = socketserver.TCPServer(("", port), handler) + print("serving at port:" + str(port)) + httpd.serve_forever() + +if __name__ == "__main__": + main()