Пример #1
0
def createNewUserIfNotExists():
	"""
		Create a new Admin user, if the userDB is empty
	"""
	userMod = getattr(conf["viur.mainApp"], "user", None)
	if (userMod  # We have a user module
			and isinstance(userMod, User)
			and "addSkel" in dir(userMod)
			and "validAuthenticationMethods" in dir(userMod)  # Its our user module :)
			and any([issubclass(x[0], UserPassword) for x in userMod.validAuthenticationMethods])):  # It uses UserPassword login
		if not db.Query(userMod.addSkel().kindName).getEntry():  # There's currently no user in the database
			addSkel = skeletonByKind(userMod.addSkel().kindName)()  # Ensure we have the full skeleton
			uname = "admin@%s.appspot.com" % utils.projectID
			pw = utils.generateRandomString(13)
			addSkel["name"] = uname
			addSkel["status"] = 10  # Ensure its enabled right away
			addSkel["access"] = ["root"]
			addSkel["password"] = pw

			try:
				addSkel.toDB()
			except Exception as e:
				logging.error("Something went wrong when trying to add admin user %s with Password %s", uname, pw)
				logging.exception(e)
				return
			logging.warning("ViUR created a new admin-user for you! Username: %s, Password: %s", uname, pw)
			utils.sendEMailToAdmins("Your new ViUR password",
									"ViUR created a new admin-user for you! Username: %s, Password: %s" % (uname, pw))
Пример #2
0
 def fromClient(self, skel: 'SkeletonInstance', name: str,
                data: dict) -> Union[None, List[ReadFromClientError]]:
     if not name in data:
         return [
             ReadFromClientError(ReadFromClientErrorSeverity.NotSet,
                                 "Field not submitted")
         ]
     value = data.get(name)
     if not value:
         # Password-Bone is special: As it cannot be read don't set back no None if no value is given
         # This means an once set password can only be changed - but never deleted.
         return [
             ReadFromClientError(ReadFromClientErrorSeverity.Empty,
                                 "No value entered")
         ]
     err = self.isInvalid(value)
     if err:
         return [
             ReadFromClientError(ReadFromClientErrorSeverity.Invalid, err)
         ]
     # As we don't escape passwords and allow most special characters we'll hash it early on so we don't open
     # an XSS attack vector if a password is echoed back to the client (which should not happen)
     salt = utils.generateRandomString(self.saltLength)
     passwd = pbkdf2(value[:conf["viur.maxPasswordLength"]], salt)
     skel[name] = {"pwhash": passwd, "salt": salt}
Пример #3
0
 def createUploadURL(
         self, node: Union[str, None]) -> Tuple[str, str, Dict[str, str]]:
     global bucket
     targetKey = utils.generateRandomString()
     conditions = [["starts-with", "$key", "%s/source/" % targetKey]]
     if isinstance(credentials, ServiceAccountCredentials
                   ):  # We run locally with an service-account.json
         policy = bucket.generate_upload_policy(conditions)
     else:  # Use our fixed PolicyGenerator - Google is currently unable to create one itself on its GCE
         policy = self.generateUploadPolicy(conditions)
     uploadUrl = "https://%s.storage.googleapis.com" % bucket.name
     # Create a correspondingfile-lock object early, otherwise we would have to ensure that the file-lock object
     # the user creates matches the file he had uploaded
     fileSkel = self.addSkel(TreeType.Leaf)
     fileSkel["key"] = targetKey
     fileSkel["name"] = "pending"
     fileSkel["size"] = 0
     fileSkel["mimetype"] = "application/octetstream"
     fileSkel["dlkey"] = targetKey
     fileSkel["parentdir"] = None
     fileSkel["pendingparententry"] = db.keyHelper(
         node,
         self.addSkel(TreeType.Node).kindName) if node else None
     fileSkel["pending"] = True
     fileSkel["weak"] = True
     fileSkel["width"] = 0
     fileSkel["height"] = 0
     fileSkel.toDB()
     # Mark that entry dirty as we might never receive an add
     utils.markFileForDeletion(targetKey)
     return targetKey, uploadUrl, policy
Пример #4
0
    def createUploadURL(
            self, node: Union[str, None]) -> Tuple[str, str, Dict[str, str]]:
        targetKey = utils.generateRandomString()
        conditions = [["starts-with", "$key", "%s/source/" % targetKey]]

        policy = bucket.generate_upload_policy(conditions)
        uploadUrl = "https://%s.storage.googleapis.com" % bucket.name

        # Create a correspondingfile-lock object early, otherwise we would have to ensure that the file-lock object
        # the user creates matches the file he had uploaded

        fileSkel = self.addLeafSkel()
        fileSkel["key"] = targetKey
        fileSkel["name"] = "pending"
        fileSkel["size"] = 0
        fileSkel["mimetype"] = "application/octetstream"
        fileSkel["dlkey"] = targetKey
        fileSkel["parentdir"] = None
        fileSkel["pendingParentdir"] = db.keyHelper(
            node,
            self.addNodeSkel().kindName) if node else None
        fileSkel["pending"] = True
        fileSkel["weak"] = True
        fileSkel["width"] = 0
        fileSkel["height"] = 0
        fileSkel[""] = ""
        fileSkel.toDB()
        # Mark that entry dirty as we might never receive an add
        utils.markFileForDeletion(targetKey)
        return targetKey, uploadUrl, policy
Пример #5
0
    def write(self,
              filename: str,
              content: Any,
              mimetype: str = "text/plain",
              width: int = None,
              height: int = None) -> db.Key:
        """
		Write a file from any buffer into the file module.

		:param filename: Filename to be written.
		:param content:  The file content to be written, as bytes-like object.
		:param mimetype: The file's mimetype.
		:param width: Optional width information for the file.
		:param height: Optional height information for the file.

		:return: Returns the key of the file object written. This can be associated e.g. with a fileBone.
		"""
        dl_key = utils.generateRandomString()

        blob = bucket.blob("%s/source/%s" % (dl_key, filename))
        blob.upload_from_file(BytesIO(content), content_type=mimetype)

        skel = self.addSkel("leaf")
        skel["name"] = filename
        skel["size"] = blob.size
        skel["mimetype"] = mimetype
        skel["dlkey"] = dl_key
        skel["weak"] = True
        skel["width"] = width
        skel["height"] = height

        return skel.toDB()
Пример #6
0
 def __init__(self, request: webob.Request, response: webob.Response):
     super()
     self.startTime = time()
     self.request = request
     self.response = response
     self.maxLogLevel = logging.DEBUG
     self._traceID = request.headers.get(
         'X-Cloud-Trace-Context') or utils.generateRandomString()
Пример #7
0
	def validateSecurityKey(self, key):
		"""
		Checks if key matches the current CSRF-Token of our session. On success, a new key is generated.
		"""
		if compare_digest(self.securityKey, key):
			self.securityKey = utils.generateRandomString(13)
			self.changed = True
			return True
		return False
Пример #8
0
			def exchangeSecurityKey():
				dbSession = db.Get(db.Key(self.kindName, self.cookieKey))
				if not dbSession:  # Should not happen (except if session.reset has been called in the same request)
					return False
				if dbSession["securityKey"] != key:  # Race-Condidtion: That skey has been used in another instance
					return False
				dbSession["securityKey"] = utils.generateRandomString(13)
				db.Put(dbSession)
				return dbSession["securityKey"]
Пример #9
0
 def serialize(self, skeletonValues, name):
     if name in skeletonValues.accessedValues and skeletonValues.accessedValues[
             name]:
         salt = utils.generateRandomString(self.saltLength)
         passwd = pbkdf2(
             skeletonValues.accessedValues[name]
             [:conf["viur.maxPasswordLength"]], salt)
         skeletonValues.entity[name] = {"pwhash": passwd, "salt": salt}
         return True
     return False
Пример #10
0
	def reset(self):
		"""
			Invalids the current session and starts a new one.

			This function is especially useful at login, where
			we might need to create an SSL-capable session.

			:warning: Everything (except the current language) is flushed.
		"""
		lang = self.session.get("language")
		if self.cookieKey:
			db.Delete(db.Key(self.kindName, self.cookieKey))
		self.cookieKey = utils.generateRandomString(42)
		self.staticSecurityKey = utils.generateRandomString(13)
		self.securityKey = utils.generateRandomString(13)
		self.changed = True
		self.isInitial = True
		self.session = db.Entity()
		if lang:
			self.session["language"] = lang
Пример #11
0
 def serialize(self, skel: 'SkeletonInstance', name: str,
               parentIndexed: bool) -> bool:
     if name in skel.accessedValues and skel.accessedValues[name]:
         salt = utils.generateRandomString(self.saltLength)
         passwd = pbkdf2(
             skel.accessedValues[name][:conf["viur.maxPasswordLength"]],
             salt)
         skel.dbEntity[name] = {"pwhash": passwd, "salt": salt}
         # Ensure our indexed flag is up2date
         indexed = self.indexed and parentIndexed
         if indexed and name in skel.dbEntity.exclude_from_indexes:
             skel.dbEntity.exclude_from_indexes.discard(name)
         elif not indexed and name not in skel.dbEntity.exclude_from_indexes:
             skel.dbEntity.exclude_from_indexes.add(name)
         return True
     return False
Пример #12
0
def create(duration: Union[None, int] = None, **kwargs) -> str:
    """
		Creates a new onetime Securitykey or returns the current sessions csrf-token.
		The custom data (given as keyword arguments) that can be stored with the key if :param:duration is set must
		be serializable by the datastore.

		:param duration: Make this key valid for a fixed timeframe (and independent of the current session)
		:returns: The new onetime key
	"""
    if not duration:
        return currentSession.get().getSecurityKey()
    key = generateRandomString()
    duration = int(duration)
    dbObj = db.Entity(db.Key(securityKeyKindName, key))
    for k, v in kwargs.items():
        dbObj[k] = v
    dbObj["until"] = utcNow() + timedelta(seconds=duration)
    db.Put(dbObj)
    return key
Пример #13
0
 def serialize(self, skel: 'SkeletonInstance', name: str,
               parentIndexed: bool) -> bool:
     if name in skel.accessedValues and skel.accessedValues[name]:
         value = skel.accessedValues[name]
         if isinstance(
                 value,
                 dict):  # It is a pre-hashed value (probably fromClient)
             skel.dbEntity[name] = value
         else:  # This has been set by skel["password"] = "******", we'll still have to hash it
             salt = utils.generateRandomString(self.saltLength)
             passwd = pbkdf2(value[:conf["viur.maxPasswordLength"]], salt)
             skel.dbEntity[name] = {"pwhash": passwd, "salt": salt}
         # Ensure our indexed flag is up2date
         indexed = self.indexed and parentIndexed
         if indexed and name in skel.dbEntity.exclude_from_indexes:
             skel.dbEntity.exclude_from_indexes.discard(name)
         elif not indexed and name not in skel.dbEntity.exclude_from_indexes:
             skel.dbEntity.exclude_from_indexes.add(name)
         return True
     return False
Пример #14
0
def create(duration: Union[None, int] = None, **kwargs):
    """
		Creates a new onetime Securitykey for the current session
		If duration is not set, this key is valid only for the current session.
		Otherwise, the key and its data is serialized and saved inside the datastore
		for up to duration-seconds

		:param duration: Make this key valid for a fixed timeframe (and independend of the current session)
		:type duration: int or None
		:returns: The new onetime key
	"""
    if not duration:
        return currentSession.getSecurityKey()
    key = generateRandomString()
    duration = int(duration)
    dbObj = db.Entity(securityKeyKindName, name=key)
    for k, v in kwargs.items():
        dbObj[k] = v
    dbObj["until"] = datetime.now() + timedelta(seconds=duration)
    db.Put(dbObj)
    return key
Пример #15
0
    def initializeUpload(self,
                         fileName: str,
                         mimeType: str,
                         node: Union[str, None],
                         size: Union[int, None] = None) -> Tuple[str, str]:
        """
		Internal helper that registers a new upload. Will create the pending fileSkel entry (needed to remove any
		started uploads from GCS if that file isn't used) and creates a resumable (and signed) uploadURL for that.
		:param fileName: Name of the file that will be uploaded
		:param mimeType: Mimetype of said file
		:param node: If set (to a string-key representation of a file-node) the upload will be written to this directory
		:param size: The *exact* filesize we're accepting in Bytes. Used to enforce a filesize limit by getUploadURL
		:return: Str-Key of the new file-leaf entry, the signed upload-url
		"""
        global bucket
        fileName = sanitizeFileName(fileName)
        targetKey = utils.generateRandomString()
        blob = bucket.blob("%s/source/%s" % (targetKey, fileName))
        uploadUrl = blob.create_resumable_upload_session(content_type=mimeType,
                                                         size=size,
                                                         timeout=60)
        # Create a corresponding file-lock object early, otherwise we would have to ensure that the file-lock object
        # the user creates matches the file he had uploaded
        fileSkel = self.addSkel("leaf")
        fileSkel["name"] = "pending"
        fileSkel["size"] = 0
        fileSkel["mimetype"] = "application/octetstream"
        fileSkel["dlkey"] = targetKey
        fileSkel["parentdir"] = None
        fileSkel["pendingparententry"] = db.keyHelper(
            node,
            self.addSkel("node").kindName) if node else None
        fileSkel["pending"] = True
        fileSkel["weak"] = True
        fileSkel["width"] = 0
        fileSkel["height"] = 0
        fileSkel.toDB()
        # Mark that entry dirty as we might never receive an add
        utils.markFileForDeletion(targetKey)
        return db.encodeKey(fileSkel["key"]), uploadUrl
Пример #16
0
    def pwrecover(self, *args, **kwargs):
        """
			This implements the password recovery process which let them set a new password for their account
			after validating a code send to them by email. The process is as following:

			- The user enters his email adress
			- We'll generate a random code, store it in his session and call sendUserPasswordRecoveryCode
			- sendUserPasswordRecoveryCode will run in the background, check if we have a user with that name
			  and send the code. It runs as a deferredTask so we don't leak the information if a user account exists.
			- If the user received his code, he can paste the code and set a new password for his account.

			To prevent automated attacks, the fist step is guarded by a captcha and we limited calls to this function
			to 10 actions per 15 minutes. (One complete recovery process consists of two calls).
		"""
        if not self.passwordRecoveryRateLimit.isQuotaAvailable():
            raise errors.Forbidden()  # Quota exhausted, bail out
        session = currentSession.get()
        request = currentRequest.get()
        recoverStep = session.get("user.auth_userpassword.pwrecover")
        if not recoverStep:
            # This is the first step, where we ask for the username of the account we'll going to reset the password on
            skel = self.lostPasswordStep1Skel()
            if not request.isPostRequest or not skel.fromClient(kwargs):
                return self.userModule.render.edit(
                    skel, self.passwordRecoveryStep1Template)
            if not securitykey.validate(kwargs.get("skey"),
                                        useSessionKey=True):
                raise errors.PreconditionFailed()
            self.passwordRecoveryRateLimit.decrementQuota()
            recoveryKey = utils.generateRandomString(
                13)  # This is the key the user will have to Copy&Paste
            self.sendUserPasswordRecoveryCode(
                skel["name"].lower(),
                recoveryKey)  # Send the code in the background
            session["user.auth_userpassword.pwrecover"] = {
                "name": skel["name"].lower(),
                "recoveryKey": recoveryKey,
                "creationdate": utcNow(),
                "errorCount": 0
            }
            del recoveryKey
            return self.pwrecover(
            )  # Fall through to the second step as that key in the session is now set
        else:
            if request.isPostRequest and kwargs.get("abort") == "1" \
             and securitykey.validate(kwargs.get("skey"), useSessionKey=True):
                # Allow a user to abort the process if a wrong email has been used
                session["user.auth_userpassword.pwrecover"] = None
                return self.pwrecover()
            # We're in the second step - the code has been send and is waiting for confirmation from the user
            if utcNow() - session["user.auth_userpassword.pwrecover"][
                    "creationdate"] > datetime.timedelta(minutes=15):
                # This recovery-process is expired; reset the session and start over
                session["user.auth_userpassword.pwrecover"] = None
                return self.userModule.render.view(
                    skel=None,
                    tpl=self.passwordRecoveryFailedTemplate,
                    reason=self.passwordRecoveryKeyExpired)
            skel = self.lostPasswordStep2Skel()
            if not skel.fromClient(kwargs) or not request.isPostRequest:
                return self.userModule.render.edit(
                    skel, self.passwordRecoveryStep2Template)
            if not securitykey.validate(kwargs.get("skey"),
                                        useSessionKey=True):
                raise errors.PreconditionFailed()
            self.passwordRecoveryRateLimit.decrementQuota()
            if not hmac.compare_digest(
                    session["user.auth_userpassword.pwrecover"]["recoveryKey"],
                    skel["recoveryKey"]):
                # The key was invalid, increase error-count or abort this recovery process altogether
                session["user.auth_userpassword.pwrecover"]["errorCount"] += 1
                if session["user.auth_userpassword.pwrecover"][
                        "errorCount"] > 3:
                    session["user.auth_userpassword.pwrecover"] = None
                    return self.userModule.render.view(
                        skel=None,
                        tpl=self.passwordRecoveryFailedTemplate,
                        reason=self.passwordRecoveryKeyInvalid)
                return self.userModule.render.edit(
                    skel,
                    self.passwordRecoveryStep2Template)  # Let's try again
            # If we made it here, the key was correct, so we'd hopefully have a valid user for this
            uSkel = userSkel().all().filter(
                "name.idx =",
                session["user.auth_userpassword.pwrecover"]["name"]).getSkel()
            if not uSkel:  # This *should* never happen - if we don't have a matching account we'll not send the key.
                session["user.auth_userpassword.pwrecover"] = None
                return self.userModule.render.view(
                    skel=None,
                    tpl=self.passwordRecoveryFailedTemplate,
                    reason=self.passwordRecoveryUserNotFound)
            if uSkel["status"] != 10:  # The account is locked or not yet validated. Abort the process
                session["user.auth_userpassword.pwrecover"] = None
                return self.userModule.render.view(
                    skel=None,
                    tpl=self.passwordRecoveryFailedTemplate,
                    reason=self.passwordRecoveryAccountLocked)
            # Update the password, save the user, reset his session and show the success-template
            uSkel["password"] = skel["password"]
            uSkel.toDB()
            session["user.auth_userpassword.pwrecover"] = None
            return self.userModule.render.view(
                None, self.passwordRecoverySuccessTemplate)