Added pillar.api.utils.utcnow() which returns a datetime for 'now'

This replaces pillar.web.utils.datetime_now() and can be used in a wider
setting (since we don't import web stuff in the api, but we do vice versa).
This commit is contained in:
Sybren A. Stüvel 2018-02-13 14:36:05 +01:00
parent d0520484bb
commit f2888069db
15 changed files with 41 additions and 52 deletions

View File

@ -1,12 +1,11 @@
import copy import copy
import logging import logging
import datetime from bson import ObjectId
from bson import ObjectId, tz_util
from eve.methods.get import get from eve.methods.get import get
from flask import Blueprint, current_app, request from flask import Blueprint, current_app, request
from pillar.api import utils from pillar.api import utils
from pillar.api.utils import authentication, authorization from pillar.api.utils import authentication, authorization, utcnow
from werkzeug import exceptions as wz_exceptions from werkzeug import exceptions as wz_exceptions
from pillar.api.projects import utils as proj_utils from pillar.api.projects import utils as proj_utils
@ -282,7 +281,7 @@ def is_home_project(project_id, user_id):
def mark_node_updated(node_id): def mark_node_updated(node_id):
"""Uses pymongo to set the node's _updated to "now".""" """Uses pymongo to set the node's _updated to "now"."""
now = datetime.datetime.now(tz=tz_util.utc) now = utcnow()
nodes_coll = current_app.data.driver.db['nodes'] nodes_coll = current_app.data.driver.db['nodes']
return nodes_coll.update_one({'_id': node_id}, return nodes_coll.update_one({'_id': node_id},

View File

@ -15,7 +15,7 @@ from requests.adapters import HTTPAdapter
from pillar import current_app from pillar import current_app
from pillar.api import service from pillar.api import service
from pillar.api.utils import authentication from pillar.api.utils import authentication, utcnow
from pillar.api.utils.authentication import find_user_in_db, upsert_user from pillar.api.utils.authentication import find_user_in_db, upsert_user
blender_id = Blueprint('blender_id', __name__) blender_id = Blueprint('blender_id', __name__)
@ -171,7 +171,7 @@ def _compute_token_expiry(token_expires_string):
blid_expiry = parser.parse(token_expires_string) blid_expiry = parser.parse(token_expires_string)
blid_expiry = blid_expiry.astimezone(tz_util.utc) blid_expiry = blid_expiry.astimezone(tz_util.utc)
our_expiry = datetime.datetime.now(tz=tz_util.utc) + datetime.timedelta(hours=1) our_expiry = utcnow() + datetime.timedelta(hours=1)
return min(blid_expiry, our_expiry) return min(blid_expiry, our_expiry)

View File

@ -3,7 +3,7 @@ import json
import logging import logging
import os import os
from bson import ObjectId, tz_util from bson import ObjectId
from flask import Blueprint from flask import Blueprint
from flask import abort from flask import abort
from flask import current_app from flask import current_app
@ -161,7 +161,7 @@ def zencoder_notifications():
file_doc['status'] = 'complete' file_doc['status'] = 'complete'
# Force an update of the links on the next load of the file. # Force an update of the links on the next load of the file.
file_doc['link_expires'] = datetime.datetime.now(tz=tz_util.utc) - datetime.timedelta(days=1) file_doc['link_expires'] = utils.utcnow() - datetime.timedelta(days=1)
r, _, _, status = current_app.put_internal('files', file_doc, _id=file_id) r, _, _, status = current_app.put_internal('files', file_doc, _id=file_id)
if status != 200: if status != 200:

View File

@ -9,7 +9,6 @@ import typing
import uuid import uuid
from hashlib import md5 from hashlib import md5
import bson.tz_util
import eve.utils import eve.utils
import pymongo import pymongo
import werkzeug.exceptions as wz_exceptions import werkzeug.exceptions as wz_exceptions
@ -27,7 +26,7 @@ from pillar.api import utils
from pillar.api.file_storage_backends.gcs import GoogleCloudStorageBucket, \ from pillar.api.file_storage_backends.gcs import GoogleCloudStorageBucket, \
GoogleCloudStorageBlob GoogleCloudStorageBlob
from pillar.api.utils import remove_private_keys from pillar.api.utils import remove_private_keys
from pillar.api.utils.authorization import require_login, user_has_role, \ from pillar.api.utils.authorization import require_login, \
user_matches_roles user_matches_roles
from pillar.api.utils.cdn import hash_file_path from pillar.api.utils.cdn import hash_file_path
from pillar.api.utils.encoding import Encoder from pillar.api.utils.encoding import Encoder
@ -419,7 +418,7 @@ def ensure_valid_link(response):
# log.debug('Inspecting link for file %s', response['_id']) # log.debug('Inspecting link for file %s', response['_id'])
# Check link expiry. # Check link expiry.
now = datetime.datetime.now(tz=bson.tz_util.utc) now = utils.utcnow()
if 'link_expires' in response: if 'link_expires' in response:
link_expires = response['link_expires'] link_expires = response['link_expires']
if now < link_expires: if now < link_expires:
@ -502,7 +501,7 @@ def on_pre_get_files(_, lookup):
return return
# Only fetch it if the date got expired. # Only fetch it if the date got expired.
now = datetime.datetime.now(tz=bson.tz_util.utc) now = utils.utcnow()
lookup_expired = lookup.copy() lookup_expired = lookup.copy()
lookup_expired['link_expires'] = {'$lte': now} lookup_expired['link_expires'] = {'$lte': now}
@ -527,7 +526,7 @@ def refresh_links_for_project(project_uuid, chunk_size, expiry_seconds):
# Retrieve expired links. # Retrieve expired links.
files_collection = current_app.data.driver.db['files'] files_collection = current_app.data.driver.db['files']
now = datetime.datetime.now(tz=bson.tz_util.utc) now = utils.utcnow()
expire_before = now + datetime.timedelta(seconds=expiry_seconds) expire_before = now + datetime.timedelta(seconds=expiry_seconds)
log.info('Limiting to links that expire before %s', expire_before) log.info('Limiting to links that expire before %s', expire_before)
@ -556,7 +555,7 @@ def refresh_links_for_backend(backend_name, chunk_size, expiry_seconds):
files_collection = current_app.data.driver.db['files'] files_collection = current_app.data.driver.db['files']
proj_coll = current_app.data.driver.db['projects'] proj_coll = current_app.data.driver.db['projects']
now = datetime.datetime.now(tz=bson.tz_util.utc) now = utils.utcnow()
expire_before = now + datetime.timedelta(seconds=expiry_seconds) expire_before = now + datetime.timedelta(seconds=expiry_seconds)
my_log.info('Limiting to links that expire before %s', expire_before) my_log.info('Limiting to links that expire before %s', expire_before)

View File

@ -1,16 +1,15 @@
"""Code for moving files between backends.""" """Code for moving files between backends."""
import datetime
import logging import logging
import os import os
import tempfile import tempfile
import bson.tz_util
import requests import requests
import requests.exceptions import requests.exceptions
from bson import ObjectId from bson import ObjectId
from flask import current_app from flask import current_app
from pillar.api import utils
from . import stream_to_gcs, generate_all_links, ensure_valid_link from . import stream_to_gcs, generate_all_links, ensure_valid_link
__all__ = ['PrerequisiteNotMetError', 'change_file_storage_backend'] __all__ = ['PrerequisiteNotMetError', 'change_file_storage_backend']
@ -74,8 +73,7 @@ def change_file_storage_backend(file_id, dest_backend):
# Generate new links for the file & all variations. This also saves # Generate new links for the file & all variations. This also saves
# the new backend we set here. # the new backend we set here.
f['backend'] = dest_backend f['backend'] = dest_backend
now = datetime.datetime.now(tz=bson.tz_util.utc) generate_all_links(f, utils.utcnow())
generate_all_links(f, now)
def copy_file_to_backend(file_id, project_id, file_or_var, src_backend, dest_backend): def copy_file_to_backend(file_id, project_id, file_or_var, src_backend, dest_backend):
@ -190,4 +188,4 @@ def gcs_move_to_bucket(file_id, dest_project_id, skip_gcs=False):
# Regenerate the links for this file # Regenerate the links for this file
f['project'] = dest_project_id f['project'] = dest_project_id
generate_all_links(f, now=datetime.datetime.now(tz=bson.tz_util.utc)) generate_all_links(f, now=utils.utcnow())

View File

@ -1,15 +1,16 @@
import base64 import base64
import datetime
import hashlib import hashlib
import logging import logging
import typing import typing
import bcrypt import bcrypt
import datetime
from bson import tz_util
from flask import abort, Blueprint, current_app, jsonify, request from flask import abort, Blueprint, current_app, jsonify, request
from pillar.api.utils.authentication import create_new_user_document from pillar.api.utils.authentication import create_new_user_document
from pillar.api.utils.authentication import make_unique_username from pillar.api.utils.authentication import make_unique_username
from pillar.api.utils.authentication import store_token from pillar.api.utils.authentication import store_token
from pillar.api.utils import utcnow
blueprint = Blueprint('authentication', __name__) blueprint = Blueprint('authentication', __name__)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -96,7 +97,7 @@ def generate_and_store_token(user_id, days=15, prefix=b'') -> dict:
token_bytes = prefix + base64.b64encode(random_bits, altchars=b'xy').strip(b'=') token_bytes = prefix + base64.b64encode(random_bits, altchars=b'xy').strip(b'=')
token = token_bytes.decode('ascii') token = token_bytes.decode('ascii')
token_expiry = datetime.datetime.now(tz=tz_util.utc) + datetime.timedelta(days=days) token_expiry = utcnow() + datetime.timedelta(days=days)
token_data = store_token(user_id, token, token_expiry) token_data = store_token(user_id, token, token_expiry)
# Include the token in the returned document so that it can be stored client-side, # Include the token in the returned document so that it can be stored client-side,

View File

@ -4,7 +4,6 @@ Assumes role names that are given to users by organization membership
start with the string "org-". start with the string "org-".
""" """
import datetime
import logging import logging
import typing import typing
@ -14,7 +13,7 @@ import flask
import werkzeug.exceptions as wz_exceptions import werkzeug.exceptions as wz_exceptions
from pillar import attrs_extra, current_app from pillar import attrs_extra, current_app
from pillar.api.utils import remove_private_keys from pillar.api.utils import remove_private_keys, utcnow
class OrganizationError(Exception): class OrganizationError(Exception):
@ -281,10 +280,9 @@ class OrgManager:
# Join all organization-given roles and roles from the tokens collection. # Join all organization-given roles and roles from the tokens collection.
org_roles = aggr_roles(org_coll, {'members': user_id}) org_roles = aggr_roles(org_coll, {'members': user_id})
self._log.debug('Organization-given roles for user %s: %s', user_id, org_roles) self._log.debug('Organization-given roles for user %s: %s', user_id, org_roles)
now = datetime.datetime.now(bson.tz_util.utc)
token_roles = aggr_roles(tokens_coll, { token_roles = aggr_roles(tokens_coll, {
'user': user_id, 'user': user_id,
'expire_time': {"$gt": now}, 'expire_time': {"$gt": utcnow()},
}) })
self._log.debug('Token-given roles for user %s: %s', user_id, token_roles) self._log.debug('Token-given roles for user %s: %s', user_id, token_roles)
org_roles.update(token_roles) org_roles.update(token_roles)

View File

@ -1,16 +1,14 @@
"""Project patching support.""" """Project patching support."""
import datetime
import logging import logging
import bson.tz_util
import flask import flask
from flask import Blueprint, request from flask import Blueprint, request
import werkzeug.exceptions as wz_exceptions import werkzeug.exceptions as wz_exceptions
from pillar import current_app from pillar import current_app
from pillar.auth import current_user from pillar.auth import current_user
from pillar.api.utils import random_etag, str2id from pillar.api.utils import random_etag, str2id, utcnow
from pillar.api.utils import authorization from pillar.api.utils import authorization
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -60,7 +58,6 @@ def patch_project(project_id: str):
# PATCHing collections, so direct MongoDB modification is used to set # PATCHing collections, so direct MongoDB modification is used to set
# _deleted=False and provide new _etag and _updated values. # _deleted=False and provide new _etag and _updated values.
new_etag = random_etag() new_etag = random_etag()
now = datetime.datetime.now(tz=bson.tz_util.utc)
log.debug('undeleting files before undeleting project %s', pid) log.debug('undeleting files before undeleting project %s', pid)
files_coll = current_app.db('files') files_coll = current_app.db('files')
@ -68,7 +65,7 @@ def patch_project(project_id: str):
{'project': pid}, {'project': pid},
{'$set': {'_deleted': False, {'$set': {'_deleted': False,
'_etag': new_etag, '_etag': new_etag,
'_updated': now}}) '_updated': utcnow()}})
log.info('undeleted %d of %d file documents of project %s', log.info('undeleted %d of %d file documents of project %s',
update_result.modified_count, update_result.matched_count, pid) update_result.modified_count, update_result.matched_count, pid)

View File

@ -10,6 +10,7 @@ import typing
import urllib.request, urllib.parse, urllib.error import urllib.request, urllib.parse, urllib.error
import bson.objectid import bson.objectid
import bson.tz_util
from eve import RFC1123_DATE_FORMAT from eve import RFC1123_DATE_FORMAT
from flask import current_app from flask import current_app
from werkzeug import exceptions as wz_exceptions from werkzeug import exceptions as wz_exceptions
@ -200,3 +201,7 @@ def random_etag() -> str:
randbytes = random.getrandbits(256).to_bytes(32, 'big') randbytes = random.getrandbits(256).to_bytes(32, 'big')
return base64.b64encode(randbytes)[:-1].decode() return base64.b64encode(randbytes)[:-1].decode()
def utcnow() -> datetime.datetime:
return datetime.datetime.now(tz=bson.tz_util.utc)

View File

@ -13,12 +13,11 @@ import logging
import typing import typing
import bson import bson
from bson import tz_util
from flask import g, current_app from flask import g, current_app
from flask import request from flask import request
from werkzeug import exceptions as wz_exceptions from werkzeug import exceptions as wz_exceptions
from pillar.api.utils import remove_private_keys from pillar.api.utils import remove_private_keys, utcnow
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -209,7 +208,7 @@ def find_token(token, is_subclient_token=False, **extra_filters):
# TODO: remove matching on unhashed tokens once all tokens have been hashed. # TODO: remove matching on unhashed tokens once all tokens have been hashed.
lookup = {'$or': [{'token': token}, {'token_hashed': token_hashed}], lookup = {'$or': [{'token': token}, {'token_hashed': token_hashed}],
'is_subclient_token': True if is_subclient_token else {'$in': [False, None]}, 'is_subclient_token': True if is_subclient_token else {'$in': [False, None]},
'expire_time': {"$gt": datetime.datetime.now(tz=tz_util.utc)}} 'expire_time': {"$gt": utcnow()}}
lookup.update(extra_filters) lookup.update(extra_filters)
db_token = tokens_coll.find_one(lookup) db_token = tokens_coll.find_one(lookup)
@ -333,9 +332,7 @@ def _delete_expired_tokens():
token_coll = current_app.data.driver.db['tokens'] token_coll = current_app.data.driver.db['tokens']
now = datetime.datetime.now(tz_util.utc) expiry_date = utcnow() - datetime.timedelta(days=7)
expiry_date = now - datetime.timedelta(days=7)
result = token_coll.delete_many({'expire_time': {"$lt": expiry_date}}) result = token_coll.delete_many({'expire_time': {"$lt": expiry_date}})
# log.debug('Deleted %i expired authentication tokens', result.deleted_count) # log.debug('Deleted %i expired authentication tokens', result.deleted_count)

View File

@ -391,13 +391,11 @@ def expire_all_project_links(project_uuid):
""" """
import datetime import datetime
import bson.tz_util from pillar.api.utils import utcnow
files_collection = current_app.data.driver.db['files'] files_collection = current_app.data.driver.db['files']
now = datetime.datetime.now(tz=bson.tz_util.utc) expires = utcnow() - datetime.timedelta(days=1)
expires = now - datetime.timedelta(days=1)
result = files_collection.update_many( result = files_collection.update_many(
{'project': ObjectId(project_uuid)}, {'project': ObjectId(project_uuid)},
{'$set': {'link_expires': expires}} {'$set': {'link_expires': expires}}

View File

@ -327,8 +327,9 @@ class AbstractPillarTest(TestMinimal):
return user return user
def create_valid_auth_token(self, user_id, token='token'): def create_valid_auth_token(self, user_id, token='token'):
now = datetime.datetime.now(tz_util.utc) from pillar.api.utils import utcnow
future = now + datetime.timedelta(days=1)
future = utcnow() + datetime.timedelta(days=1)
with self.app.test_request_context(): with self.app.test_request_context():
from pillar.api.utils import authentication as auth from pillar.api.utils import authentication as auth

View File

@ -10,10 +10,11 @@ from pillarsdk import Node
from pillarsdk import Project from pillarsdk import Project
import werkzeug.exceptions as wz_exceptions import werkzeug.exceptions as wz_exceptions
from pillar.api.utils import utcnow
from pillar.web import subquery from pillar.web import subquery
from pillar.web.nodes.routes import blueprint from pillar.web.nodes.routes import blueprint
from pillar.web.utils import gravatar from pillar.web.utils import gravatar
from pillar.web.utils import pretty_date, datetime_now from pillar.web.utils import pretty_date
from pillar.web.utils import system_util from pillar.web.utils import system_util
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -111,7 +112,7 @@ def format_comment(comment, is_reply=False, is_team=False, replies=None):
return dict(_id=comment._id, return dict(_id=comment._id,
gravatar=gravatar(comment.user.email, size=32), gravatar=gravatar(comment.user.email, size=32),
time_published=pretty_date(comment._created or datetime_now(), detail=True), time_published=pretty_date(comment._created or utcnow(), detail=True),
rating=comment.properties.rating_positive - comment.properties.rating_negative, rating=comment.properties.rating_positive - comment.properties.rating_negative,
author=comment.user.full_name, author=comment.user.full_name,
author_username=comment.user.username, author_username=comment.user.username,

View File

@ -21,6 +21,7 @@ from flask_login import login_required, current_user
import werkzeug.exceptions as wz_exceptions import werkzeug.exceptions as wz_exceptions
from pillar import current_app from pillar import current_app
from pillar.api.utils import utcnow
from pillar.web import system_util from pillar.web import system_util
from pillar.web import utils from pillar.web import utils
from pillar.web.utils.jstree import jstree_get_children from pillar.web.utils.jstree import jstree_get_children
@ -82,7 +83,7 @@ def index():
show_deleted_projects = request.args.get('deleted') is not None show_deleted_projects = request.args.get('deleted') is not None
if show_deleted_projects: if show_deleted_projects:
timeframe = utils.datetime_now() - datetime.timedelta(days=31) timeframe = utcnow() - datetime.timedelta(days=31)
projects_deleted = Project.all({ projects_deleted = Project.all({
'where': {'user': current_user.objectid, 'where': {'user': current_user.objectid,
'category': {'$ne': 'home'}, 'category': {'$ne': 'home'},

View File

@ -114,12 +114,6 @@ def gravatar(email: str, size=64):
return api_gravatar(email, size) return api_gravatar(email, size)
def datetime_now():
"""Returns a datetime.datetime that represents 'now' in UTC."""
return datetime.datetime.now(tz=pillarsdk.utils.utc)
def pretty_date(time, detail=False, now=None): def pretty_date(time, detail=False, now=None):
"""Get a datetime object or a int() Epoch timestamp and return a """Get a datetime object or a int() Epoch timestamp and return a
pretty string like 'an hour ago', 'Yesterday', '3 months ago', pretty string like 'an hour ago', 'Yesterday', '3 months ago',