diff --git a/apps/_scaffold/common.py b/apps/_scaffold/common.py index 6102856ea..428f7499c 100644 --- a/apps/_scaffold/common.py +++ b/apps/_scaffold/common.py @@ -4,8 +4,8 @@ """ import os import sys -import logging from py4web import Session, Cache, Translator, Flash, DAL, Field, action +from py4web.server_adapters.logging_utils import make_logger from py4web.utils.mailer import Mailer from py4web.utils.auth import Auth from py4web.utils.downloader import downloader @@ -17,19 +17,7 @@ # ####################################################### # implement custom loggers form settings.LOGGERS # ####################################################### -logger = logging.getLogger("py4web:" + settings.APP_NAME) -formatter = logging.Formatter( - "%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s" -) -for item in settings.LOGGERS: - level, filename = item.split(":", 1) - if filename in ("stdout", "stderr"): - handler = logging.StreamHandler(getattr(sys, filename)) - else: - handler = logging.FileHandler(filename) - handler.setFormatter(formatter) - logger.setLevel(getattr(logging, level.upper(), "DEBUG")) - logger.addHandler(handler) +logger = make_logger("py4web:" + settings.APP_NAME, settings.LOGGERS) # ####################################################### # connect to db diff --git a/apps/_scaffold/settings.py b/apps/_scaffold/settings.py index f35883e7f..0f81b1bf7 100644 --- a/apps/_scaffold/settings.py +++ b/apps/_scaffold/settings.py @@ -66,7 +66,7 @@ # logger settings LOGGERS = [ "warning:stdout" -] # syntax "severity:filename" filename can be stderr or stdout +] # syntax "severity:filename:format" filename can be stderr or stdout # Disable default login when using OAuth DEFAULT_LOGIN_ENABLED = True diff --git a/apps/fadebook/common.py b/apps/fadebook/common.py index ef28b7259..428f7499c 100644 --- a/apps/fadebook/common.py +++ b/apps/fadebook/common.py @@ -4,31 +4,20 @@ """ import os import sys -import logging from py4web import Session, Cache, Translator, Flash, DAL, Field, action +from py4web.server_adapters.logging_utils import make_logger from py4web.utils.mailer import Mailer from py4web.utils.auth import Auth from py4web.utils.downloader import downloader from pydal.tools.tags import Tags +from pydal.tools.scheduler import Scheduler from py4web.utils.factories import ActionFactory from . import settings # ####################################################### # implement custom loggers form settings.LOGGERS # ####################################################### -logger = logging.getLogger("py4web:" + settings.APP_NAME) -formatter = logging.Formatter( - "%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s" -) -for item in settings.LOGGERS: - level, filename = item.split(":", 1) - if filename in ("stdout", "stderr"): - handler = logging.StreamHandler(getattr(sys, filename)) - else: - handler = logging.FileHandler(filename) - handler.setFormatter(formatter) - logger.setLevel(getattr(logging, level.upper(), "DEBUG")) - logger.addHandler(handler) +logger = make_logger("py4web:" + settings.APP_NAME, settings.LOGGERS) # ####################################################### # connect to db @@ -52,6 +41,7 @@ # ####################################################### if settings.SESSION_TYPE == "cookies": session = Session(secret=settings.SESSION_SECRET_KEY) + elif settings.SESSION_TYPE == "redis": import redis @@ -64,11 +54,13 @@ else cs(k, v, e) ) session = Session(secret=settings.SESSION_SECRET_KEY, storage=conn) + elif settings.SESSION_TYPE == "memcache": import memcache, time conn = memcache.Client(settings.MEMCACHE_CLIENTS, debug=0) session = Session(secret=settings.SESSION_SECRET_KEY, storage=conn) + elif settings.SESSION_TYPE == "database": from py4web.utils.dbstore import DBStore @@ -84,7 +76,7 @@ auth.param.login_after_registration = settings.LOGIN_AFTER_REGISTRATION auth.param.allowed_actions = settings.ALLOWED_ACTIONS auth.param.login_expiration_time = 3600 -auth.param.password_complexity = {"entropy": 0} +auth.param.password_complexity = {"entropy": settings.PASSWORD_ENTROPY} auth.param.block_previous_password_num = 3 auth.param.default_login_enabled = settings.DEFAULT_LOGIN_ENABLED auth.define_tables() @@ -133,6 +125,31 @@ callback_url="auth/plugin/oauth2google/callback", ) ) + +if settings.OAUTH2GOOGLE_SCOPED_CREDENTIALS_FILE: + from py4web.utils.auth_plugins.oauth2google_scoped import ( + OAuth2GoogleScoped, + ) # TESTED + + auth.register_plugin( + OAuth2GoogleScoped( + secrets_file=settings.OAUTH2GOOGLE_SCOPED_CREDENTIALS_FILE, + scopes=[], # Put here any scopes you want in addition to login + db=db, # Needed to store credentials in auth_credentials + ) + ) + +if settings.OAUTH2GITHUB_CLIENT_ID: + from py4web.utils.auth_plugins.oauth2github import OAuth2Github # TESTED + + auth.register_plugin( + OAuth2Github( + client_id=settings.OAUTH2GITHUB_CLIENT_ID, + client_secret=settings.OAUTH2GITHUB_CLIENT_SECRET, + callback_url="auth/plugin/oauth2github/callback", + ) + ) + if settings.OAUTH2FACEBOOK_CLIENT_ID: from py4web.utils.auth_plugins.oauth2facebook import OAuth2Facebook # UNTESTED @@ -160,10 +177,12 @@ # files uploaded and reference by Field(type='upload') # ####################################################### if settings.UPLOAD_FOLDER: - @action('download/') - @action.uses(db) + + @action("download/") + @action.uses(db) def download(filename): - return downloader(db, settings.UPLOAD_FOLDER, filename) + return downloader(db, settings.UPLOAD_FOLDER, filename) + # To take advantage of this in Form(s) # for every field of type upload you MUST specify: # @@ -171,17 +190,15 @@ def download(filename): # field.download_url = lambda filename: URL('download/%s' % filename) # ####################################################### -# Optionally configure celery +# Define and optionally start the scheduler # ####################################################### -if settings.USE_CELERY: - from celery import Celery - - # to use "from .common import scheduler" and then use it according - # to celery docs, examples in tasks.py - scheduler = Celery( - "apps.%s.tasks" % settings.APP_NAME, broker=settings.CELERY_BROKER +if settings.USE_SCHEDULER: + scheduler = Scheduler( + db, logger=logger, max_concurrent_runs=settings.SCHEDULER_MAX_CONCURRENT_RUNS ) - + scheduler.start() +else: + scheduler = None # ####################################################### # Enable authentication @@ -190,6 +207,8 @@ def download(filename): # ####################################################### # Define convenience decorators +# They can be used instead of @action and @action.uses +# They should NEVER BE MIXED with @action and @action.uses # ####################################################### unauthenticated = ActionFactory(db, session, T, flash, auth) authenticated = ActionFactory(db, session, T, flash, auth.user) diff --git a/apps/fadebook/settings.py b/apps/fadebook/settings.py index 34e90c65d..0f81b1bf7 100644 --- a/apps/fadebook/settings.py +++ b/apps/fadebook/settings.py @@ -8,16 +8,20 @@ import os from py4web.core import required_folder +# mode (default or development) +MODE = os.environ.get("PY4WEB_MODE") + # db settings APP_FOLDER = os.path.dirname(__file__) APP_NAME = os.path.split(APP_FOLDER)[-1] + # DB_FOLDER: Sets the place where migration files will be created # and is the store location for SQLite databases DB_FOLDER = required_folder(APP_FOLDER, "databases") DB_URI = "sqlite://storage.db" DB_POOL_SIZE = 1 DB_MIGRATE = True -DB_FAKE_MIGRATE = False # maybe? +DB_FAKE_MIGRATE = False # location where static files are stored: STATIC_FOLDER = required_folder(APP_FOLDER, "static") @@ -26,17 +30,20 @@ UPLOAD_FOLDER = required_folder(APP_FOLDER, "uploads") # send verification email on registration -VERIFY_EMAIL = False +VERIFY_EMAIL = MODE != "development" + +# complexity of the password 0: no constraints, 50: safe! +PASSWORD_ENTROPY = 0 if MODE == "development" else 50 # account requires to be approved ? REQUIRES_APPROVAL = False # auto login after registration -# requires False VERIFY_EMAIL & REQUIRES_APPROVAL -LOGIN_AFTER_REGISTRATION = True +# requires False VERIFY_EMAIL & REQUIRES_APPROVAL +LOGIN_AFTER_REGISTRATION = False # ALLOWED_ACTIONS in API / default Forms: -# ["all"] +# ["all"] # ["login", "logout", "request_reset_password", "reset_password", \ # "change_password", "change_email", "profile", "config", "register", # "verify_email", "unsubscribe"] @@ -52,14 +59,14 @@ # session settings SESSION_TYPE = "cookies" -SESSION_SECRET_KEY = None # or replace with your own secret +SESSION_SECRET_KEY = None # or replace with your own secret MEMCACHE_CLIENTS = ["127.0.0.1:11211"] REDIS_SERVER = "localhost:6379" # logger settings LOGGERS = [ "warning:stdout" -] # syntax "severity:filename" filename can be stderr or stdout +] # syntax "severity:filename:format" filename can be stderr or stdout # Disable default login when using OAuth DEFAULT_LOGIN_ENABLED = True @@ -68,6 +75,10 @@ OAUTH2GOOGLE_CLIENT_ID = None OAUTH2GOOGLE_CLIENT_SECRET = None +# Single sign on Google, with stored credentials for scopes (will be used if provided). +# set it to something like os.path.join(APP_FOLDER, "private/credentials.json" +OAUTH2GOOGLE_SCOPED_CREDENTIALS_FILE = None + # single sign on Okta (will be used if provided. Please also add your tenant # name to py4web/utils/auth_plugins/oauth2okta.py. You can replace the XXX # instances with your tenant name.) @@ -78,6 +89,10 @@ OAUTH2FACEBOOK_CLIENT_ID = None OAUTH2FACEBOOK_CLIENT_SECRET = None +# single sign on GitHub (will be used if provided) +OAUTH2GITHUB_CLIENT_ID = None +OAUTH2GITHUB_CLIENT_SECRET = None + # enable PAM USE_PAM = False @@ -85,14 +100,18 @@ USE_LDAP = False LDAP_SETTINGS = { "mode": "ad", # Microsoft Active Directory - "server": "mydc.domain.com", # FQDN or IP of one Domain Controller - "base_dn": "cn=Users,dc=domain,dc=com", # base dn, i.e. where the users are located + "server": "mydc.domain.com", # FQDN or IP of one Domain Controller + "base_dn": "cn=Users,dc=domain,dc=com", # base dn, i.e. where the users are located } # i18n settings T_FOLDER = required_folder(APP_FOLDER, "translations") -# Celery settings +# Scheduler settings +USE_SCHEDULER = False +SCHEDULER_MAX_CONCURRENT_RUNS = 1 + +# Celery settings (alternative to the build-in scheduler) USE_CELERY = False CELERY_BROKER = "redis://localhost:6379/0" diff --git a/apps/tagged_posts/common.py b/apps/tagged_posts/common.py index bf3bc5443..428f7499c 100644 --- a/apps/tagged_posts/common.py +++ b/apps/tagged_posts/common.py @@ -4,31 +4,20 @@ """ import os import sys -import logging from py4web import Session, Cache, Translator, Flash, DAL, Field, action +from py4web.server_adapters.logging_utils import make_logger from py4web.utils.mailer import Mailer from py4web.utils.auth import Auth from py4web.utils.downloader import downloader from pydal.tools.tags import Tags +from pydal.tools.scheduler import Scheduler from py4web.utils.factories import ActionFactory from . import settings # ####################################################### # implement custom loggers form settings.LOGGERS # ####################################################### -logger = logging.getLogger("py4web:" + settings.APP_NAME) -formatter = logging.Formatter( - "%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s" -) -for item in settings.LOGGERS: - level, filename = item.split(":", 1) - if filename in ("stdout", "stderr"): - handler = logging.StreamHandler(getattr(sys, filename)) - else: - handler = logging.FileHandler(filename) - handler.setFormatter(formatter) - logger.setLevel(getattr(logging, level.upper(), "DEBUG")) - logger.addHandler(handler) +logger = make_logger("py4web:" + settings.APP_NAME, settings.LOGGERS) # ####################################################### # connect to db @@ -52,6 +41,7 @@ # ####################################################### if settings.SESSION_TYPE == "cookies": session = Session(secret=settings.SESSION_SECRET_KEY) + elif settings.SESSION_TYPE == "redis": import redis @@ -64,11 +54,13 @@ else cs(k, v, e) ) session = Session(secret=settings.SESSION_SECRET_KEY, storage=conn) + elif settings.SESSION_TYPE == "memcache": import memcache, time conn = memcache.Client(settings.MEMCACHE_CLIENTS, debug=0) session = Session(secret=settings.SESSION_SECRET_KEY, storage=conn) + elif settings.SESSION_TYPE == "database": from py4web.utils.dbstore import DBStore @@ -84,7 +76,7 @@ auth.param.login_after_registration = settings.LOGIN_AFTER_REGISTRATION auth.param.allowed_actions = settings.ALLOWED_ACTIONS auth.param.login_expiration_time = 3600 -auth.param.password_complexity = {"entropy": 0} +auth.param.password_complexity = {"entropy": settings.PASSWORD_ENTROPY} auth.param.block_previous_password_num = 3 auth.param.default_login_enabled = settings.DEFAULT_LOGIN_ENABLED auth.define_tables() @@ -135,13 +127,15 @@ ) if settings.OAUTH2GOOGLE_SCOPED_CREDENTIALS_FILE: - from py4web.utils.auth_plugins.oauth2google_scoped import OAuth2GoogleScoped # TESTED + from py4web.utils.auth_plugins.oauth2google_scoped import ( + OAuth2GoogleScoped, + ) # TESTED auth.register_plugin( OAuth2GoogleScoped( secrets_file=settings.OAUTH2GOOGLE_SCOPED_CREDENTIALS_FILE, - scopes=[], # Put here any scopes you want in addition to login - db=db, # Needed to store credentials in auth_credentials + scopes=[], # Put here any scopes you want in addition to login + db=db, # Needed to store credentials in auth_credentials ) ) @@ -183,10 +177,12 @@ # files uploaded and reference by Field(type='upload') # ####################################################### if settings.UPLOAD_FOLDER: - @action('download/') + + @action("download/") @action.uses(db) def download(filename): return downloader(db, settings.UPLOAD_FOLDER, filename) + # To take advantage of this in Form(s) # for every field of type upload you MUST specify: # @@ -194,17 +190,15 @@ def download(filename): # field.download_url = lambda filename: URL('download/%s' % filename) # ####################################################### -# Optionally configure celery +# Define and optionally start the scheduler # ####################################################### -if settings.USE_CELERY: - from celery import Celery - - # to use "from .common import scheduler" and then use it according - # to celery docs, examples in tasks.py - scheduler = Celery( - "apps.%s.tasks" % settings.APP_NAME, broker=settings.CELERY_BROKER +if settings.USE_SCHEDULER: + scheduler = Scheduler( + db, logger=logger, max_concurrent_runs=settings.SCHEDULER_MAX_CONCURRENT_RUNS ) - + scheduler.start() +else: + scheduler = None # ####################################################### # Enable authentication @@ -213,6 +207,8 @@ def download(filename): # ####################################################### # Define convenience decorators +# They can be used instead of @action and @action.uses +# They should NEVER BE MIXED with @action and @action.uses # ####################################################### unauthenticated = ActionFactory(db, session, T, flash, auth) authenticated = ActionFactory(db, session, T, flash, auth.user) diff --git a/apps/tagged_posts/settings.py b/apps/tagged_posts/settings.py index 77bb95a4e..0f81b1bf7 100644 --- a/apps/tagged_posts/settings.py +++ b/apps/tagged_posts/settings.py @@ -8,16 +8,20 @@ import os from py4web.core import required_folder +# mode (default or development) +MODE = os.environ.get("PY4WEB_MODE") + # db settings APP_FOLDER = os.path.dirname(__file__) APP_NAME = os.path.split(APP_FOLDER)[-1] + # DB_FOLDER: Sets the place where migration files will be created # and is the store location for SQLite databases DB_FOLDER = required_folder(APP_FOLDER, "databases") DB_URI = "sqlite://storage.db" DB_POOL_SIZE = 1 DB_MIGRATE = True -DB_FAKE_MIGRATE = False # maybe? +DB_FAKE_MIGRATE = False # location where static files are stored: STATIC_FOLDER = required_folder(APP_FOLDER, "static") @@ -26,7 +30,10 @@ UPLOAD_FOLDER = required_folder(APP_FOLDER, "uploads") # send verification email on registration -VERIFY_EMAIL = True +VERIFY_EMAIL = MODE != "development" + +# complexity of the password 0: no constraints, 50: safe! +PASSWORD_ENTROPY = 0 if MODE == "development" else 50 # account requires to be approved ? REQUIRES_APPROVAL = False @@ -52,14 +59,14 @@ # session settings SESSION_TYPE = "cookies" -SESSION_SECRET_KEY = None # or replace with your own secret +SESSION_SECRET_KEY = None # or replace with your own secret MEMCACHE_CLIENTS = ["127.0.0.1:11211"] REDIS_SERVER = "localhost:6379" # logger settings LOGGERS = [ "warning:stdout" -] # syntax "severity:filename" filename can be stderr or stdout +] # syntax "severity:filename:format" filename can be stderr or stdout # Disable default login when using OAuth DEFAULT_LOGIN_ENABLED = True @@ -93,14 +100,18 @@ USE_LDAP = False LDAP_SETTINGS = { "mode": "ad", # Microsoft Active Directory - "server": "mydc.domain.com", # FQDN or IP of one Domain Controller - "base_dn": "cn=Users,dc=domain,dc=com", # base dn, i.e. where the users are located + "server": "mydc.domain.com", # FQDN or IP of one Domain Controller + "base_dn": "cn=Users,dc=domain,dc=com", # base dn, i.e. where the users are located } # i18n settings T_FOLDER = required_folder(APP_FOLDER, "translations") -# Celery settings +# Scheduler settings +USE_SCHEDULER = False +SCHEDULER_MAX_CONCURRENT_RUNS = 1 + +# Celery settings (alternative to the build-in scheduler) USE_CELERY = False CELERY_BROKER = "redis://localhost:6379/0"