I am trying to set up authentication with flask and ldap3. For this I have a global_authentication.py, a main.py in which I create Flask and perform the view, as well as a loginform.py where the template for the login is located, a login.html and a _formhelper.html. When I start the Flask server a login screen appears with the login data. When I enter login info I get the following error message:
ldap3.core.exceptions.LDAPPasswordIsMandatoryError: password is mandatory in simple bind
Furthermore it refers to the lines:
File "D:\Projects\admin_tool\Logic\main.py", line 49, in index
login_msg = global_ldap_authentication(login_id, login_password)
and file
"D:\Projects\admin_tool\Logic\views\global_ldap_authentication.py", line 34, in global_ldap_authentication if not connection.bind():
When i print the password, the password seems empty, and has the following content:
Password: None
cn=None,dc=example,dc=org
What I have tried so far:
Instead of: form = LoginValidation() --> LoginValidation(request.POST)
When I do this I can no longer log in and get instant the following error:
AttributeError: 'Request' object has no attribute 'POST'
I am now running out of ideas what the error could be. Can anyone help me?
main.py
from flask import Flask, render_template, request, redirect, url_for, flash, g, session
import os
from flask_bootstrap import Bootstrap
from flask_wtf.csrf import CSRFProtect
from views.global_ldap_authentication import *
from forms.LoginForm import *
from flask_cors import CORS, cross_origin
from flask.sessions import SecureCookieSessionInterface
from itsdangerous import URLSafeTimedSerializer
from flask_restx import Namespace,Resource,fields
import requests
import json
app = Flask(__name__,template_folder='templates')
bootstrap = Bootstrap(app)
app.secret_key = os.urandom(24)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
app.config['WTF_CSRF_ENABLED'] = False
#app.route('/login', methods=['GET','POST'])
def index():
# initiate the form..
form = LoginValidation()
if request.method in ('POST') :
login_id = form.user_name_pid.data
login_password = form.user_pid_Password.data
# create a directory to hold the Logs
login_msg = global_ldap_authentication(login_id, login_password)
# validate the connection
if login_msg == "Success":
success_message = f"*** Authentication Success "
return render_template('success.html', success_message=success_message)
else:
error_message = f"*** Authentication Failed - {login_msg}"
return render_template("error.html", error_message=str(error_message))
return render_template('login.html', form=form)
#app.route('/hello')
class HelloResource(Resource):
def get(self):
return {"message":"Hello World"}
if __name__ == '__main__':
app.run(debug=True)
global_ldap_authentication.py
from ldap3 import Server, Connection, ALL, SUBTREE from ldap3.core.exceptions import LDAPException, LDAPBindError
def global_ldap_authentication(user_name, user_pwd):
"""
Function: global_ldap_authentication
Purpose: Make a connection to encrypted LDAP server.
:params: ** Mandatory Positional Parameters
1. user_name - LDAP user Name
2. user_pwd - LDAP User Password
:return: None
"""
# fetch the username and password
ldap_user_name = user_name
ldap_user_pwd = user_pwd
# ldap server hostname and port
ldsp_server = f"ldap://localhost:389"
# dn
root_dn = "dc=example,dc=org"
# user
user = f'cn={ldap_user_name},{root_dn}'
print(user)
server = Server(ldsp_server, get_info=ALL)
connection = Connection(server,
user=user,
password=ldap_user_pwd)
if not connection.bind():
print(f" *** Cannot bind to ldap server: {connection.last_error} ")
l_success_msg = f' ** Failed Authentication: {connection.last_error}'
else:
print(f" *** Successful bind to ldap server")
l_success_msg = 'Success'
return l_success_msg
LoginForm.py
from main import app
from flask_wtf import Form
from wtforms import StringField, PasswordField, validators
class LoginValidation(Form):
user_name_pid = StringField('', [validators.DataRequired()],
render_kw={'autofocus': True, 'placeholder': 'Enter User'})
user_pid_Password = PasswordField('', [validators.DataRequired()],
render_kw={'autofocus': True, 'placeholder': 'Enter your login Password'})
login.html
{% from "_formhelpers.html" import render_field %}
<form method=post>
<dl>
{{ render_field(form.user_name_pid) }}
{{ render_field(form.user_pid_Password) }}
</dl>
<p><input type=submit value=Login>
</form>
_formhelpers.html
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
Related
When unittesting my flask-app and sending post-requests with a Testclient I find a form.error that the CSRF token is missing.
I have been to this and to this post, also read the documentation, however I still cant get my Problem solved.
How the CSRF-Protection is created:
class Routes:
app = Flask(__name__)
app.config['SECRET_KEY'] = WebserverConfig().secret_key
CSRFProtect().init_app(__app)
The view to be tested:
#__app.route("/settings", methods=["GET", "POST"])
def settings():
form = SettingsForm()
if request.method == "POST":
if form.validate_on_submit():
Routes.__reqhandler.put_settings(form=form)
return redirect(url_for("settings"))
if form.errors != {}:
Routes.__reqhandler.error_put_settings(form=form)
data = Routes.__prepdata.prep_settings()
return render_template("settings.html", form=form, data=data)
The template which contains the form:
<form method="POST">
{{ form.hidden_tag() }}
<div class="text-center">
<h3>Einstellungen</h3>
</div>
{{ form.intervall.label }}
{{ form.intervall(id="intervall", class="form-control", placeholder=data["intervall"])}}
</form>
The fixture creating the testclient:
#pytest.fixture
def client():
app = Routes().get_app()
app.config["WTF_CSRF_METHODS"] = []
with app.test_client() as client:
yield client
The test:
def test_settings_valid_intervall(client):
res = client.post("settings", data={"intervall": "00:01:00"}, follow_redirects=True)
assert b'value="00:01:00' in res.data
When printing the data (print(res.data) an alert (created with a flash if there are errors in a form) with the info that "The CSRF token is missing" is sent. What am I missing to tell form.validate_on_submit to not check if a valid CSRF-token was passed?
I solved it by not only emptying the list of the WTF_CSRF_METHODS but also disabling CSRF within the fixture that creates the client:
app.config["WTF_CSRF_ENABLED"] = False
I try build gooogle login with flask login.
I find the thread here
Using Google OAuth2 with Flask
I was able to port the accepted answer to use Requests-OAuthlib instead of Rauth. As of this writing, the package's last commit was on June 2019 and was currently use by 30K+ repositories.
the init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
import dir
from flask_mail import Mail
# init SQLAlchemy so we can use it later in our models
db = SQLAlchemy()
mail = Mail()
GOOGLE_LOGIN_CLIENT_ID = "mygoogle API id .apps.googleusercontent.com" #I am sure It is correct
GOOGLE_LOGIN_CLIENT_SECRET = "my_secret_key"
def create_app():
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'secret-key-goes-here'
# app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////' + str(dir.dir) + '/admin.sqlite'
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USERNAME'] = 'ruaxe.sdafsafsafs'
app.config['MAIL_PASSWORD'] = 'uiykejafsaffiqklziccld'
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
app.config['OAUTH_CREDENTIALS'] = {
'google': {
'id': GOOGLE_LOGIN_CLIENT_ID,
'secret': GOOGLE_LOGIN_CLIENT_SECRET
}
}
mail.init_app(app)
db.init_app(app)
with app.app_context():
db.create_all()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
from .models import User
#login_manager.user_loader
def load_user(user_id):
# since the user_id is just the primary key of our user table, use it in the query for the user
return User.query.get(int(user_id))
# blueprint for auth routes in our app
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
# blueprint for non-auth parts of app
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
Then i build the auth.py with login page
auth = Blueprint('auth', __name__)
#auth.route('/login', methods=['GET'])
def login():
if current_user is not None and current_user.is_authenticated:
return redirect(url_for('main.index'))
return render_template('login.html')
the user view page i build in main.py
from __future__ import print_function
from flask import Blueprint, render_template, request, redirect, flash, url_for, current_app
from flask_login import login_required, current_user,login_user
from project.function import cmsHaravan as hara, cmsCalendar as cal
from .function import config as cf, cmsContacts as ct
from datetime import datetime, timedelta, date
from .models import User, Order, Shop
from . import db, mail
from flask_mail import Message
import random
import string
from werkzeug.security import generate_password_hash
import json
from requests_oauthlib import OAuth2Session
from urllib.request import urlopen
main = Blueprint('main', __name__)
class OAuthSignIn(object):
providers = None
def __init__(self, provider_name):
self.provider_name = provider_name
credentials = current_app.config['OAUTH_CREDENTIALS'][provider_name]
self.consumer_id = credentials['id']
self.consumer_secret = credentials['secret']
def authorize(self):
pass
def callback(self):
pass
def get_callback_url(self):
return url_for('main.profile', provider=self.provider_name,
_external=True)
#classmethod
def get_provider(self, provider_name):
if self.providers is None:
self.providers={}
for provider_class in self.__subclasses__():
provider = provider_class()
self.providers[provider.provider_name] = provider
return self.providers[provider_name]
class GoogleSignIn(OAuthSignIn):
openid_url = "https://accounts.google.com/.well-known/openid-configuration"
def __init__(self):
super(GoogleSignIn, self).__init__("google")
self.openid_config = json.load(urlopen(self.openid_url))
self.session = OAuth2Session(
client_id=self.consumer_id,
redirect_uri=self.get_callback_url(),
scope=self.openid_config["scopes_supported"]
)
def authorize(self):
auth_url, _ = self.session.authorization_url(
self.openid_config["authorization_endpoint"])
print(auth_url)
return redirect(auth_url)
def callback(self):
if "code" not in request.args:
return None, None
self.session.fetch_token(
token_url=self.openid_config["token_endpoint"],
code=request.args["code"],
client_secret=self.consumer_secret,
)
me = self.session.get(self.openid_config["userinfo_endpoint"]).json()
print(me)
print(me["name"], me["email"])
return me["name"], me["email"]
#main.route('/authorize/<provider>')
def oauth_authorize(provider):
# Flask-Login function
if not current_user.is_anonymous:
return redirect(url_for('index'))
oauth = OAuthSignIn.get_provider(provider)
return oauth.authorize()
#main.route('/profile/<provider>')
def oauth_callback(provider):
if not current_user.is_anonymous:
return redirect(url_for('main.index'))
oauth = OAuthSignIn.get_provider(provider)
name, email = oauth.callback()
print("da lay duoc email", email)
if email is None:
# I need a valid email address for my user identification
flash('Authentication failed.')
return redirect(url_for('auth.login'))
# Look if the user already exists
user=User.query.filter_by(email=email).first()
if not user:
# Create the user. Try and use their name returned by Google,
# but if it is not set, split the email address at the #.
name = name
if name is None or name == "":
name = email.split('#')[0]
# We can do more work here to ensure a unique nickname, if you
# require that.
user=User(firstname=name, email=email)
db.session.add(user)
db.session.commit()
# Log in the user, by default remembering them for their next visit
# unless they log out.
login_user(user, remember=True)
return redirect(url_for('main.index'))
#main.route('/profile')
#login_required
def profile():
ListCarBrands = cal.getListCarBrands()
ListProvinces = cal.getListProvinces()
order_email = current_user.email
list_order = {}
i = 0
for row in Order.query.filter_by(order_email=order_email):
i = i + 1
total_order = i
list_order[i] = row.__dict__
return render_template('profile.html', list_order=list_order, name=current_user.lastname,
biensoxe=current_user.biensoxe,
ListCarBrands=ListCarBrands, brands=current_user.brands, carclass=current_user.carclass,
firstname=current_user.firstname, lastname=current_user.lastname, phone=current_user.phone,
email=current_user.email, ListProvinces=ListProvinces, provinces=current_user.provinces)
The login.html
{% extends "base.html" %}
{% block content %}
<div id="sign-in">
<h1>Sign In</h1>
<p>
<a href={{ url_for('main.oauth_authorize', provider='google') }}><img src="{{ url_for('static', filename='img/sign-in-with-google.png') }}" /></a>
</div>
<form method="POST" action="/login">
<div class="ui-information-body">
<div class="align-content-center">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="notification is-danger">
{{ messages[0] }}
</div>
{% endif %}
{% endwith %}
<h4>Login {{ error }}</h4>
<div class="row">
<div class="col-5 "><label class="label-input-group">Email </label><input type="email"
class="next-input"
name="email" id="email"
value=""></div>
</div>
<div class="row">
<div class="col-5 "><label class="label-input-group">Password</label><input type="password"
class="next-input"
name="password"
id="password"
value=""></div>
</div>
<div class="row">
<div class="col-5 "><span class="label-input-group"><br></span>
<a href="{{ url_for('main.reset_password') }}" class="btn btn-danger btn-lg btn-block">
Nếu bạn quên mật khẩu? Reset
</a></div>
</div>
<div class="row">
<div class="col-5 "><span class="label-input-group"><br></span><input
class="btn btn-primary btn-lg btn-block" type="submit" value="Login"></div>
</div>
</div>
</div>
</div>
</form>
{% endblock %}
Profile html
{% extends "base.html" %}
{% block content %}
<form method="POST" action="/profile">
Some code here for user can update profile
</form>
</div>
{% endblock %}
Then i run program, i can get the link as the code in
def authorize(self):
auth_url, _ = self.session.authorization_url(
self.openid_config["authorization_endpoint"])
print(auth_url) ##### This function run
return redirect(auth_url)
https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=880298757050-ij79mostsm1fccdcvuj43m0oe0quisih.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fprofile%3Fprovider%3Dgoogle&scope=openid+email+profile&state=xwbrUEpMjhIFrM6l3PlXgcXdgzyDbd
127.0.0.1 - - [21/Aug/2020 14:03:32] "GET /authorize/google HTTP/1.1" 302 -
127.0.0.1 - - [21/Aug/2020 14:03:35] "GET /profile?provider=google&state=xwbrUEpMjhIFrM6l3PlXgcXdgzyDbd&code=4%2F3QFTG6I2FzBPUKD_Sk0hq4IUhlr0jA4EQ2fTLyQizyYsPkCLxRf_WXwQz929v4wUeJhN4IXWFWu7nLKBJ2NHhog&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&authuser=0&prompt=consent HTTP/1.1" 302 -
127.0.0.1 - - [21/Aug/2020 14:03:35] "GET /login?next=%2Fprofile%3Fprovider%3Dgoogle%26state%3DxwbrUEpMjhIFrM6l3PlXgcXdgzyDbd%26code%3D4%252F3QFTG6I2FzBPUKD_Sk0hq4IUhlr0jA4EQ2fTLyQizyYsPkCLxRf_WXwQz929v4wUeJhN4IXWFWu7nLKBJ2NHhog%26scope%3Demail%2Bprofile%2Bopenid%2Bhttps%253A%252F%252Fwww.googleapis.com%252Fauth%252Fuserinfo.email%2Bhttps%253A%252F%252Fwww.googleapis.com%252Fauth%252Fuserinfo.profile%26authuser%3D0%26prompt%3Dconsent HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2020 14:03:35] "GET /static/img/sign-in-with-google.png HTTP/1.1" 404 -
127.0.0.1 - - [21/Aug/2020 14:03:35] "GET /static/css/bootstrap.js HTTP/1.1" 404 -
BUT I TRY TO PRINT EMAIL AS THE CODE in route profile
oauth = OAuthSignIn.get_provider(provider)
name, email = oauth.callback()
print("da lay duoc email", email)
Nothing come! So anyone can help me
Thanks to all
Thanks all
I change scope and it run!
class GoogleSignIn(OAuthSignIn):
openid_url = "https://accounts.google.com/.well-known/openid-configuration"
def __init__(self):
super(GoogleSignIn, self).__init__("google")
self.openid_config = json.load(urlopen(self.openid_url))
self.session = OAuth2Session(
client_id=self.consumer_id,
redirect_uri=self.get_callback_url(),
scope='https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email'
)
I'd like to setup an LDAP Authentication Backend in Django, and I've already used ldap3 to confirm a bind, with success.
I'm now realising that writing a class for my LDAP Backend with just ldap3 is not so straightforward, and that installing
django_auth_ldap could be another route to explore.
I've tested already some code to create a bind to the LDAP "server", and then perform a simple search. All okay. This method I tested is outside of my Django framework.
When implementing the same method into my Django framework, I encounter an issue when the method gets called. Because of the print statements, I know the view is getting called as exptected, but the part of the code whereby the first "if" statement should be executed, does not get called unless I change from "POST"
to "GET". But then this seems to create the next issue (when I set to "GET", so to force the next lines to be executed), because I'd like that a login page then gets called, where I can then input my LDAP credentials,
ultimately confirming the LDAP connection. Here is my code:
views.py
def login_ldap(request):
LDAP_SERVER = '10.222.4.88'
searchFilter='random'
print (request)
print (LDAP_SERVER)
print ("request.method:{0}".format(request.method))
if request.method == "GET":
print ("if statement executed")
username = request.GET['username']
print ("U:{0}".format(username))
password = request.GET['password']
print ("P:{0}".format(password))
# Define the server and bind_dn
server = Server(LDAP_SERVER, get_info=ALL)
bind_dn = 'cn={0}, ou=Prod, ou=Extern, ou=User, ou=ABC, dc=DEF, dc=com'.format(username)
# Define the Connection
conn = Connection(server, bind_dn, password, auto_bind=True) # Use raise_exceptions=True for exceptions
print ("search: {0}",format(conn.search))
print ("conn: {0}",format(conn))
conn.start_tls() #Session now on a secure channel. See output from following print statement and "tls started"
print ("conn_tls: {0}",format(conn))
d = conn.extend.standard.who_am_i()
print (d)
#print ("Server Info: {0}",format(server.info))
conn.open()
conn.bind()
# The LDAP search base for looking up users.
LDAP_AUTH_SEARCH_BASE = "ou=ABC, dc=DEF, dc=com"
if conn.bind():
conn.search(
search_base=LDAP_AUTH_SEARCH_BASE,
search_filter= '(cn={})'.format(searchFilter), # This is the user being searched for
search_scope=SUBTREE # BASE & LEVEL also possible settings
)
entry = conn.entries[0]
res = conn.bind()
print (res)
return render(request, 'search_page.html', {'entry':entry})
The error message received on my webpage:
MultiValueDictKeyError at /login_ldap/
"'username'"
Request Method: GET
Request URL: http://127.0.0.1:8000/login_ldap/
Django Version: 1.11
Exception Type: MultiValueDictKeyError
Exception Value:
"'username'"
I assume this is related to the GET and no longer the POST method. Why is the request.method being automatically set to GET and not POST when implementing
this in Django? Is this class for the authentication in the correct location, in the views.py file or should there be a separate file for this?
What should be included in the settings.py exactly (related to BACKEND_AUTH)?
EDIT:
views.py
def login_view(request):
if request.POST:
username = request.POST['username']
print ("U:{0}".format(username))
password = request.POST['password']
print ("P:{0}".format(password))
user = authenticate(username=username, password=password)
print (user)
if user is not None:
if user.is_active:
login(request, user)
return redirect('index')
else:
messages.error(request, "User is not active in Database")
else:
print ("Please check your credentials!")
messages.error(request, "Please check your username and password!")
return render(request, 'login.html')
index.html
<div class="row">
<div class="col-lg-3"></div>
<div class="col-lg-6"><H1>This is the public page, Please Login</H1></div>
</div>
urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login_ldap/$', login_ldap, name='login_ldap'),
url(r'^login/$', login_view, name='login'),
url(r'^logout/$', logout_view, name='logout'),
url(r'^change_password/$', change_password, name='change_password'),
url(r'^$', index, name='index'),
]
settings.py
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
search_page.html
{% load static %}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link href='{% static "login.css" %}' rel="stylesheet">
<div class="wrapper">
<form method='post' action="" class="form-signin">{% csrf_token %}
<h2 class="form-signin-heading">Please login</h2>
<input type="text" class="form-control" name="username" placeholder="Username" required="" autofocus=""/>
<br>
<input type="password" class="form-control" name="password" placeholder="Password" required=""/>
<br>
<button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
</form>
</div>
You can use get() function to get the data.
if request.method == "GET":
print ("if statement executed")
username = request.GET.get('username')
For more reference you can see here,
MultiValueDictKeyError in Django
And about POST method you may require to import the csr_exempt and use the decorator before your view.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def myView(request):
Everything works fine and I get the Succes message i have set when the email is sent. But its just not sending email to the given address.
Also I get the following message in terminal when i submit the form, where the local server is running. I believe the 200 message means that the communication is successful.
"POST /submit/ HTTP/1.1" 200 10141
Given below my views.py, settings.py, forms.py and the template file.
views.py
from django.shortcuts import render_to_response
from django.template import RequestContext
from .forms import SubmitForm
from django.template import Context, loader
from django.core.mail import send_mail,EmailMessage
def submit(request):
success = False
success_message = " Thanks!"
form = SubmitForm(request.POST or None)
if form.is_valid():
original_title = request.POST.get('original_title')
english_title = request.POST.get('english_title')
sent_email = 'receive#email.com'
message_template = loader.get_template('submit.txt')
message_context = Context({ 'original_title':original_title,'english_title':english_title,
})
mail = EmailMessage(original_title, english_title, [sent_email])
mail.send()
success = True
return render_to_response('submit.html',
{'form' : form,'success': success,'success_message':success_message},
context_instance=RequestContext(request))
settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'sender#gmail.com'
EMAIL_HOST_PASSWORD = '*****'
EMAIL_PORT = 587
DEFAULT_FROM_EMAIL = 'Sample < example#gmail.com >
forms.py
from django import forms
from django.conf import settings
from django.core.cache import cache
from django.contrib.admin import widgets
from django.shortcuts import get_object_or_404
import os
class SubmitForm(forms.Form):
title = forms.CharField()
name = forms.CharField()
submit.html
<form method="post" action="{% url 'submit' %}" style="width:75%;margin:auto">{%csrf_token%}
<legend>Title</legend>
<br/>
<div class="row">
<dl class="field">
<dd><label>Title: *</label></dd>
<dt class="text">{{ form.name.errors }}
{{ form.original_title }}</dt>
<dd class="msg">You filled this out wrong.</dd>
</dl>
<dl class="field ">
<dd><label>English Title: *</label></dd>
<dt class="text">{{ form.name.errors }}
{{ form.english_title }}</dt>
<dd class="msg">You filled this out wrong.</dd>
</dl>
<input class="submit three columns" type="submit" value="Register"/>
I have added the submit.txt also in proper way. I get the success_message when i click submit but no email sent.
When you initialize an EmailMessage, the third argument is the from address. You appear to have a list containing the receiver's address instead.
Make sure you create your message in the same way as the docs:
email = EmailMessage('Hello', 'Body goes here', 'from#example.com',
['to1#example.com', 'to2#example.com'], ['bcc#example.com'],
headers = {'Reply-To': 'another#example.com'})
I'm having problem with accessing datastore entities. I'm new to GAE and Python, so be kind. I have been sourcing code from online tutorials and others example code and that's been going OK, until now.
Ultimately i want to be able to strip an email attachment, do some simple changes to the file(s) and merge with other .kml file(s) (or even better .kmz, but one thing at a time :) .
step1. was to send emails to the app's appspotmail address = done :)
step2. ...which writes to a db.model = i can see in dashboard/datastore viewer that the entities are there, so no problem = done :)
step3. - render a template to show the most recent,say 20 emails received. this list would dynamically update..... :( Hmmmmm
What happens instead is that i see none rendered for all variables in index.html. Can you perhaps point me in the right direction? thanks!
Dav-o
here is yaml.app
application: racetracer
version: 1
runtime: python27
api_version: 1
threadsafe: false
libraries:
- name: jinja2
version: latest
handlers:
- url: /_ah/mail/.+
script: handle_incoming_email.py
login: admin
- url: /favicon.ico
static_files: images/favicon.ico
upload: images/favicon.ico
- url: /static/css
static_dir: static/css
- url: /static/html
static_dir: static/index.html
- url: .*
script: main.app
inbound_services:
- mail
here is handle_incoming_email.py
import logging, email
import cgi
import datetime
import os.path
import os
from os import path
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.ext.webapp.mail_handlers import InboundMailHandler
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp.template import render
import jinja2
import wsgiref.handlers
from main import *
from baseController import *
from authController import *
class Vault(db.Model):
#fromAddress= db.UserProperty()
fromAddress= db.StringProperty()
subject = db.StringProperty(multiline=True)
#date = db.DateTimeProperty(auto_now_add=True)
date = db.StringProperty(multiline=True)
name = db.StringProperty(multiline=True)
class ProcessEmail(InboundMailHandler):
def receive(self, mailMessage):
logging.info("Email from: " + mailMessage.sender + " received ok")
logging.info("Email subject: " + mailMessage.subject )
logging.info("Email date: " + mailMessage.date )
fromAddress = mailMessage.sender
subject = mailMessage.subject
date = mailMessage.date
if not hasattr(mailMessage, 'attachments'):
logging.info("Oi, the email has no attachments")
# raise ProcessingFailedError('Email had no attached documents')
else:
logging.info("Email has %i attachment(s) " % len (mailMessage.attachments))
for attach in mailMessage.attachments:
name = attach[0] #this is the file name
contents = str(attach[1]) #and this is the attached files contents
logging.info("Attachment name: " + name )
logging.info("Attachment contents: " + contents)
vault = Vault()
vault.fromAddress = fromAddress
vault.subject = subject
vault.date = date
vault.name = name
vault.contents = contents #.decode() EncodedPayload.decode()
vault.put()
#f = open("aardvark.txt", "r")
#blah = f.read(30);
#logging.info("the examined string is : " + blah)
#f.close() # Close opened file
app = webapp.WSGIApplication([
ProcessEmail.mapping()
], debug=True)
def main():
run_wsgi_app(app)
if __name__ == "__main__":
main()
and here is basecontroller.py
from authController import *
from handle_incoming_email import *
import logging
import os
import webapp2
import jinja2
import wsgiref.handlers
from google.appengine.ext.webapp import template
from google.appengine.ext import db
from google.appengine.api import users
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__))
jinja_environment = \
jinja2.Environment(loader=jinja2.FileSystemLoader(TEMPLATE_DIR))
class Vault(db.Model):
#fromAddress= db.UserProperty()
fromAddress= db.StringProperty()
subject = db.StringProperty(multiline=True)
#date = db.DateTimeProperty(auto_now_add=True)
date = db.StringProperty(multiline=True)
name = db.StringProperty(multiline=True)
class MainPage(webapp2.RequestHandler):
def get(self):
logging.info("this is MainPage in baseController")
list = db.GqlQuery("SELECT * FROM Vault ORDER BY date DESC").fetch(10)
logging.info("this is in list: " + str(list))
vault = Vault()
fromAddress = vault.fromAddress
subject = vault.subject
date = vault.date
name = vault.name
values = {
'fromAddress': fromAddress,
'subject': subject,
'date': date,
'name': name,
}
templ = jinja_environment.get_template('index.html')
self.response.out.write(templ.render(values))
def main():
wsgiref.handlers.CGIHandler().run(app)
if __name__ == "__main__":
main()
i read in a tutorial that rendering templates is a superior to just using the html in the .py, but maybe i've broken it down in too many files? Their logic was that different people do the front end and backend, so get used to it now. Anyway the above intends to render to index.html which is:
index.html
{% extends "base.html" %}
{% block title %} racetracer {% endblock %}
{% block content %}
<h1>Racetracer </h1>
<h3>Files recently received :</h3>
<br>
<table>
<tr>
<th>Email address: </th>
<th>----------- </th>
<th>Subject: </th>
<th>Date Received: </th>
<th>Attachment: </th>
</tr>
<tr>
<td> {{ fromAddress }} </td>
<td> --------------- </td>
<td> {{ subject }} </td>
<td> {{ date }} </td>
<td> {{ name }} </td>
<blockquote>{{ content|escape }}</blockquote>
</p>
</tr>
</tr>
</table>
<br>
<hr>
<form action="/sign" method="post">
<textarea name="content" rows="1" cols="20"></textarea>
<input type="submit" value="Submit (in index:)">
</form>
{% endblock %}
and base.html....
<html><head>
<link type="text/css" rel="stylesheet" href="/static/css/main.css" /></head>
<body> From the file base out in the racetracer folder<br>
right now you are logged in as :
{% if user %}
{{ user.nickname }}!
[<b>sign out</b>]
{% else %}
anonymous, who are you?
[<b>sign in</b>]
{% endif %}
{% block content %}
{% endblock %}
</body></html>
and here is main.py
import os
import webapp2
from handle_incoming_email import *
from baseController import *
from authController import *
from google.appengine.ext.webapp import template
app = webapp2.WSGIApplication([
('/', MainPage),
#('/ProcessEmail', ProcessEmail),
], debug=True)
def main():
wsgiref.handlers.CGIHandler().run(app)
if __name__ == "__main__":
main()
Thanks for your help! it will be MUCH appreciated.
You're getting None because it IS None. You're declaring the Vault object but not assigning any values to it. The values would be in the list entity as a result of the GQL query.
vault = Vault() # This creates an empty object (assuming you aren't doing any dynamic init
fromAddress = vault.fromAddress # Still empty....
subject = vault.subject
date = vault.date
name = vault.name
Also, you shouldn't assign list as a var since that's reserved for Py.
What you want to do is set something like:
my_list = YOUR GQL QUERY.fetch()
# Create a formatted list
values = []
for ml in my_list:
value = {
'fromAddress': ml.fromAddress,
'subject': ml.subject,
'date': ml.date,
'name': ml.name,
}
values.append(value) # You could just append the dictionary directly here, of course
Then use the values list in you template params
** Update **
Your GQL query looks good as compared to your datastore Vault model.
First, make sure you've changed the 'list' var to 'my_list'.
Next, if you want to see the contents of the retrieved object printed as a readable string,
add this to your model:
def __unicode__(self):
return ('%s %s %s' % (self.key,self.fromAddress,self.subject)) # etc... for the vars you want printed with you print the object
Check your logging.info(my_list) and see if it prints anything more readable.
If not, run a for loop on the list and log the key and/or fromAddress, so:
for vault in my_list:
logging.info('the key = %s'%vault.key)
If that's not returning anything, go directly to the interactive console within your development environment and run that query:
from google.appengine.ext import db
from *vault_file* import Vault
my_list = db.GqlQuery("SELECT * FROM Vault ORDER BY date DESC").fetch(10) # run query
for vault in my_list:
print vault.key
print vault.fromAddress
Let me know where these tests leave you and I will continue to update.
* Update #2 *
So now that you can confirm your getting datastore values
you want to set a template page parameter equal to the vault list, so:
params = dict(values=my_list)
self.response.out.write(templ.render(params))
And now on your page, you need to loop through your list to print each dictionary item:
{% for vault in values %}
<tr>
<td>{{vault.fromAddress}}}</td>
etc...
</tr>
{% endfor %}
That work?