From ad860ef236156c0342a0d7d9ce59a431d4e45756 Mon Sep 17 00:00:00 2001 From: Matej Kramny Date: Fri, 18 Mar 2016 23:23:03 +0000 Subject: [PATCH] Login working --- database/sql_migrations/v0.0.0.sql | 17 +++----- models/User.go | 2 +- public/html/auth/login.jade | 4 +- public/js/app.js | 18 +++++++- public/js/controllers/auth/login.js | 2 +- public/js/routes/auth.js | 2 +- routes/auth/login.go | 67 +++++++++++++++++++++++++++++ routes/main.go | 10 ++++- util/config.go | 12 ++++++ 9 files changed, 117 insertions(+), 17 deletions(-) create mode 100644 routes/auth/login.go diff --git a/database/sql_migrations/v0.0.0.sql b/database/sql_migrations/v0.0.0.sql index 4a44721b..c74a2a5e 100644 --- a/database/sql_migrations/v0.0.0.sql +++ b/database/sql_migrations/v0.0.0.sql @@ -1,28 +1,25 @@ create table user ( - `id` varchar (255) not null comment "UUID v4", - `created` datetime not null default NOW() comment "Created timestamp", + `id` int(11) not null auto_increment primary key, + `created` datetime not null default NOW(), `username` varchar(255) not null comment "Username, unique", `name` varchar(255) not null comment "Full name", `email` varchar(255) not null comment "Email, unique", `password` varchar(255) not null comment "Password", UNIQUE KEY `username` (`username`), - UNIQUE KEY `email` (`email`), - PRIMARY KEY `id` (`id`) + UNIQUE KEY `email` (`email`) ) ENGINE=InnoDB CHARSET=utf8; create table project ( - `id` varchar (255) not null comment "UUID v4", + `id` int(11) not null auto_increment primary key, `created` datetime not null default NOW() comment "Created timestamp", - `name` varchar(255) not null comment "Project name", - - PRIMARY KEY `id` (`id`) + `name` varchar(255) not null comment "Project name" ) ENGINE=InnoDB CHARSET=utf8; create table project__user ( - `project_id` varchar (255) not null comment "Project ID", + `project_id` int(11) not null, `user_id` varchar (255) not null comment "User ID", - `admin` tinyint (1) not null default 0 comment `Gives user god-like privileges`, + `admin` tinyint (1) not null default 0 comment 'Gives user god-like privileges', UNIQUE KEY `id` (`project_id`, `user_id`) ) ENGINE=InnoDB CHARSET=utf8; diff --git a/models/User.go b/models/User.go index 50f882fb..55861d7e 100644 --- a/models/User.go +++ b/models/User.go @@ -7,7 +7,7 @@ import ( ) type User struct { - ID string `db:"id" json:"id"` + ID int `db:"id" json:"id"` Created time.Time `db:"created" json:"created"` Username string `db:"username" json:"username"` Name string `db:"name" json:"name"` diff --git a/public/html/auth/login.jade b/public/html/auth/login.jade index f1899168..fd2fae41 100644 --- a/public/html/auth/login.jade +++ b/public/html/auth/login.jade @@ -6,9 +6,9 @@ span(ng-bind="status") .form-group(style="margin-top: 25px"): .col-sm-12 - input.text-center.form-control.input-lg(type="email" ng-model="user.auth" placeholder="Email / Username") + input.text-center.form-control.input-lg(type="text" ng-model="user.auth" placeholder="Email / Username") .form-group: .col-sm-12 - input.text-center.form-control.input-lg(type="password" required ng-model="user.password" placeholder="Password") + input.text-center.form-control.input-lg(type="password" ng-model="user.password" placeholder="Password") .form-group(style="margin-top: 25px"): .col-sm-12 button.btn.btn-primary.btn-block.btn-lg(ng-click="authenticate(user)") Sign In \ No newline at end of file diff --git a/public/js/app.js b/public/js/app.js index 262e957d..f3aa28e6 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -2,6 +2,22 @@ var app = angular.module('semaphore', ['scs.couch-potato', 'ui.router', 'ui.boot couchPotato.configureApp(app); +app.config(['$httpProvider', function ($httpProvider) { + $httpProvider.interceptors.push(['$q', '$injector', '$log', function ($q, $injector, $log) { + return { + request: function (request) { + var url = request.url; + if (!(url.indexOf('/public') !== -1 || url.indexOf('://') !== -1)) { + request.url = "/api" + request.url; + request.headers['Cache-Control'] = 'no-cache'; + } + + return request || $q.when(request); + } + }; + }]); +}]); + app.run(['$rootScope', '$window', '$couchPotato', '$injector', '$state', '$http', function ($rootScope, $window, $couchPotato, $injector, $state, $http) { app.lazy = $couchPotato; @@ -30,7 +46,7 @@ app.run(['$rootScope', '$window', '$couchPotato', '$injector', '$state', '$http' $rootScope.user = null; $rootScope.loggedIn = false; - $http.get('/api/user') + $http.get('/user') .then(function (user) { $rootScope.user = user; $rootScope.loggedIn = true; diff --git a/public/js/controllers/auth/login.js b/public/js/controllers/auth/login.js index 211808de..b89cd041 100644 --- a/public/js/controllers/auth/login.js +++ b/public/js/controllers/auth/login.js @@ -12,7 +12,7 @@ define(function () { var pwd = user.password; user.password = ""; - $http.post('/auth/password', { + $http.post('/auth/login', { auth: user.auth, password: pwd }).success(function (data, status) { diff --git a/public/js/routes/auth.js b/public/js/routes/auth.js index 79ab69f6..32368631 100644 --- a/public/js/routes/auth.js +++ b/public/js/routes/auth.js @@ -1,6 +1,6 @@ app.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $couchPotatoProvider) { $stateProvider.state('login', { - url: '/', + url: '/login', pageTitle: "Sign In", templateUrl: "/public/html/auth/login.html", controller: "SignInCtrl", diff --git a/routes/auth/login.go b/routes/auth/login.go new file mode 100644 index 00000000..e85ff0c2 --- /dev/null +++ b/routes/auth/login.go @@ -0,0 +1,67 @@ +package auth + +import ( + "database/sql" + "fmt" + "net/mail" + "strings" + "time" + + "golang.org/x/crypto/bcrypt" + + "github.com/ansible-semaphore/semaphore/database" + "github.com/ansible-semaphore/semaphore/models" + "github.com/gin-gonic/gin" + sq "github.com/masterminds/squirrel" +) + +func Login(c *gin.Context) { + var login struct { + Auth string `json:"auth" binding:"required"` + Password string `json:"password" binding:"required"` + } + + if err := c.Bind(&login); err != nil { + return + } + + login.Auth = strings.ToLower(login.Auth) + + q := sq.Select("*"). + From("user") + + _, err := mail.ParseAddress(login.Auth) + if err == nil { + q = q.Where("email=?", login.Auth) + } else { + q = q.Where("username=?", login.Auth) + } + + query, args, _ := q.ToSql() + fmt.Println(query, args) + + var user models.User + if err := database.Mysql.SelectOne(&user, query, args...); err != nil { + if err == sql.ErrNoRows { + c.AbortWithStatus(400) + return + } + + panic(err) + } + + if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(login.Password)); err != nil { + c.AbortWithStatus(400) + return + } + + session := c.MustGet("session").(models.Session) + session.UserID = &user.ID + + status := database.Redis.Set("session:"+session.ID, string(session.Encode()), 7*24*time.Hour) + if err := status.Err(); err != nil { + panic(err) + } + + c.AbortWithStatus(204) +} diff --git a/routes/main.go b/routes/main.go index bb2f2c94..9515f1e5 100644 --- a/routes/main.go +++ b/routes/main.go @@ -3,6 +3,7 @@ package routes import ( "strings" + "github.com/ansible-semaphore/semaphore/routes/auth" "github.com/ansible-semaphore/semaphore/util" "github.com/gin-gonic/gin" ) @@ -20,7 +21,9 @@ func Route(r *gin.Engine) { api.Use(authentication) - // serve /api/auth + func(api *gin.RouterGroup) { + api.POST("/login", auth.Login) + }(api.Group("/auth")) api.Use(MustAuthenticate) @@ -31,6 +34,11 @@ func Route(r *gin.Engine) { func servePublic(c *gin.Context) { path := c.Request.URL.Path + if strings.HasPrefix(path, "/api") { + c.Next() + return + } + if !strings.HasPrefix(path, "/public") { path = "/public/html/index.html" } diff --git a/util/config.go b/util/config.go index 1143f960..c2befb87 100644 --- a/util/config.go +++ b/util/config.go @@ -6,6 +6,8 @@ import ( "fmt" "os" + "golang.org/x/crypto/bcrypt" + "github.com/bugsnag/bugsnag-go" "github.com/gin-gonic/gin" "github.com/mattbaird/gochimp" @@ -45,8 +47,18 @@ func init() { flag.BoolVar(&Migration, "migrate", false, "execute migrations") path := flag.String("config", "", "config path") + var pwd string + flag.StringVar(&pwd, "hash", "", "generate hash of given password") + flag.Parse() + if len(pwd) > 0 { + password, _ := bcrypt.GenerateFromPassword([]byte(pwd), 11) + fmt.Println("Generated password: ", string(password)) + + os.Exit(0) + } + if path != nil && len(*path) > 0 { // load file, err := os.Open(*path)