masoniteframework / core Goto Github PK
View Code? Open in Web Editor NEWMain pip package location for Masonite
Home Page: http://masoniteproject.com
License: MIT License
Main pip package location for Masonite
Home Page: http://masoniteproject.com
License: MIT License
Create a new authentication module with the methods to log in, log out, remember, and recover password. In addition to shortcuts such as generate password, etc.
This will be similar to session data but will be removed after the page is done loading.
This will be used for sending things like "success messages" when a form is submitted properly and will look something like:
request().redirect('/some/url').flash({'message', 'value'})
and then used in the template:
{% if session('message') %}
<div class="alert alert-success">
{{ session('message') }}
</div>
{% endif %}
but this will only be good for that request. if the page is refreshed then it will be gone.
This will have to either be inside the session and removed somehow at the end of a request or somehow inside the request object but be attached to an IP address. Since this is similar to both session data and IP addresses then it should really be inside the Session
class
There are a few different ways we can do this. One way is to check if a template exists before rendering it. So for example we can check if a resources/templates/errors/500.html
template exists and render that, else just throw a normal error on the system. This can be done inside the new ExceptionHandler
class but we will need a new method on the view class:
def show(self):
view().exists('errors/500')
# or
view().exists('/masonite/snippets/500.html')
because then inside our exception class we can do something like:
def _render(self):
self._app.bind('StatusCode', '500 Internal Server Error')
# Check if DEBUG is False
if not self._app.make('Application').DEBUG or self._app.make('Application').DEBUG == 'False':
# Raise the exception here
if self._app.make('View').exists('errors/500'):
# do all the rendering stuff here
return
else:
raise self._exception
# return a view
rendered_view = self._app.make('View')('/masonite/snippets/exception',
{
'exception': self._exception,
'traceback': traceback,
'tb': sys.exc_info(),
'app': self._app,
'enumerate': enumerate,
'open': open
}
).rendered_template
self._app.bind('Response', rendered_view)
Masonite 1.5 will focus on bug fixes and API's. This issue will be one of the leading issues to complete.
Require #41 to be closed
A Resource route will be called something like: UserResource
where it's whole purpose is to be a class that returns Json or something.
Now this UserResource
will need to run a serialize
method which will be inherited by a base class which will come from JsonSerialize
or XmlSerialize
or something.
Masonite will need to now accept GET
, POST
, PUT
, PATCH
, DELETE
.
This is simple as all the logic is there but it only currently is set to run on POST
requests.
I'm thinking the user resource might look something like:
from app.User import User
class UserResource:
## basic methods
def to_array(self):
return {
'id': self.id,
'username': self.username
}
## Special Methods:
def with():
pass
Then inside the routes/api.py
should look something like:
from masonite.resources import Resource
from app.http.resources.UserResource import UserResource
from app.User import User
ROUTES = [
Resource(UserResource(User))
]
or
ROUTES = [
UserResource(User),
DashboardResource(Dashboard),
ListResource(List),
]
And that should automatically handle show, store, update, destroy
This will be oAuth integrations like Twitter, GitHub, Slack etc etc.
This will start off with integrations for Slack and Discord and will be open for extension for other drivers, primarily third party unless we deem necessary for other integrations.
I chose Slack and Discord because that is what I need for my current Masonite project. I want it to be a simple plug and play system.
All this will require is to add a service provider and it will simply make the integration and drivers accessible from the container.
If you want to add other third party integrations then it will simply be loading them into the container.
I'm contemplating about making this either party and wholly a third party package instead of integrating this into this core
library. This might have to be something like MasoniteFramework/oAuth
or something and then pip install masonite-oauth
End code will look like:
Add two routes:
ROUTES = [
Get().route('send/to/slack', 'Controller@get_auth'),
Get().route('from/slack', 'Controller@receive_auth'),
]
Get and send the authorization
def get_auth(self, IntegrationManager):
IntegrationManager.driver('slack').redirect()
def receive_auth(self, IntegrationManager):
IntegrationManager.driver('slack').user()
For Masonite 1.5, only Slack and Discord drivers will be pre installed. Social media drivers will be added in 1.6
right now all cookies are set without an expiration date which makes them session cookies essentially. If a user closes out there browser completely, the cookie will be deleted. Make all cookies be defaulted to forever but can be changed by using the time
parameter.
Something like:
Request.cookie('key', 'value', time='1 minute')
for some reason there is no delete_cookie
method.
This is also a bug because whenever the developer changes their key, all cookies are now obsolete and will throw internal errors. The behavior needs to be that if a cookie cannot be decrypted, it should delete that cookie entirely. This will enable rapid changes in a Masonite secret key if it is breached without any fears of usability (minus logging people out or something)
Masonite needs to have phenomenal testing support out of the box. I'll need to design an entire test suite. Each major release will have a little added capabilities to the suite
This issue will pertain to testing routes
a redirect url of '/url/here'
compiles into "urlhere" for some reason. check tests because I thought this was fixed
Because form requests can't actually do Put, Patch and Delete, I'm thinking you can do something like:
<form action="" method="">
<input type="request_method" value="PUT">
</form>
Masonite will check if that form value is present and if it is, it will change the request method to that method type. This could possibly be a middleware instead
This is deprecated and not being used
Start with basic token based authentication but make it extendable
Requires #41 to be closed
Is this file needed ?
For example:
from masonite.routes import Get
def get(url, controller)
return Get().route(url, controller)
Add this helpers for GET, POST, PUT, and DELETE.
currently in order to switch drivers, you need to use the manager class instead of the actual driver class. In other words you have to do:
def show(self, Upload):
Upload.store(request().input('file'))
but if you want to change the driver on the fly, you need to use a manager class:
def show(self, UploadManager):
UploadManager.driver('s3').store(request().input('file'))
I would like to have it so you can do:
def show(self, Upload):
Upload.driver('s3').store(request().input('file'))
This will require a class to inherit from such as a new Driver
class which should contain 2 methods:
class Driver:
_manager = None
def driver(self):
""" Used to switch the driver """
return self._manager
def load_manager(self, manager):
""" Load the manager class into the driver """
self._manager = manager
and all drivers need to inherit from this (so they will need to be added to all contracts):
class UploadDiskDriver(Driver, BaseUploadDriver, UploadContract):
....
Then the Manager class will need to change inside masonite/managers/Manager.py
:
...
self.manage_driver = self.container.make(
'{0}{1}Driver'.format(self.driver_prefix, driver)
).load_manager(self)
...
Since the request is technically a singleton throughout the life of the server (it is instantiated before the server is started) we can technically have sessions. We would need to create a session()
method on the request class which will generate a _session
attribute on the request class that might look something like:
_session = {
'28.029.291.199': {
'username': 'Joseph'
}
}
Then we can use something like:
# sets the session variable
Request.session('username', 'Mike')
or just use:
# sets the session variable
Request.session('username')
# Returns Mike
I have to make sure that somehow this is secure. Using an IP address might actually be insecure because it might be able to be spoofed and now we have cross session access. I'll need a way to somehow ensure that WSGI doesn't allow a way to set the REMOTE_ADDR as an environ variable (I think that even if one is sent it will be converted to HTTP_REMOTE_ADDR) so it should be fine. There is something called X-HTTP-FORWARDED-FOR
. Might need to check that.
This should also be it's own class and the request class should inherit it. Call the class Session
which should not be able to be instantiated directly (and abstract class). Put this class inside a new file inside the base of the project called session.py
. So this should be inside masonite/session.py
and put the class Session inside of that.
Then import it into the request.py and inherit the Request class from it.
@mapeveri Is this something you'd want to do? It's pretty straight forward.
This should live in it's own repository and should come with:
is_admin
to the user model which requires the user to make a new migrationI'm wondering if this should be a replica of the Django interface or if it should be unique and cool looking
This might require having to make views accessible globally like how controllers are accessible globally.
This will wrap the application in a try and catch block. and then handle the exception in a new exception provider.
This exception provider should render a new view that is located in Masonite.
This is going to be tricky to get this clean
this will also be a really big feature which a lot of changes that may get reverted in Masonite 2.0
Masonite Package consists of helper function to easily integrate and scaffold a Masonite project.
Things it will do:
create_or_append_config()
append_web_routes()
append_api_rotues()
create_controller()
Right now all cookies are set with an HTTP only flag. This prevents javascript from reading cookies. This was the intended result because it was a security measure to prevent malicious javascript code from reading cookies (like retrieving sessions)
Sometimes you may wish to actually set cookies and have your javascript code use it so there should be a parameter to set it:
request().cookie('key', 'value', encrypt=False, http_only=False)
almost all of the configuration files for features are the same. We should look into making a driver based system, for Orator. this might not even be possible but it's worth looking into
contracts are a great way to ensure that a class has all the methods it needs. This is very similar to a interface in other languages but Python doesn't have traditional interfaces. We can do something like:
class UploadDriverContract(object):
def store(): raise NotImplemented()
def store_as(): raise NotImplemented()
def another_method(): raise NotImplemented()
then all drivers need to inherit from this:
from masonite.contracts import UploadDriverContract
class UploadGoogleDriver(UploadDriverContract):
pass
Now anytime anyone creates a driver they will have a minimum amount of methods necessary required
Masonite drivers need to program to an interface and not an implementation
There needs to be a way to accept specific file types. The end state of the code should look something like:
def show(self, Upload):
Upload.accept('png', 'jpg').store(request().input(file))
You may have to abstract this and make a BaseUploadDriver which contains this accept method and just make sure it works on the two drivers by inheriting from them. It should be really simple and should just be a check if the filetype is inside the list
Let me know if anyone has any additional questions
Just like controllers /billing.controller.BillingController@show
we should be able to have views accessible globally:
return view('/billing/views/settings')
I hate the way every other framework does forms. This solution needs to be super straight forward and as expandable and pluggable as possible.
This needs to be dead simple .... like dead simple.
This issue depends on #52. What is needed is to add drivers to store the sessions, for example Redis, etc.
Many languages have a static()
function helper inside the template which basically just returns a string prefix for files.
Masonite requires this as well primarily for non local file storage
I like the idea of having all the dependencies you need out of the box. I'm building the framework around what I would wan't a Python framework to be personally which comes with good and bad. The most obvious bad is that it is very opinionated although I've been programming for almost 10 years I definitely have an idea of what I was a framework to be and think I also have an idea what other developers want a framework to be, even if they don't know they want it yet. I personally feel that once we show them the way then they will like it a lot more.
At this point in the framework I want to break away from what other frameworks do and take our own path that we feel is right.
Now with that being said I completely agree that you shouldn't have to install an apt-get
package on a computer and honestly I didn't know that Linux was such a widely used development platform. If I had to guess I would have thought it was around 50% Mac, 40% windows and 10% Linux but it's more like 20% Linux which is pretty big.
I think there are 2 solutions here. 1 is to use a different mysql driver (currently its mysqlclient
) which is the requirement in requirements.txt but there is another one we can install. I'll do some Linux testing soon.
option 2 would be to throw an exception when they first use the model which tells them to install one of the requirements.
This is going to have to stay for several reasons. Reason 1 is that the requirements are needed for many of the Service Providers. With a batteries included framework comes many dependencies. This was always a really weird complaint from developers I never understood. Why does it matter that an application has a lot of dependencies? You don't have to use all of them and they are just dependencies. If an application is large in file size that really shouldn't matter these days since nearly all applications will be ran on dedicated servers or cloud infrastructure which has very high amounts of storage.
Reason 2 is that Service Providers (and drivers) require these dependencies and therefore the install process would ask questions and have to build a PROVIDERS
list automatically somehow and associate the dependencies with them. I feel like this would be a pretty incredible feat and hard to maintain.
Reason 3 is I hate question installs because when first creating a program, I don't know what I need and I don't want to have to run through an hour of documentation and time to install something I said no to and didn't think I needed. I just want things to be there when I need it.
Also because service providers are designed to load before the server starts, Adding service providers will not slow the application down. Only a very few service providers that are required by the application need to be loaded on every request. if people want to strip the application when it comes time to deploy then they can simply remove service providers before deploying.
I want to keep it this way. To be fair I accepted the PEP8 because it was not client facing and only developers contributing will see it but PEP8 is extremely ugly. like really ugly. I know Python required PEP8 but quite frankly, everyone takes PEP like it's the law of the land and not following by it is death.
Developers have to understand that PEP's are just guidelines and aren't strict measurements. I like the comments and the standards of the application generated by craft because they look beautiful.
Also know that PEP8 also says:
Many projects have their own coding style guidelines. In the event of any conflicts, such project-specific guides take precedence for that project.
PEP8 is good for the core library but anything client facing should look nice over PEP8 standards
Orator does support all three. I need to create documentation for Orator from scratch. Their documentation is not the best. I just keep dread writing it because the library has so many features.
put me with self.schema.table ('blogs') as table: and when running the migration I made an alter table.
Can you explain this one more? There are only 2 types of migrations needed:
if you want to alter a table you simply make the table migration and use the .change()
method. Again I'll have to create solid documentation on the Orator ORM
This could be good. Let's open an issue and discuss more about this.
this could be a craft controller NameOfController --crud
which can fill in the show
store
update
delete
methods.
let's talk more about this in an issue
Can you explain this more? You want to do something like craft controller Name
which will append "Controller" to the end?
This might not be good because not all controllers require the "XController" signature. I don't want to force developers to have to call all controllers "XController" (although that is the default convention of Masonite) This can change though if you have other thoughts.
Do you mean something like craft controller DashboardController --templates dashboard dashboard/user dashboard/profile
? If so let's open up and issue for this and discuss more. This could be a good feature after some discussion.
I like. let's open an issue and discuss how this will work.
What do you mean? all classes in Masonite are "StuddlyCase" by default so models should keep the class naming convention of Masonite. This is more of a naming convention thing.
You should be able to do craft model user
which will create the file name and model name it in lowercase. If this does not then let's open a bug issue
Can you explore more? If you name a model the singular and the table the plural then it will default to that table.
For example, if I have a photos table then I make a model called Photo
which will automatically point to the photos
table. If you want to change the table it points to you can specify the __table__='another_table'
attribute on the model. Again I need to update the documentation.
Explain this further. You mean like Django's super user? I do agree with this and have realized that I need to do craft auth
and then sign up manually. Let's open an issue and discuss this
it uploads to the 'storage/' directory and not the one specified in the second parameter of the store method or the configuration file.
experiment with better container injection. right now it gets all the parameters, injects them and then gets all the annotations and injects them. Try looking for a "inject in place" solution.
from masonite.requests import Request
def show(self, request: Request, Upload):
pass
does not work. Get this to work
A lot of the time you will need to redirect to a different page and quickly redirect back.
For example you may want the user to login but then go right back to the page they were at.
right now the solution is something like:
domain.com/login?back=/user/1
and then catching that in the show controller:
def show(self):
if request().has('back'):
request().session.set('back', request().input('back'))
and then inside the store controller (or wherever you are processing the form)
def store(self):
if request().session.has('back'):
return request().redirect(request().session.get('back'))
I'm wondering if there is a shorter way to do this.
A lot of the managers look very similar. Remove the redundant create driver method and replace it with class attributes.
I see a problem in the near future.
the Craft command tool should be able to be used by any Masonite version. The problem here though is that some changes in the API might lead to some negative side effects.
For example when creating an authentication system. There might be some changes to how controllers are made (such as inheriting from a new class only available in Masonite 1.6 or something).
The solution here is to load the commands in when the craft command is called. We can load commands into craft inside a service provider because craft grabs all the commands from the service container. Now each version will be able to have it's own changes in the commands
Right now the dependencies are split between the the masonite core repository and the actual masonite repo (MasoniteFramework/masonite
). These need to all be combined into this repo. The only dependency inside the MasoniteFramework/masonite
should be Masonite
@mapeveri Thoughts?
currently there is this:
get('/url/here', 'Controller@method')
which will look for: app.http.controllers.Controller.py
Good so far.
Now in order to do a controller from say a billing
module:
get('/url/here', 'Controller@method').module('billing.controllers')
this will look for a controller in billing.controllers.Controller.py
This seems a little weird to me. I think we should do something like:
get('/url/here', '\billing.controllers.Controller@method')
All this will do in the backend is check if the string starts with a backslash and get the controller accordingly. This logic is in the Route class.
If we go this route I say we remove the module method completely.
for example currently we can pass in the class we need as an annotation:
from masonite.requests import Request
def show(self, obj: Request):
obj # returns Request class
good so far
what we need to do is pass in an instance and get that like a contract
For example:
from masonite.contracts.UploadContract import UploadContract
def show(self, obj: UploadContract):
obj # returns Upload class
This will just search the container for anything that has an instance of the class provided.
this might actually already work but if it does, it is untested
So this will require a few moving parts here.
So how CSRF works is that there is a random key generated (like 8yr8738b8b87b8br48gf84b7bv
) at the start of each request. This changes on every request so it needs to be random.
This token can be used inside forms so there needs to be a {{ csrf_field() }}
function which just points to the request.get_csrf()
method or something. Check the HelpersProvider
for how helper functions work. Especially in templates (it uses the View.share
) method.
Then when the request is submitted via POST, the CSRF token outputted by the csrf_field()
function is checked against the one that was at the start of the request. If it's the same then the request was made by the current. If it wasn't then there is something wrong and it should throw an exception like InvalidCSRFToken
Now there is something to be aware of and that is that not all routes should have to be CSRF protected. It should be up to the developer to choose the ones that are not protected (all routes should be protected by default).
What I'm thinking of is that only new CSRF tokens should be generated if it is a GET request. This allows you to have a POST request and check if the CSRF token is valid
Please let me know if you have any questions
there are cookies like:
csrf_token 287827437843847
csrf_token 93948395738957
csrf_token 218937129847294
token dh38y8t7y987984bfy487fty48
after a while going back and forth between pages. We have to check if a cookie of that value already exists and then set if it does not. This should prevent the duplication of the same cookie keys
This will focus on third party oAuth and start with Slack and Discord
This repo is started and will be up soon
Masonite, being a framework that will be much better than django when it comes to writing modern applications, needs real time support for web applications.
Out of the box there needs to be 2 drivers, Pusher and a more local websocket approach like tornado or something.
This solution needs to be DEAD simple and should look something like:
def show(self, Socket):
Socket.broadcast('channel1', message)
Socket.broadcast(['channel1', 'channel2'], message)
Socket.broadcast('*', message)
Socket.broadcast_group('group1', message)
sometimes views don't need to be loaded over and over again and can just be rendered from a cache. I should be able to cache on a per template basis. Maybe something like:
def show(self):
view('welcome').cache() # cache forever
view('welcome').cache_for(5, 'minutes')
view('welcome').cache_for(5, 'hours')
view('welcome').cache_for(5, 'days')
view('welcome').cache_for(5, 'months')
view('welcome').cache_for(5, 'years')
This should ignore the template rendering entirely and just show the already rendered cached template
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.