Skip to content
This repository has been archived by the owner on Jul 18, 2019. It is now read-only.

Commit

Permalink
Accounts update
Browse files Browse the repository at this point in the history
  • Loading branch information
Igoorx committed Jul 17, 2019
1 parent ad9e9b8 commit b567213
Show file tree
Hide file tree
Showing 6 changed files with 297 additions and 45 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,5 @@ venv.bak/

# local files
blocked.json
server.cfg
server.cfg
server.dat
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ This project uses Python <b>3.7</b> and the following dependencies:
- emoji
- configparser
- jsonschema
- discord_webhook **(OPTIONAL)**
- captcha **(OPTIONAL)**
- argon2 **(OPTIONAL)**

<b>If you are on Windows, the module <u>pypiwin32</u> may also be required.</b>

Expand Down
2 changes: 2 additions & 0 deletions Tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.png
*.wav
113 changes: 113 additions & 0 deletions Tests/captcha_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
'''
Created on Aug 23, 2018
@author: zhaosong
'''

from captcha.image import ImageCaptcha
from captcha.audio import AudioCaptcha
import random
from io import BytesIO

# The number list, lower case character list and upper case character list are used to generate captcha text.
number_list = ['0','1','2','3','4','5','6','7','8','9']

alphabet_lowercase = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']

alphabet_uppercase = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']

# This function will create a random captcha string text based on above three list.
# The input parameter is the captcha text length.
def create_random_captcha_text(captcha_string_size=5):

captcha_string_list = []

base_char = alphabet_lowercase + alphabet_uppercase + number_list

for i in range(captcha_string_size):

# Select one character randomly.
char = random.choice(base_char)

# Append the character to the list.
captcha_string_list.append(char)

captcha_string = ''

# Change the character list to string.
for item in captcha_string_list:
captcha_string += str(item)

return captcha_string

# This function will create a fully digital captcha string text.
def create_random_digital_text(captcha_string_size=5):

captcha_string_list = []
# Loop in the number list and return a digital captcha string list
for i in range(captcha_string_size):
char = random.choice(number_list)
captcha_string_list.append(char)

captcha_string = ''

# Convert the digital list to string.
for item in captcha_string_list:
captcha_string += str(item)

return captcha_string

# Create an image captcha with special text.
def create_image_captcha(captcha_text):
image_captcha = ImageCaptcha()
# Create the captcha image.
image = image_captcha.generate_image(captcha_text)

# Add noise curve for the image.
# image_captcha.create_noise_curve(image, image.getcolors())

# Add noise dots for the image.
# image_captcha.create_noise_dots(image, image.getcolors())

# Save the image to a png file.
image_file = "./captcha_"+captcha_text + ".png"
imgByteArr = BytesIO()
image.save(imgByteArr, format='PNG')
imgByteArr = imgByteArr.getvalue()
open("test.png", "wb").write(imgByteArr)
#image_captcha.write(captcha_text, image_file)

print(image_file + " has been created.")

# Create an audio captcha file.
def create_audio_captcha():

# Create the audio captcha with the specified voice wav file library folder.
# Each captcha char should has it's own directory under the specified folder ( such as ./voices),
# for example ./voices/a/a.wav will be played when the character is a.
# If you do not specify your own voice file library folder, the default built-in voice library which has only digital voice file will be used.
# audio_captcha = AudioCaptcha(voicedir='./voices')

# Create an audio captcha which use digital voice file only.
audio_captcha = AudioCaptcha()

# Because we use the default module voice library, so we can only generate digital text voice.
captcha_text = create_random_digital_text()

# Generate the audio captcha file.
audio_data = audio_captcha.generate(captcha_text)

# Save the autiod captcha file.
audio_file = "./captcha_"+captcha_text+'.wav'
audio_captcha.write(captcha_text, audio_file)

print(audio_file + " has been created.")

if __name__ == '__main__':
# Create random text.
captcha_text = create_random_captcha_text()

# Create image captcha.
create_image_captcha(captcha_text)

# Create audio captcha.
create_audio_captcha()
84 changes: 62 additions & 22 deletions datastore.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import os
import hashlib
import argon2
try:
import argon2
A2_IMPORT = True
except:
# Maybe we can switch to a built-in passwordHasher?
print("Can't import argon2-cffi, accounts functioning will be disabled.")
A2_IMPORT = False
import pickle
import secrets

accounts = {}
session = {}

ph = argon2.PasswordHasher()
if A2_IMPORT:
ph = argon2.PasswordHasher()
else:
ph = None

def loadState():
global accounts
try:
if os.path.exists("server.dat"):
with open("server.dat", "rb") as f:
accounts = pickle.load(f)
#print(str(len(accounts)) + " accounts")
except Exception as e:
print(e)

Expand All @@ -24,59 +32,91 @@ def persistState():
pickle.dump(accounts, f)

def register(username, password):
if len(username) < 5:
return (False, "username too short")
if ph is None:
return False, "account system disabled"
if len(username) < 3:
return False, "username too short"
if len(username) > 20:
return False, "username too long"
if len(password) < 8:
return (False, "password too short")
return False, "password too short"
if len(password) > 120:
return False, "password too long"
if username in accounts:
return (False, "account already registered")
return False, "account already registered"

salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
pwdhash = ph.hash(password.encode('utf-8')+salt)
acc = {"salt" : salt, "pwdhash" : pwdhash, "nickname": username, "skin": 0, "squad": "" }

acc = { "salt": salt,
"pwdhash": pwdhash,
"nickname": username,
"skin": 0,
"squad": "" }
accounts[username] = acc
persistState()

acc2 = acc.copy()
del acc2["salt"]
del acc2["pwdhash"]

token = secrets.token_urlsafe(32)
acc2["session"] = token
session[token] = username
persistState()
return (True, acc2)
acc2["session"] = token
return True, acc2

def login(username, password):
if ph is None:
return False, "account system disabled"

invalidMsg = "invalid user name or password"
if not username in accounts:
return (False, invalidMsg)
if len(username) < 3:
return False, invalidMsg
if len(username) > 20:
return False, invalidMsg
if len(password) < 8:
return False, invalidMsg
if len(password) > 120:
return False, invalidMsg
if username not in accounts:
return False, invalidMsg
acc = accounts[username]

try:
ph.verify(acc["pwdhash"], password.encode('utf-8')+acc["salt"])
except:
return (False, invalidMsg)
return False, invalidMsg

acc2 = acc.copy()
del acc2["salt"]
del acc2["pwdhash"]

token = secrets.token_urlsafe(32)
acc2["session"] = token
session[token] = username
return (True, acc2)
acc2["session"] = token
return True, acc2

def resumeSession(token):
if not token in session:
return (False, "session expired, please log in")
if token not in session:
return False, "session expired, please log in"

username = session[token]
if not username in accounts:
return (False, "invalid user name or password")
if username not in accounts:
return False, "invalid user name or password"
acc = accounts[username]

acc2 = acc.copy()
del acc2["salt"]
del acc2["pwdhash"]

acc2["username"] = username
acc2["session"] = token
return (True, acc2)
return True, acc2

def updateAccount(username, data):
if not username in accounts:
if username not in accounts:
return

acc = accounts[username]
if "nickname" in data:
acc["nickname"] = data["nickname"]
Expand Down
Loading

0 comments on commit b567213

Please sign in to comment.