Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env-template
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ [email protected]
NGINX_ODOO_SMTP_PASS=test
NGINX_ODOO_LISTEN_HOST=localhost
NGINX_ODOO_LISTEN_PORT=8888
NGINX_ODOO_DEFAULT_LOGIN=./templates/login-2.html
NGINX_ODOO_DEFAULT_HOTP=./templates/hotp-2.html
NGINX_ODOO_ODOO_URL=http://localhost:8069
NGINX_ODOO_ODOO_DATABASE=bladatabase
# Whether to display the HOTP code in the logs
Expand Down
2 changes: 2 additions & 0 deletions lib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
BUTTONCOLOR = os.environ.get("NGINX_ODOO_BUTTON_COLOR", "")
BUTTONHOVERCOLOR = os.environ.get("NGINX_ODOO_BUTTON_HOVER_COLOR", "")
BUTTONSHADOWCOLOR = os.environ.get("NGINX_ODOO_BUTTON_SHADOW_COLOR", "")
DEFAULT_LOGIN = os.environ.get("NGINX_ODOO_DEFAULT_LOGIN", r"./templates/login-2.html")
DEFAULT_HOTP = os.environ.get("NGINX_ODOO_DEFAULT_HOTP", r"./templates/hotp-2.html")

theme_params = {
"backgroundcolor": BACKGROUNDCOLOR,
Expand Down
22 changes: 16 additions & 6 deletions nginx-odoo.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

db = config.db
OdooAuthHandler = config.OdooAuthHandler
login_page = config.DEFAULT_LOGIN or r"./templates/login-2.html"
hotp_page = config.DEFAULT_HOTP or r"./templates/hotp-2.html"

from lib.email import email

Expand All @@ -37,7 +39,7 @@ def get(self):
if not redirect_url.endswith("/"):
redirect_url += "/"
return self.redirect(f"{redirect_url}auth/{session_id}")
self.render(r"./templates/login.html", **config.theme_params)
self.render(login_page, **config.theme_params)

async def post(self):
# handle username/password
Expand All @@ -60,12 +62,12 @@ async def post(self):
message = "Mail with security code not sent."
logging.error(message)
return self.render(
r"./templates/login.html",
login_page,
**config.theme_params,
error=message,
)
return self.render(
r"./templates/hotp.html",
config.DEFAULT_HOTP,
**config.theme_params,
counter=counter,
code=code,
Expand All @@ -76,7 +78,7 @@ async def post(self):
message = "Invalid username or password."
logging.info(message)
return self.render(
r"./templates/login.html", **config.theme_params, error=message
login_page, **config.theme_params, error=message
)

# check HOTP
Expand All @@ -88,13 +90,13 @@ async def post(self):
if not hotp.verify(hotp_code, int(counter)):
message = "Invalid security code."
return self.render(
r"./templates/login.html", **config.theme_params, error=message
login_page, **config.theme_params, error=message
)
session_id = db.verify_code_and_expiry(counter, code)
if not session_id:
message = "Invalid security code (2)."
return self.render(
r"./templates/login.html", **config.theme_params, error=message
login_page, **config.theme_params, error=message
)
db.save_session(session_id, config.EXPIRY_INTERVAL)
logging.info("Setting session cookie: %s", session_id)
Expand Down Expand Up @@ -245,7 +247,15 @@ async def post(self):
debug=True,
)

# Use this after setup instructions if purpose is just testing tornado server
def main():
# Create an HTTP server listening on localhost, port 8080.
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(8080, address='127.0.0.1')
tornado.ioloop.IOLoop.current().start()

if __name__ == "__main__":
# main()
# Check connection with email service
loop = asyncio.get_event_loop()
loop.run_until_complete(email.test())
Expand Down
18 changes: 18 additions & 0 deletions static/hotp-2.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
html {
/*background: linear-gradient( 90deg, #d2d5d487, #8c929391, #aeb4b58f, #9b97989e, #7e7b7ba1, #a59e9e9e, #938d8ac4, #c7c5c3bf );*/
/*background: linear-gradient( 90deg, #005c4f87, #005c6091, #0181878f, #ac255e9e, #ca485ca1, #e16b5c9e, #f39060c4, #ffb56bbf );*/
}

.container {
margin-top: 200px;
padding: 50px;
}

.factor-auth-input {
position: relative;
overflow: hidden;
margin: 0px;
color: #333;
background-color: #fff;
border-color: #ccc;
}
Binary file added static/img/user-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/user-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/user.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
262 changes: 262 additions & 0 deletions static/login-2.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@

/* BASIC */

html {
background-color: #56baed;
}

body {
font-family: "Poppins", sans-serif;
height: 100vh;
}

a {
color: #92badd;
display:inline-block;
text-decoration: none;
font-weight: 400;
}

h2 {
text-align: center;
font-size: 16px;
font-weight: 600;
text-transform: uppercase;
display:inline-block;
margin: 40px 8px 10px 8px;
color: #cccccc;
}



/* STRUCTURE */

.wrapper {
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
width: 100%;
min-height: 100%;
padding: 20px;
}

#formContent {
-webkit-border-radius: 10px 10px 10px 10px;
border-radius: 10px 10px 10px 10px;
background: #fff;
padding: 30px;
width: 90%;
max-width: 450px;
position: relative;
padding: 0px;
-webkit-box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
text-align: center;
}

#formFooter {
background-color: #f6f6f6;
border-top: 1px solid #dce8f1;
padding: 25px;
text-align: center;
-webkit-border-radius: 0 0 10px 10px;
border-radius: 0 0 10px 10px;
}



/* TABS */

h2.inactive {
color: #cccccc;
}

h2.active {
color: #0d0d0d;
border-bottom: 2px solid #5fbae9;
}



/* FORM TYPOGRAPHY*/

input[type=button], input[type=submit], input[type=reset] {
background-color: #56baed;
border: none;
color: white;
padding: 15px 80px;
text-align: center;
text-decoration: none;
display: inline-block;
text-transform: uppercase;
font-size: 13px;
-webkit-box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4);
box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4);
-webkit-border-radius: 5px 5px 5px 5px;
border-radius: 5px 5px 5px 5px;
margin: 5px 20px 40px 20px;
-webkit-transition: all 0.3s ease-in-out;
-moz-transition: all 0.3s ease-in-out;
-ms-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}

input[type=button]:hover, input[type=submit]:hover, input[type=reset]:hover {
background-color: #39ace7;
}

input[type=button]:active, input[type=submit]:active, input[type=reset]:active {
-moz-transform: scale(0.95);
-webkit-transform: scale(0.95);
-o-transform: scale(0.95);
-ms-transform: scale(0.95);
transform: scale(0.95);
}

input[type=text], input[type=password] {
background-color: #f6f6f6;
border: none;
color: #0d0d0d;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 5px;
width: 85%;
border: 2px solid #f6f6f6;
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
-ms-transition: all 0.5s ease-in-out;
-o-transition: all 0.5s ease-in-out;
transition: all 0.5s ease-in-out;
-webkit-border-radius: 5px 5px 5px 5px;
border-radius: 5px 5px 5px 5px;
}

input[type=text]:focus, input[type=password]:focus {
background-color: #fff;
border-bottom: 2px solid #5fbae9;
}

input[type=text]:placeholder, input[type=password]:placeholder {
color: #cccccc;
}



/* ANIMATIONS */

/* Simple CSS3 Fade-in-down Animation */
.fadeInDown {
-webkit-animation-name: fadeInDown;
animation-name: fadeInDown;
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}

@-webkit-keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
100% {
opacity: 1;
-webkit-transform: none;
transform: none;
}
}

@keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
100% {
opacity: 1;
-webkit-transform: none;
transform: none;
}
}

/* Simple CSS3 Fade-in Animation */
@-webkit-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
@-moz-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
@keyframes fadeIn { from { opacity:0; } to { opacity:1; } }

.fadeIn {
opacity:0;
-webkit-animation:fadeIn ease-in 1;
-moz-animation:fadeIn ease-in 1;
animation:fadeIn ease-in 1;

-webkit-animation-fill-mode:forwards;
-moz-animation-fill-mode:forwards;
animation-fill-mode:forwards;

-webkit-animation-duration:1s;
-moz-animation-duration:1s;
animation-duration:1s;
}

.fadeIn.first {
-webkit-animation-delay: 0.4s;
-moz-animation-delay: 0.4s;
animation-delay: 0.4s;
}

.fadeIn.second {
-webkit-animation-delay: 0.6s;
-moz-animation-delay: 0.6s;
animation-delay: 0.6s;
}

.fadeIn.third {
-webkit-animation-delay: 0.8s;
-moz-animation-delay: 0.8s;
animation-delay: 0.8s;
}

.fadeIn.fourth {
-webkit-animation-delay: 1s;
-moz-animation-delay: 1s;
animation-delay: 1s;
}

/* Simple CSS3 Fade-in Animation */
.underlineHover:after {
display: block;
left: 0;
bottom: -10px;
width: 0;
height: 2px;
background-color: #56baed;
content: "";
transition: width 0.2s;
}

.underlineHover:hover {
color: #0d0d0d;
}

.underlineHover:hover:after{
width: 100%;
}



/* OTHERS */

*:focus {
outline: none;
}

#icon {
width: 20%;
}
Loading