<commit_before>
Promise = require 'bluebird'

exports.$models = (schema) ->
	
	User = schema.define 'User',
		
		email:
			type: String
			index: true
		
		name:
			type: String
			default: 'Anonymous'
			length: 24
			index: true
			
		passwordHash:
			type: String
		
		resetPasswordToken:
			type: String
			length: 128
			index: true
		
		salt:
			type: String
			length: 128
			
	# Temporary... secure by default.
	User::hasPermission = (perm) -> false
	User::isAccessibleBy = (user) -> false
	
	User::redactFor = (user) ->
		
		redacted =
			name: @name
			id: @id
			email: @email
		
		Promise.resolve redacted

augmentModel = (User, Model, name) ->
	
	validateUser = (user) ->
		
		new Promise (resolve, reject) ->
		
			unless user instanceof User
				
				error = new Error "Invalid user."
				error.code = 500
				reject error
	
	checkPermission = (user, perm) ->
	
		new Promise (resolve, reject) ->

			unless user.hasPermission perm
				
				error = new Error "Access denied."
				error.code = 403
				reject error
			
	Model.authenticatedAll = (user, params) ->
		
		(validateUser user).then(->

			checkPermission user, "schema:#{name}:all"
		
		).then(->
			
			Model.all params
		
		).then((models) ->
			
			models.filter (model) -> model.isAccessibleBy user
		
		).then((models) ->
			
			Promise.all models.map (model) -> model.redactFor user
		
		).then (models) ->
			
			if models.length is 0
			
				error = new Error "Collection not found."
				error.code = 404
				Promise.reject error
			
			else
				
				models
	
	Model.authenticatedCount = (user) ->
		
		(validateUser user).then(->
			
			checkPermission user, "schema:#{name}:count"
		
		).then -> Model.count()
		
	Model.authenticatedCreate = (user, properties) ->

		(validateUser user).then(->
			
			checkPermission user, "schema:#{name}:create"
		
		).then -> Model.create properties

	Model.authenticatedDestroy = (user, id) ->
	
		(validateUser user).then(->
			
			checkPermission user, "schema:#{name}:create"
		
		).then(->
			
			Model.authenticatedFind user, id
		
		).then (model) ->
		
			if model.isDeletableBy user
				
				model.destroy()
				
			else
				
				if model.isAccessibleBy user
					error = new Error "Access denied."
					error.code = 403
				else
					error = new Error "Resource not found."
					error.code = 404
				
				Promise.reject error
	
	Model.authenticatedDestroyAll = (user) ->
	
		(validateUser user).then(->
			
			checkPermission "schema:#{name}:destroyAll"
		
		).then ->
			
			Model.destroyAll()
		
	Model.authenticatedFind = (user, id) ->

		(validateUser user).then(->
			
			Model.find id
	
		).then (model) ->
			
			if model? and model.isAccessibleBy user
				
				model.redactFor user
			
			else
				
				error = new Error "Resource not found."
				error.code = 404
				Promise.reject error
	
	Model.authenticatedUpdate = (user, id, properties) ->

		(validateUser user).then(->
			
			Model.authenticatedFind user, id
		
		).then (model) ->
			
			if model.isEditableBy user
				
				model.updateAttributes properties
				
			else
				
				if model.isAccessibleBy user
					error = new Error "Access denied."
					error.code = 403
				else
					error = new Error "Resource not found."
					error.code = 404
				
				Promise.reject error
			
			Model.authenticatedFind user, id
		
	Model::isAccessibleBy ?= (user) -> true
	Model::isEditableBy ?= (user) -> false
	Model::isDeletableBy ?= (user) -> false
	Model::redactFor ?= (user) -> Promise.resolve this

exports.$modelsAlter = (models) ->
	
	augmentModel models.User, Model, name for name, Model of models
		
exports.$service = -> [
	'$q', 'config', 'rpc', 'schema'
	($q, config, rpc, schema) ->
		
		service = {}
		
		user = new schema.models.User
		<commit_msg>Control flow elegance.<commit_after>
Promise = require 'bluebird'

exports.$models = (schema) ->
	
	User = schema.define 'User',
		
		email:
			type: String
			index: true
		
		name:
			type: String
			default: 'Anonymous'
			length: 24
			index: true
			
		passwordHash:
			type: String
		
		resetPasswordToken:
			type: String
			length: 128
			index: true
		
		salt:
			type: String
			length: 128
			
	# Temporary... secure by default.
	User::hasPermission = (perm) -> false
	User::isAccessibleBy = (user) -> false
	
	User::redactFor = (user) ->
		
		redacted =
			name: @name
			id: @id
			email: @email
		
		Promise.resolve redacted

augmentModel = (User, Model, name) ->
	
	validateUser = (user) ->
		
		new Promise (resolve, reject) ->
		
			return resolve() if user instanceof User
				
			error = new Error "Invalid user."
			error.code = 500
			reject error
	
	checkPermission = (user, perm) ->
	
		new Promise (resolve, reject) ->

			return resolve() if user.hasPermission perm
				
			error = new Error "Access denied."
			error.code = 403
			reject error
			
	Model.authenticatedAll = (user, params) ->
		
		validateUser(user).then(->

			checkPermission user, "schema:#{name}:all"
		
		).then(->
			
			Model.all params
		
		).then((models) ->
			
			models.filter (model) -> model.isAccessibleBy user
		
		).then((models) ->
			
			Promise.all models.map (model) -> model.redactFor user
		
		).then (models) ->
			
			return models if models.length > 0
			
			error = new Error "Collection not found."
			error.code = 404
			Promise.reject error
	
	Model.authenticatedCount = (user) ->
		
		validateUser(user).then(->
			
			checkPermission user, "schema:#{name}:count"
		
		).then -> Model.count()
		
	Model.authenticatedCreate = (user, properties) ->

		validateUser(user).then(->
			
			checkPermission user, "schema:#{name}:create"
		
		).then -> Model.create properties

	Model.authenticatedDestroy = (user, id) ->
	
		validateUser(user).then(->
			
			checkPermission user, "schema:#{name}:create"
		
		).then(->
			
			Model.authenticatedFind user, id
		
		).then (model) ->
		
			return model.destroy() if model.isDeletableBy user
				
			if model.isAccessibleBy user
				error = new Error "Access denied."
				error.code = 403
			else
				error = new Error "Resource not found."
				error.code = 404
			
			Promise.reject error
	
	Model.authenticatedDestroyAll = (user) ->
	
		validateUser(user).then(->
			
			checkPermission "schema:#{name}:destroyAll"
		
		).then ->
			
			Model.destroyAll()
		
	Model.authenticatedFind = (user, id) ->

		validateUser(user).then(->
			
			Model.find id
	
		).then (model) ->
			
			if model? and model.isAccessibleBy user
				return model.redactFor user
			
			error = new Error "Resource not found."
			error.code = 404
			Promise.reject error
	
	Model.authenticatedUpdate = (user, id, properties) ->

		validateUser(user).then(->
			
			Model.authenticatedFind user, id
		
		).then (model) ->
			
			if model.isEditableBy user
				return model.updateAttributes properties
				
			if model.isAccessibleBy user
				error = new Error "Access denied."
				error.code = 403
			else
				error = new Error "Resource not found."
				error.code = 404
			
			Promise.reject error
		
	Model::isAccessibleBy ?= (user) -> true
	Model::isEditableBy ?= (user) -> false
	Model::isDeletableBy ?= (user) -> false
	Model::redactFor ?= (user) -> Promise.resolve this

exports.$modelsAlter = (models) ->
	
	augmentModel models.User, Model, name for name, Model of models
		
exports.$service = -> [
	'$q', 'config', 'rpc', 'schema'
	($q, config, rpc, schema) ->
		
		service = {}
		
		user = new schema.models.User
		
{'fraction_non_alphanumeric': 0.12381980037766388, 'fraction_numerical': 0.008902077151335312, 'mean_word_length': 2.5192216421452303, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 90, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5537540', 'n_tokens_mistral': 3102, 'n_tokens_neox': 2243, 'n_words': 623}