├── .gitignore ├── public ├── img │ ├── favicon.png │ ├── glyphicons-halflings.png │ └── glyphicons-halflings-white.png ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── css │ └── font-awesome.min.css ├── app ├── models │ ├── server.go │ ├── project.go │ ├── user.go │ └── certificate.go ├── views │ ├── errors │ │ ├── 500.html │ │ └── 404.html │ ├── footer.html │ ├── User │ │ ├── Index.html │ │ ├── Password.html │ │ ├── Register.html │ │ └── Create.html │ ├── Project │ │ ├── GenerateOneTimeLink.html │ │ ├── LoadCSR.html │ │ ├── Index.html │ │ ├── ViewCert.html │ │ ├── CreateCertFromTemplate.html │ │ └── CreateCert.html │ ├── flash.html │ ├── App │ │ ├── Index.html │ │ └── Register.html │ ├── Admin │ │ ├── Users.html │ │ ├── Index.html │ │ ├── Projects.html │ │ ├── Project.html │ │ ├── EditProjectMembership.html │ │ ├── EditProject.html │ │ ├── User.html │ │ ├── EditUser.html │ │ ├── EditCertificate.html │ │ ├── ManageProject.html │ │ ├── EditTemplate.html │ │ ├── SignCSR.html │ │ └── NewTemplate.html │ ├── Tour │ │ ├── Index.html │ │ ├── Project.html │ │ └── User.html │ ├── debug.html │ ├── registration_form.html │ └── header.html ├── init.go └── controllers │ ├── tour.go │ ├── gorp.go │ ├── user.go │ └── init.go ├── tests └── apptest.go ├── messages └── sample.en ├── conf ├── app.conf └── routes ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | test-results/ 2 | tmp/ 3 | routes/ 4 | -------------------------------------------------------------------------------- /public/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinJudd/CAGo/HEAD/public/img/favicon.png -------------------------------------------------------------------------------- /public/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinJudd/CAGo/HEAD/public/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /app/models/server.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import () 4 | 5 | type Server struct { 6 | Id int 7 | URL string 8 | } 9 | -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinJudd/CAGo/HEAD/public/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinJudd/CAGo/HEAD/public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinJudd/CAGo/HEAD/public/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinJudd/CAGo/HEAD/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /public/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinJudd/CAGo/HEAD/public/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinJudd/CAGo/HEAD/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinJudd/CAGo/HEAD/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinJudd/CAGo/HEAD/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /app/views/errors/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Application error 5 | 6 | 7 | {{if eq .RunMode "dev"}} 8 | {{template "errors/500-dev.html" .}} 9 | {{else}} 10 |

Oops, an error occured

11 |

12 | This exception has been logged. 13 |

14 | {{end}} 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/views/footer.html: -------------------------------------------------------------------------------- 1 | {{if eq .RunMode "dev"}} 2 | {{template "debug.html" .}} 3 | {{end}} 4 | 5 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/views/errors/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Not found 5 | 6 | 7 | {{if eq .RunMode "dev"}} 8 | {{template "errors/404-dev.html" .}} 9 | {{else}} 10 | {{with .Error}} 11 |

12 | {{.Title}} 13 |

14 |

15 | {{.Description}} 16 |

17 | {{end}} 18 | {{end}} 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/views/User/Index.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Home"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |
7 |
8 |

It works!

9 |

10 |
11 |
12 |
13 |
14 | 15 | {{template "footer.html" .}} 16 | -------------------------------------------------------------------------------- /tests/apptest.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import "github.com/robfig/revel" 4 | 5 | type AppTest struct { 6 | revel.TestSuite 7 | } 8 | 9 | func (t *AppTest) Before() { 10 | println("Set up") 11 | } 12 | 13 | func (t AppTest) TestThatIndexPageWorks() { 14 | t.Get("/") 15 | t.AssertOk() 16 | t.AssertContentType("text/html") 17 | } 18 | 19 | func (t *AppTest) After() { 20 | println("Tear down") 21 | } 22 | -------------------------------------------------------------------------------- /messages/sample.en: -------------------------------------------------------------------------------- 1 | # Sample messages file for the English language (en) 2 | # Message file extensions should be ISO 639-1 codes (http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) 3 | # Sections within each message file can optionally override the defaults using ISO 3166-1 alpha-2 codes (http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) 4 | # See also: 5 | # - http://www.rfc-editor.org/rfc/bcp/bcp47.txt 6 | # - http://www.w3.org/International/questions/qa-accept-lang-locales 7 | 8 | -------------------------------------------------------------------------------- /app/views/Project/GenerateOneTimeLink.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "One Time URL"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

Project: {{.project.Name}} {{.certDownload.Certificate.CommonName}}

7 | 8 | The following URL can only be used once to download the Private Key for this certificate. 9 | {{.certDownload.Hash}} 10 | 11 | 12 | 13 |
14 | 15 | {{template "footer.html" .}} 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/views/flash.html: -------------------------------------------------------------------------------- 1 | {{if .flash.success}} 2 |
3 | 4 | {{.flash.success}} 5 |
6 | {{end}} 7 | 8 | {{if or .errors .flash.error}} 9 |
10 | 11 | {{if .flash.error}} 12 | {{.flash.error}} 13 | {{end}} 14 | 19 |
20 | {{end}} 21 | -------------------------------------------------------------------------------- /app/views/App/Index.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Home"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | {{if .projects}} 8 |

Available Projects:

9 |
10 | 19 | {{else}} 20 |

Please login

21 | {{end}} 22 | 23 |
24 | 25 | {{template "footer.html" .}} 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/views/Admin/Users.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "User Management"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

User Settings

7 |
8 | {{ $projects := .projects }} 9 | 23 | Create user 24 | 25 | 26 | 27 |
28 | 29 | {{template "footer.html" .}} 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/views/Admin/Index.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Admin"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

User Settings

7 | 8 | 11 | 12 | {{ if .anyAdmin }} 13 |

Admin Settings

14 | 26 | 27 | 28 | {{end}} 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 | {{template "footer.html" .}} 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/init.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import "github.com/robfig/revel" 4 | 5 | func init() { 6 | // Filters is the default set of global filters. 7 | revel.Filters = []revel.Filter{ 8 | revel.PanicFilter, // Recover from panics and display an error page instead. 9 | revel.RouterFilter, // Use the routing table to select the right Action 10 | revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters. 11 | revel.ParamsFilter, // Parse parameters into Controller.Params. 12 | revel.SessionFilter, // Restore and write the session cookie. 13 | revel.FlashFilter, // Restore and write the flash cookie. 14 | revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie. 15 | revel.I18nFilter, // Resolve the requested language 16 | revel.InterceptorFilter, // Run interceptors around the action. 17 | revel.ActionInvoker, // Invoke the action. 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/views/Admin/Projects.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Manage Projects"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

Manage Projects

7 |
8 | 31 |
32 | {{ if .user.IsAdmin }} 33 | Create New Project 34 | {{end}} 35 | 36 |
37 | 38 | {{template "footer.html" .}} 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/views/Tour/Index.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Tour Start"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

Welcome to CA Go!

7 |

8 | To begin, we will need the server information. 9 |

10 | 11 | 12 |
13 | {{with $field := field "serverURL" .}} 14 |
15 | 16 |
17 | 18 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 19 |
20 |
21 | {{end}} 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 |
30 | 31 | 32 | 33 |
34 | 35 | {{template "footer.html" .}} 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /conf/app.conf: -------------------------------------------------------------------------------- 1 | app.name=ca_go 2 | app.secret=bPlNFGdSC2wd8f2QnFhk5A84JJjKWZdKH9H2FHFuvUs9Jz8UvBHv3Vc5awx39ivu 3 | http.addr= 4 | http.port=9000 5 | http.ssl=false 6 | http.sslcert= 7 | http.sslkey= 8 | cookie.httponly=false 9 | cookie.prefix=REVEL 10 | cookie.secure=false 11 | format.date=01/02/2006 12 | format.datetime=01/02/2006 15:04 13 | results.chunked=false 14 | 15 | log.trace.prefix = "TRACE " 16 | log.info.prefix = "INFO " 17 | log.warn.prefix = "WARN " 18 | log.error.prefix = "ERROR " 19 | 20 | 21 | db.import = github.com/mattn/go-sqlite3 22 | db.driver = sqlite3 23 | db.spec = ca_go.db 24 | 25 | # The default language of this application. 26 | i18n.default_language=en 27 | 28 | module.static=github.com/robfig/revel/modules/static 29 | 30 | [dev] 31 | mode.dev=true 32 | results.pretty=true 33 | watch=true 34 | 35 | module.testrunner = github.com/robfig/revel/modules/testrunner 36 | 37 | log.trace.output = off 38 | log.info.output = stderr 39 | log.warn.output = stderr 40 | log.error.output = stderr 41 | 42 | [prod] 43 | mode.dev=false 44 | results.pretty=false 45 | watch=false 46 | 47 | module.testrunner = 48 | 49 | log.trace.output = off 50 | log.info.output = off 51 | log.warn.output = %(app.name)s.log 52 | log.error.output = %(app.name)s.log 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CAGo 2 | ==== 3 | 4 | A web-based Certificate Authority and Certificate manager. 5 | 6 | Features 7 | -------- 8 | 9 | * Create RSA and ECDSA Keys and Certificates 10 | * Create CA Signed or Self-Signed Keys 11 | * Revoke Certificates 12 | * Create and host CRLs 13 | * Create Projects to manage Certificates 14 | * Create Certificate creation Templates to expedite creation of new certificates 15 | * Multi-User Platform, can assign users to be a part of a project or assign certificates to a user 16 | * Polished Web-UI 17 | * Option to Encrypt Private Keys when downloaded 18 | 19 | 20 | Dependencies 21 | ------------ 22 | * https://github.com/robfig/revel 23 | * https://github.com/coopernurse/gorp 24 | * https://github.com/mattn/go-sqlite3 25 | 26 | Building CAGo 27 | ------------- 28 | (Assumes GOPATH is set) 29 | 30 | 1. go get github.com/JustinJudd/CAGo 31 | 2. go get github.com/robfig/revel/revel 32 | 3. go get github.com/coopernurse/gorp 33 | 4. go get github.com/mattn/go-sqlite3 34 | 5. bin/revel build github.com/JustinJudd/CAGo 35 | 36 | This will create a binary for your system that will run the CAGo webserver. You can change the port information in the conf/app.conf file. Once you run the binary you can go to the given port in your webbrowser to access CAGo (eg. localhost:9000). 37 | 38 | *This tool would contain your private keys, you probably won't want to run this as a public facing web server. 39 | -------------------------------------------------------------------------------- /app/views/debug.html: -------------------------------------------------------------------------------- 1 | 20 | 44 | 45 | 46 | 65 | -------------------------------------------------------------------------------- /app/models/project.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | //"github.com/robfig/revel" 6 | "github.com/coopernurse/gorp" 7 | ) 8 | 9 | // Project model as stored in database 10 | type Project struct { 11 | Id int 12 | 13 | Name, Description string 14 | /* 15 | If project is public any one with access to the website can see project and access public certs for certificates in project 16 | If not, only project owners and members will have visibility 17 | */ 18 | Public bool 19 | } 20 | 21 | // Model for mapping a user to a project that they are a member of 22 | type ProjectMembership struct { 23 | Id int 24 | ProjectId, UserId int 25 | 26 | Admin bool 27 | 28 | Project *Project 29 | User *User 30 | } 31 | 32 | func (c *ProjectMembership) PreInsert(_ gorp.SqlExecutor) error { 33 | if c.User != nil { 34 | c.UserId = c.User.Id 35 | } 36 | 37 | if c.Project != nil { 38 | c.ProjectId = c.Project.Id 39 | } 40 | 41 | return nil 42 | } 43 | 44 | func (c *ProjectMembership) PostGet(exe gorp.SqlExecutor) error { 45 | var ( 46 | obj interface{} 47 | err error 48 | ) 49 | 50 | if c.UserId != 0 { 51 | obj, err = exe.Get(User{}, c.UserId) 52 | if err != nil { 53 | return fmt.Errorf("Error loading a certificate's user (%d): %s", c.UserId, err) 54 | } 55 | c.User = obj.(*User) 56 | } 57 | 58 | if c.ProjectId != 0 { 59 | obj, err = exe.Get(Project{}, c.ProjectId) 60 | if err != nil { 61 | return fmt.Errorf("Error loading a certificate (%d): %s", c.ProjectId, err) 62 | } 63 | c.Project = obj.(*Project) 64 | } 65 | 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /app/views/Project/LoadCSR.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Create Certificate"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 |

Load CSR:

8 | 9 | {{ $cas := .cas}} 10 | {{ $project := .project}} 11 | 12 |
13 | 14 | 15 | {{with $field := field "csr.CSR" .}} 16 |
17 | 18 |
19 | 20 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 21 |
22 |
23 | {{end}} 24 | 25 | {{with $field := field "csr.RequestedCAId" .}} 26 |
27 | 28 |
29 | 35 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 36 |
37 |
38 | {{end}} 39 | 40 | 41 |
42 |
43 | 44 |
45 |
46 |
47 | 48 |
49 | 50 | {{template "footer.html" .}} 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/models/user.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | "github.com/robfig/revel" 6 | "regexp" 7 | //"github.com/coopernurse/gorp" 8 | ) 9 | 10 | // User model as stored in the database 11 | type User struct { 12 | Id int 13 | Email string 14 | Username, Name, Password string // Password is not stored in the DB 15 | HashedPassword []byte 16 | IsAdmin bool 17 | } 18 | 19 | func (u *User) String() string { 20 | return fmt.Sprintf("User(%s), Email(%s)", u.Username, u.Email) 21 | } 22 | 23 | var userRegex = regexp.MustCompile("^\\w*$") 24 | 25 | func (user *User) Validate(v *revel.Validation) { 26 | 27 | /* 28 | v.Check(user.Username, 29 | revel.Required{}, 30 | revel.Range{revel.Min{4},revel.Max{15}}, 31 | revel.Match{userRegex}, 32 | ) 33 | 34 | v.Check(user.Email, 35 | revel.Required{}, 36 | revel.MaxSize{50}, 37 | revel.MinSize{4}, 38 | 39 | //revel.Match{revel.emailPattern}, 40 | ).Message("Email") 41 | 42 | ValidatePassword(v, user.Password). 43 | Key("user.Password") 44 | 45 | v.Check(user.Name, 46 | revel.Required{}, 47 | revel.MaxSize{100}, 48 | ) 49 | */ 50 | 51 | v.Required(user.Username) 52 | v.MinSize(user.Username, 6).Message("Username must be at least 6 characters") 53 | 54 | v.Required(user.Name) 55 | v.MinSize(user.Name, 2).Message("Name must be at least 2 characters") 56 | 57 | v.Required(user.Email) 58 | v.Email(user.Email) 59 | v.MinSize(user.Email, 5) 60 | 61 | /* 62 | v.Required(user.Password) 63 | v.MinSize(user.Password, 8).Message("Password must be at least 8 characters") 64 | */ 65 | 66 | } 67 | 68 | /* 69 | func ValidatePassword(v *revel.Validation, password string) *revel.ValidationResult { 70 | return v.Check(password, 71 | revel.Required{}, 72 | revel.MaxSize{15}, 73 | revel.MinSize{5}, 74 | ) 75 | } 76 | */ 77 | -------------------------------------------------------------------------------- /app/views/Admin/Project.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Create New Project"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | 8 |
9 |
10 |
11 |
12 | Create project: 13 | {{with $field := field "project.Name" .}} 14 |
15 | 16 |
17 | 18 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 19 |
20 |
21 | {{end}} 22 | 23 | {{with $field := field "project.Description" .}} 24 |
25 | 26 |
27 | 28 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 29 |
30 |
31 | {{end}} 32 | 33 | {{with $field := field "project.Public" .}} 34 |
35 |
36 |
37 | 40 |
41 |
42 |
43 | {{end}} 44 | 45 | 46 | 47 |
48 |
49 | 50 |
51 |
52 |
53 |
54 | 55 |
56 | 57 | 58 | 59 |
60 | 61 | {{template "footer.html" .}} 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/controllers/tour.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/JustinJudd/CAGo/app/models" 5 | "github.com/JustinJudd/CAGo/app/routes" 6 | "fmt" 7 | "github.com/robfig/revel" 8 | 9 | //"crypto/tls" 10 | 11 | //"encoding/hex" 12 | //"encoding/base64" 13 | ) 14 | 15 | // Tour section for first time the App is run 16 | type Tour struct { 17 | App 18 | } 19 | 20 | // Start page of tour - setup server 21 | func (c Tour) Index() revel.Result { 22 | return c.Render() 23 | } 24 | 25 | // Form to create first user 26 | func (c Tour) User() revel.Result { 27 | return c.Render() 28 | } 29 | 30 | // Form to create first project 31 | func (c Tour) Project() revel.Result { 32 | return c.Render() 33 | } 34 | 35 | // Create and Save first project 36 | func (c Tour) SaveProject(project models.Project) revel.Result { 37 | 38 | err := c.saveProject(project) 39 | if err != nil { 40 | c.Flash.Error("Error creating project") 41 | return c.Redirect(routes.Tour.Project()) 42 | } 43 | 44 | c.Flash.Success("Project " + project.Name + " created") 45 | //fmt.Println(user) 46 | return c.Redirect(routes.Admin.Index()) 47 | } 48 | 49 | // Create and Save first project 50 | func (c Tour) SaveServerInfo(serverURL string) revel.Result { 51 | 52 | if len(serverURL) == 0 { 53 | c.Flash.Error("Unable to save server info") 54 | return c.Redirect(routes.Tour.Index()) 55 | } 56 | server := &models.Server{URL: serverURL} 57 | err := c.Txn.Insert(server) 58 | if err != nil { 59 | c.Flash.Error("Unable to save server info") 60 | return c.Redirect(routes.Tour.Index()) 61 | } 62 | 63 | c.Flash.Success("Server info saved") 64 | //fmt.Println(user) 65 | return c.Redirect(routes.Tour.User()) 66 | } 67 | 68 | // Save the first user 69 | func (c Tour) SaveUser(user models.User, verifyPassword string) revel.Result { 70 | err := c.saveUser(user, verifyPassword) 71 | if err != nil { 72 | c.Flash.Error("Unable to save user", err.Error()) 73 | return c.Redirect(routes.Tour.User()) 74 | } 75 | 76 | c.Session["user"] = user.Username 77 | c.Flash.Success("Created, " + user.Username) 78 | fmt.Println(user) 79 | return c.Redirect(routes.Tour.Project()) 80 | } 81 | -------------------------------------------------------------------------------- /app/views/Tour/Project.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Tour: Create Project"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

Almost finished setting up

7 |

8 | Now we can create a project to manage and hold certificates. 9 |

10 | 11 | 12 |
13 |
14 |
15 | {{with $field := field "project.Name" .}} 16 |
17 | 18 |
19 | 20 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 21 |
22 |
23 | {{end}} 24 | 25 | {{with $field := field "project.Description" .}} 26 |
27 | 28 |
29 | 30 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 31 |
32 |
33 | {{end}} 34 | 35 | {{with $field := field "project.Public" .}} 36 |
37 |
38 |
39 | 42 |
43 |
44 |
45 | {{end}} 46 | 47 | 48 | 49 |
50 |
51 | 52 |
53 |
54 |
55 | 56 |
57 | 58 | 59 |
60 | 61 | {{template "footer.html" .}} 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/views/User/Password.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Change Password"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 | Change Password: 14 | 15 | {{with $field := field "currentPassword" .}} 16 |
17 | 18 |
19 | 20 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 21 |
22 |
23 | {{end}} 24 | 25 | {{with $field := field "password" .}} 26 |
27 | 28 |
29 | 30 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 31 |
32 |
33 | {{end}} 34 | {{with $field := field "verifyPassword" .}} 35 |
36 | 37 |
38 | 39 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 40 | 41 |
42 |
43 | {{end}} 44 | 45 | 46 |
47 |
48 | 49 |
50 |
51 | 52 | 53 | 54 |
55 | 56 |
57 | 58 | {{template "footer.html" .}} 59 | -------------------------------------------------------------------------------- /app/views/Admin/EditProjectMembership.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Edit Project Membership"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | 8 | {{ $project := .project }} 9 | {{ $member_map := .member_map }} 10 | {{ $owner_map := .owner_map }} 11 | {{ $users_map := .users_map }} 12 | 13 | 14 |
15 |
16 |
17 |
18 | Edit Membership: 19 | 20 | 21 | 22 | 23 | {{with $field := field "projectOwnership" .}} 24 |
25 | 26 |
27 | 32 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 33 |
34 |
35 | {{end}} 36 | 37 | {{with $field := field "projectMembership" .}} 38 |
39 | 40 |
41 | 46 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 47 |
48 |
49 | {{end}} 50 | 51 | 52 |
53 |
54 | 55 |
56 |
57 |
58 |
59 | 60 |
61 | 62 | 63 | 64 |
65 | 66 | {{template "footer.html" .}} 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /app/views/Admin/EditProject.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Create New Project"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | {{ $project := .project}} 8 |
9 |
10 |
11 |
12 | Edit project: 13 | 14 | {{with $field := field "project.Name" .}} 15 |
16 | 17 |
18 | 19 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 20 |
21 |
22 | {{end}} 23 | 24 | {{with $field := field "project.Description" .}} 25 |
26 | 27 |
28 | 29 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 30 |
31 |
32 | {{end}} 33 | 34 | {{with $field := field "project.Public" .}} 35 |
36 |
37 |
38 | 41 |
42 |
43 |
44 | {{end}} 45 | 46 | 47 |
48 |
49 | 50 |
51 |
52 |
53 |
54 | 55 |
56 | 57 | 58 | 59 |
60 | 61 | {{template "footer.html" .}} 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | module:testrunner 6 | 7 | GET / App.Index 8 | GET /download/:hash App.DownloadOneTimeLink 9 | GET /crl/:crlId App.DownloadCRL 10 | 11 | #POST /app/create App.CreateCertificate 12 | 13 | GET /user/register User.Register 14 | POST /user/register User.SaveUser 15 | POST /user/login User.Login 16 | GET /user/Logout User.Logout 17 | GET /user/ User.Index 18 | 19 | GET /Project/:id Project.Index 20 | GET /Project/:id/Create Project.CreateCert 21 | GET /Project/:projectId/Create/:templateId Project.CreateCertFromTemplate 22 | GET /Project/:projectId/View/:certId Project.ViewCert 23 | GET /Project/:id/Download/:certId Project.Download 24 | GET /Project/:id/DownloadChain/:certId Project.DownloadChain 25 | GET /Project/:id/DownloadKey/:certId Project.DownloadKey 26 | GET /Project/:id/GenerateOneTimeLink/:certId Project.GenerateOneTimeLink 27 | POST /Project/:id/Create Project.CreateCertificate 28 | 29 | GET /Project/:id/LoadCSR Project.LoadCSR 30 | 31 | GET /Admin/Project/:id Admin.ManageProject 32 | GET /Admin/Project/:id/Edit Admin.EditProject 33 | POST /Admin/Project/:id/Edit Admin.UpdateProject 34 | GET /Admin/Project/:id/Create Admin.NewTemplate 35 | POST /Admin/Project/:id/Create Admin.CreateTemplate 36 | GET /Admin/Project/:id/Edit/:templateId Admin.EditTemplate 37 | 38 | GET /Admin/User/:userId/Edit Admin.EditUser 39 | POST /Admin/User/:userId/Edit Admin.SaveUser 40 | 41 | GET /Admin/Project/:projectId/EditMembership Admin.EditProjectMembership 42 | POST /Admin/User/:projectId/EditMembership Admin.SaveProjectMembership 43 | 44 | GET /Admin/Project/:projectId/EditCert/:certId Admin.EditCertificate 45 | POST /Admin/Project/:projectId/EditCert/:certId Admin.UpdateCertificate 46 | 47 | GET /Admin/Project/:projectId/SignCSR/:csrId Admin.SignCSR 48 | POST /Admin/Project/:projectId/SignCSR/:csrId Admin.SaveSignCSR 49 | 50 | # Ignore favicon requests 51 | GET /favicon.ico 404 52 | 53 | # Map static resources from the /app/public folder to the /public path 54 | GET /public/*filepath Static.Serve("public") 55 | 56 | # Catch all 57 | * /:controller/:action :controller.:action 58 | -------------------------------------------------------------------------------- /app/views/App/Register.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Register"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 |

Register:

8 | 9 |
10 | {{with $field := field "user.Email" .}} 11 |
12 | 13 |
14 | 15 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 16 |
17 |
18 | {{end}} 19 | {{with $field := field "user.Username" .}} 20 |
21 | 22 |
23 | 24 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 25 |
26 |
27 | {{end}} 28 | {{with $field := field "user.Name" .}} 29 |
30 | 31 |
32 | 33 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 34 |
35 |
36 | {{end}} 37 | {{with $field := field "user.Password" .}} 38 |
39 | 40 |
41 | 42 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 43 |
44 |
45 | {{end}} 46 | {{with $field := field "verifyPassword" .}} 47 |
48 | 49 |
50 | 51 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 52 | 53 |
54 |
55 | {{end}} 56 | 57 |
58 |
59 | Cancel 60 |
61 |
62 |
63 | 64 |
65 | 66 | {{template "footer.html" .}} 67 | -------------------------------------------------------------------------------- /app/views/registration_form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | {{with $field := field "user.Email" .}} 5 |
6 | 7 |
8 | 9 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 10 |
11 |
12 | {{end}} 13 | {{with $field := field "user.Username" .}} 14 |
15 | 16 |
17 | 18 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 19 |
20 |
21 | {{end}} 22 | 23 | {{with $field := field "user.Name" .}} 24 |
25 | 26 |
27 | 28 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 29 |
30 |
31 | {{end}} 32 | 33 | {{with $field := field "user.Password" .}} 34 |
35 | 36 |
37 | 38 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 39 |
40 |
41 | {{end}} 42 | {{with $field := field "verifyPassword" .}} 43 |
44 | 45 |
46 | 47 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 48 | 49 |
50 |
51 | {{end}} 52 | 53 | {{with $field := field "user.IsAdmin" .}} 54 | 63 | {{end}} 64 | 65 | 66 |
67 |
68 | Cancel 69 |
70 |
71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /app/controllers/gorp.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/JustinJudd/CAGo/app/models" 5 | "database/sql" 6 | "github.com/coopernurse/gorp" 7 | _ "github.com/mattn/go-sqlite3" 8 | r "github.com/robfig/revel" 9 | "github.com/robfig/revel/modules/db/app" 10 | ) 11 | 12 | var ( 13 | // Database Map 14 | Dbm *gorp.DbMap 15 | ) 16 | 17 | // Initialize database and tables 18 | func Init() { 19 | db.Init() 20 | Dbm = &gorp.DbMap{Db: db.Db, Dialect: gorp.SqliteDialect{}} 21 | 22 | setColumnSizes := func(t *gorp.TableMap, colSizes map[string]int) { 23 | for col, size := range colSizes { 24 | t.ColMap(col).MaxSize = size 25 | } 26 | } 27 | 28 | t := Dbm.AddTable(models.User{}).SetKeys(true, "Id") 29 | t.ColMap("Password").Transient = true 30 | setColumnSizes(t, map[string]int{ 31 | "Email": 50, 32 | "Username": 100, 33 | "Name": 100, 34 | }) 35 | 36 | t = Dbm.AddTable(models.Project{}).SetKeys(true, "Id") 37 | setColumnSizes(t, map[string]int{ 38 | "Name": 100, 39 | "Description": 10240, 40 | }) 41 | 42 | t = Dbm.AddTable(models.Certificate{}).SetKeys(true, "Id") 43 | t.ColMap("Project").Transient = true 44 | 45 | t = Dbm.AddTable(models.CertificateTemplate{}).SetKeys(true, "Id") 46 | t.ColMap("Project").Transient = true 47 | 48 | t = Dbm.AddTable(models.CertificateOwnership{}).SetKeys(true, "Id") 49 | t.ColMap("Certificate").Transient = true 50 | t.ColMap("User").Transient = true 51 | 52 | t = Dbm.AddTable(models.CertificateOneTimeDownload{}).SetKeys(true, "Id") 53 | t.ColMap("Certificate").Transient = true 54 | 55 | t = Dbm.AddTable(models.RevokedCertificate{}).SetKeys(true, "Id") 56 | t.ColMap("Certificate").Transient = true 57 | 58 | t = Dbm.AddTable(models.ProjectMembership{}).SetKeys(true, "Id") 59 | t.ColMap("Project").Transient = true 60 | t.ColMap("User").Transient = true 61 | 62 | t = Dbm.AddTable(models.CertificateRequest{}).SetKeys(true, "Id") 63 | t.ColMap("Project").Transient = true 64 | t.ColMap("RequestedCA").Transient = true 65 | t.ColMap("User").Transient = true 66 | 67 | t = Dbm.AddTable(models.CACount{}).SetKeys(true, "Id") 68 | t.ColMap("Certificate").Transient = true 69 | 70 | t = Dbm.AddTable(models.Server{}).SetKeys(true, "Id") 71 | 72 | //Dbm.TraceOn("[gorp]", r.INFO) 73 | Dbm.CreateTablesIfNotExists() 74 | 75 | } 76 | 77 | // Controller wrapping around database operations - Foundation for app controller 78 | type GorpController struct { 79 | *r.Controller 80 | Txn *gorp.Transaction 81 | } 82 | 83 | // Begin database transaction 84 | func (c *GorpController) Begin() r.Result { 85 | txn, err := Dbm.Begin() 86 | if err != nil { 87 | panic(err) 88 | } 89 | c.Txn = txn 90 | return nil 91 | } 92 | 93 | // Commit database transaction 94 | func (c *GorpController) Commit() r.Result { 95 | if c.Txn == nil { 96 | return nil 97 | } 98 | if err := c.Txn.Commit(); err != nil && err != sql.ErrTxDone { 99 | panic(err) 100 | } 101 | c.Txn = nil 102 | return nil 103 | } 104 | 105 | // Rollback a database transaction 106 | func (c *GorpController) Rollback() r.Result { 107 | if c.Txn == nil { 108 | return nil 109 | } 110 | if err := c.Txn.Rollback(); err != nil && err != sql.ErrTxDone { 111 | panic(err) 112 | } 113 | c.Txn = nil 114 | return nil 115 | } 116 | 117 | // Add table for Certificates 118 | func (c *GorpController) AddCertTable(tableName string) { 119 | t := Dbm.AddTableWithName(models.Certificate{}, tableName).SetKeys(true, "Id") 120 | t.ColMap("User").Transient = true 121 | } 122 | -------------------------------------------------------------------------------- /app/views/Admin/User.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Create New User"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

Create a new user

7 | 8 | 9 |
10 | {{with $field := field "user.Email" .}} 11 |
12 | 13 |
14 | 15 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 16 |
17 |
18 | {{end}} 19 | {{with $field := field "user.Username" .}} 20 |
21 | 22 |
23 | 24 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 25 |
26 |
27 | {{end}} 28 | 29 | {{with $field := field "user.Name" .}} 30 |
31 | 32 |
33 | 34 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 35 |
36 |
37 | {{end}} 38 | 39 | {{with $field := field "user.Password" .}} 40 |
41 | 42 |
43 | 44 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 45 |
46 |
47 | {{end}} 48 | {{with $field := field "verifyPassword" .}} 49 |
50 | 51 |
52 | 53 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 54 | 55 |
56 |
57 | {{end}} 58 | 59 | {{with $field := field "user.IsAdmin" .}} 60 |
61 |
62 |
63 | 66 |
67 |
68 |
69 | {{end}} 70 | 71 | 72 |
73 |
74 | 75 |
76 |
77 |
78 | 79 | 80 | 81 |
82 | 83 | {{template "footer.html" .}} 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /app/views/User/Register.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Register"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 |

Register:

8 | 9 |
10 | {{with $field := field "user.Email" .}} 11 |
12 | 13 |
14 | 15 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 16 |
17 |
18 | {{end}} 19 | {{with $field := field "user.Username" .}} 20 |
21 | 22 |
23 | 24 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 25 |
26 |
27 | {{end}} 28 | 29 | {{with $field := field "user.Name" .}} 30 |
31 | 32 |
33 | 34 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 35 |
36 |
37 | {{end}} 38 | 39 | {{with $field := field "user.Password" .}} 40 |
41 | 42 |
43 | 44 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 45 |
46 |
47 | {{end}} 48 | {{with $field := field "verifyPassword" .}} 49 |
50 | 51 |
52 | 53 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 54 | 55 |
56 |
57 | {{end}} 58 | 59 | {{with $field := field "user.IsAdmin" .}} 60 |
61 |
62 |
63 | 66 |
67 |
68 |
69 | {{end}} 70 | 71 | 72 |
73 |
74 | Cancel 75 |
76 |
77 |
78 | 79 |
80 | 81 | {{template "footer.html" .}} 82 | -------------------------------------------------------------------------------- /app/views/Tour/User.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Tour Start"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

Making progress

7 |

8 | Next, we will need to create an admin user. 9 |

10 | 11 | 12 |
13 | {{with $field := field "user.Email" .}} 14 |
15 | 16 |
17 | 18 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 19 |
20 |
21 | {{end}} 22 | {{with $field := field "user.Username" .}} 23 |
24 | 25 |
26 | 27 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 28 |
29 |
30 | {{end}} 31 | 32 | {{with $field := field "user.Name" .}} 33 |
34 | 35 |
36 | 37 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 38 |
39 |
40 | {{end}} 41 | 42 | {{with $field := field "user.Password" .}} 43 |
44 | 45 |
46 | 47 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 48 |
49 |
50 | {{end}} 51 | {{with $field := field "verifyPassword" .}} 52 |
53 | 54 |
55 | 56 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 57 | 58 |
59 |
60 | {{end}} 61 | 62 | {{with $field := field "user.IsAdmin" .}} 63 | 72 | {{end}} 73 | 74 | 75 |
76 |
77 | 78 |
79 |
80 |
81 | 82 | 83 | 84 |
85 | 86 | {{template "footer.html" .}} 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /app/views/User/Create.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Create User"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 | Create User: 14 | 15 | {{with $field := field "user.Email" .}} 16 |
17 | 18 |
19 | 20 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 21 |
22 |
23 | {{end}} 24 | {{with $field := field "user.Username" .}} 25 |
26 | 27 |
28 | 29 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 30 |
31 |
32 | {{end}} 33 | 34 | {{with $field := field "user.Name" .}} 35 |
36 | 37 |
38 | 39 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 40 |
41 |
42 | {{end}} 43 | 44 | {{with $field := field "user.Password" .}} 45 |
46 | 47 |
48 | 49 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 50 |
51 |
52 | {{end}} 53 | {{with $field := field "verifyPassword" .}} 54 |
55 | 56 |
57 | 58 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 59 | 60 |
61 |
62 | {{end}} 63 | 64 | {{with $field := field "user.IsAdmin" .}} 65 |
66 |
67 |
68 | 71 |
72 |
73 |
74 | {{end}} 75 | 76 | 77 |
78 |
79 | Cancel 80 |
81 |
82 |
83 |
84 | 85 |
86 | 87 |
88 | 89 | {{template "footer.html" .}} 90 | -------------------------------------------------------------------------------- /app/views/Admin/EditUser.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Edit User"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | 8 | 9 | {{ $user := .user }} 10 | {{ $all_projects := .all_projects }} 11 | {{ $admin_projects := .admin_projects }} 12 | {{ $project_map := .project_map }} 13 | 14 |
15 |
16 |
17 |
18 | Edit User: 19 | 20 | 21 | {{with $field := field "user.Username" .}} 22 |
23 | 24 |
25 | 26 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 27 |
28 |
29 | {{end}} 30 | 31 | 32 | {{with $field := field "user.Email" .}} 33 |
34 | 35 |
36 | 37 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 38 |
39 |
40 | {{end}} 41 | 42 | 43 | {{with $field := field "user.Name" .}} 44 |
45 | 46 |
47 | 48 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 49 |
50 |
51 | {{end}} 52 | 53 | 54 | {{with $field := field "user.IsAdmin" .}} 55 |
56 |
57 |
58 | 61 |
62 |
63 |
64 | {{end}} 65 | 66 | 67 | {{with $field := field "projectOwnership" .}} 68 |
69 | 70 |
71 | 76 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 77 |
78 |
79 | {{end}} 80 | 81 | 82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 | 90 |
91 | 92 | 93 | 94 |
95 | 96 | {{template "footer.html" .}} 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /app/views/Project/Index.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Project"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

Project: {{.project.Name}}

7 | {{ $project := .project}} 8 | {{ $templates := .templates}} 9 | {{ $revokedMap := .revokedMap }} 10 | 11 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {{ range $i, $certificate := .certs }} 30 | 31 | 32 | 35 | 38 | 59 | 60 | 61 | {{else}} 62 | No Certificates created 63 | {{end}} 64 | 65 | 66 | 67 |
Common NameCertificate AuthorityActions
33 | {{if index $revokedMap $certificate.Id}}{{$certificate.CommonName}}{{else}}{{$certificate.CommonName}}{{end}} 34 | 36 | 37 | 39 |
40 | Download Public Certificate 41 | 42 | 43 | 57 |
58 |
68 | 69 | 70 | {{ if .user}} 71 | Upload CSR 72 | {{end}} 73 | 74 | 75 |
76 | 77 | {{ range $i, $certificate := .certs }} 78 | 119 | {{end}} 120 | 121 | {{template "footer.html" .}} 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /app/views/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{.title}} 7 | 8 | 9 | 10 | 11 | 12 | {{range .moreStyles}} 13 | 14 | {{end}} 15 | {{range .moreScripts}} 16 | 17 | {{end}} 18 | 19 | 20 | 27 | 28 | 87 |
88 |
89 |
90 | {{template "flash.html" .}} 91 |
92 |
93 | 94 | {{if .breadcrumbs }} 95 | 104 | 105 | {{end}} 106 |
107 | 108 | 109 | -------------------------------------------------------------------------------- /app/views/Project/ViewCert.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "View Certificate"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

Project: {{.project.Name}} View Cert

7 | 8 | {{ $project := .project}} 9 | {{ $certificate := .certificate}} 10 | {{ $chain := index .chains 0 }} 11 | 12 | 13 |
14 |
Status:
{{ if .revoked}}REVOKED{{else}}VALID{{end}}
15 |
Country:
{{ index .cert.Subject.Country 0 }}
16 |
State:
{{ index .cert.Subject.Province 0 }}
17 |
City:
{{ index .cert.Subject.Locality 0 }}
18 |
Organization:
{{ index .cert.Subject.Organization 0 }}
19 |
Organizational Unit:
{{ index .cert.Subject.OrganizationalUnit 0 }}
20 |
Common Name:
{{ index .cert.Subject.CommonName }}
21 |
22 |
Signed By:
{{ index .cert.Issuer.CommonName }}
23 |
24 |
Key Uses:
25 |
26 | {{ $size := len .keyUses}}{{ range $i, $key := .keyUses }}{{if $i}}, {{end}}{{$key}}{{end}} 27 |
28 |
Extra Key Uses:
29 |
30 | {{ range $i, $key := .extKeyUses }}{{if $i}}, {{end}}{{$key}}{{end}} 31 |
32 |
33 |
CA:
34 |
CA Chain:
{{range $i, $cert := $chain}} 35 | {{if $i}}{{end}}{{$cert.Subject.CommonName}} 36 | {{end}}
37 |
38 | 39 | 40 | 41 | 42 |
43 | Download Public Certificate 44 | {{if or .canDownloadKey .canRevokeKey}} 45 | 46 | 47 | 70 | {{end}} 71 |
72 | 73 | {{if .canRevokeKey}} 74 | Edit cert 75 | {{end}} 76 | 77 |
78 | 79 | 80 |
81 | 82 | 122 | 123 | {{template "footer.html" .}} 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /app/controllers/user.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/JustinJudd/CAGo/app/models" 5 | "github.com/JustinJudd/CAGo/app/routes" 6 | "code.google.com/p/go.crypto/bcrypt" 7 | "github.com/robfig/revel" 8 | ) 9 | 10 | // Controller for user related actions 11 | type User struct { 12 | App 13 | } 14 | 15 | // Get a user based on a name 16 | func (c App) getUser(username string) *models.User { 17 | users, err := c.Txn.Select(models.User{}, `select * from User where Username = ?`, username) 18 | if err != nil { 19 | panic(err) 20 | } 21 | if len(users) == 0 { 22 | return nil 23 | } 24 | return users[0].(*models.User) 25 | } 26 | 27 | // Get a user based on the table index 28 | func (c App) getUserFromId(userId int) *models.User { 29 | users, err := c.Txn.Select(models.User{}, `select * from User where Id = ?`, userId) 30 | if err != nil { 31 | panic(err) 32 | } 33 | if len(users) == 0 { 34 | return nil 35 | } 36 | return users[0].(*models.User) 37 | } 38 | 39 | // Check if a user is connected(logged in) 40 | func (c App) connected() *models.User { 41 | if c.RenderArgs["user"] != nil { 42 | return c.RenderArgs["user"].(*models.User) 43 | } 44 | if username, ok := c.Session["user"]; ok { 45 | return c.getUser(username) 46 | } 47 | return nil 48 | } 49 | 50 | // Add a user to the session 51 | func (c App) AddUser() revel.Result { 52 | if user := c.connected(); user != nil { 53 | c.RenderArgs["user"] = user 54 | } 55 | return nil 56 | } 57 | 58 | // Show defualt User page 59 | func (c User) Index() revel.Result { 60 | if c.Session["user"] == "" { 61 | c.Flash.Error("Not Logged In") 62 | return c.Redirect(routes.App.Index()) 63 | } 64 | 65 | return c.Render() 66 | } 67 | 68 | // Log a user in based on provided username and password 69 | func (c User) Login(username, password string) revel.Result { 70 | if _, ok := c.RenderArgs["user"]; ok { 71 | return c.Redirect(routes.App.Index()) 72 | } 73 | user := c.getUser(username) 74 | if user != nil { 75 | err := bcrypt.CompareHashAndPassword(user.HashedPassword, []byte(password)) 76 | if err == nil { 77 | c.Session["user"] = username 78 | c.Flash.Success("Welcome, " + username) 79 | return c.Redirect(routes.App.Index()) 80 | } 81 | } 82 | 83 | c.Flash.Out["username"] = username 84 | c.Flash.Error("Login failed") 85 | return c.Redirect(routes.App.Index()) 86 | } 87 | 88 | // Show page for a new user to register 89 | func (c User) Register() revel.Result { 90 | return c.Render() 91 | } 92 | 93 | // Show page to create a new user 94 | func (c User) Create() revel.Result { 95 | 96 | var user *models.User 97 | if c.RenderArgs["user"] == nil { 98 | c.Flash.Error("You must log in first") 99 | return c.Redirect(routes.App.Index()) 100 | } 101 | 102 | user = c.RenderArgs["user"].(*models.User) 103 | 104 | if !user.IsAdmin { 105 | c.Flash.Error("You do not have permissions for this page") 106 | return c.Redirect(routes.App.Index()) 107 | 108 | } 109 | 110 | breadcrumbs := make([]BreadCrumb, 0) 111 | breadcrumbs = append(breadcrumbs, BreadCrumb{Name: "Home", Url: routes.App.Index()}) 112 | breadcrumbs = append(breadcrumbs, BreadCrumb{Name: "Admin", Url: routes.Admin.Index()}) 113 | breadcrumbs = append(breadcrumbs, BreadCrumb{Name: "Manage Users", Url: routes.Admin.Users()}) 114 | breadcrumbs = append(breadcrumbs, BreadCrumb{Name: "Create User", Url: routes.User.Create(), Active: true}) 115 | return c.Render(breadcrumbs) 116 | } 117 | 118 | // Create and save the new user 119 | func (c User) SaveUser(user models.User, verifyPassword string) revel.Result { 120 | var activeUser *models.User 121 | if c.RenderArgs["user"] == nil { 122 | c.Flash.Error("You must log in first") 123 | return c.Redirect(routes.App.Index()) 124 | } 125 | 126 | activeUser = c.RenderArgs["user"].(*models.User) 127 | 128 | if !activeUser.IsAdmin { 129 | c.Flash.Error("You do not have permissions for this page") 130 | return c.Redirect(routes.App.Index()) 131 | 132 | } 133 | 134 | err := c.saveUser(user, verifyPassword) 135 | if err != nil { 136 | c.Flash.Error("Unable to save user", err.Error()) 137 | return c.Redirect(routes.User.Register()) 138 | } 139 | 140 | c.Flash.Success("Created user " + user.Username) 141 | return c.Redirect(routes.Admin.Index()) 142 | } 143 | 144 | // Log the current user out and close their session 145 | func (c User) Logout() revel.Result { 146 | for k := range c.Session { 147 | delete(c.Session, k) 148 | } 149 | c.Flash.Success("Logged Out") 150 | return c.Redirect(routes.App.Index()) 151 | } 152 | 153 | // Display page for the user to change their password 154 | func (c User) Password() revel.Result { 155 | user := c.connected() 156 | if user == nil { 157 | return c.Redirect(routes.App.Index()) 158 | } 159 | 160 | breadcrumbs := make([]BreadCrumb, 0) 161 | breadcrumbs = append(breadcrumbs, BreadCrumb{Name: "Home", Url: "/"}) 162 | breadcrumbs = append(breadcrumbs, BreadCrumb{Name: "Admin", Url: "/Admin/Index"}) 163 | breadcrumbs = append(breadcrumbs, BreadCrumb{Name: "Change Password", Url: "/User/Password", Active: true}) 164 | 165 | return c.Render(user, breadcrumbs) 166 | } 167 | 168 | // Update the user password and update hash in database 169 | func (c User) UpdatePassword() revel.Result { 170 | user := c.connected() 171 | if user == nil { 172 | return c.Redirect(routes.App.Index()) 173 | } 174 | 175 | currentPassword := c.Params.Values.Get("currentPassword") 176 | password := c.Params.Values.Get("password") 177 | verifyPassword := c.Params.Values.Get("verifyPassword") 178 | 179 | c.Validation.Required(currentPassword) 180 | c.Validation.Required(password) 181 | c.Validation.Required(verifyPassword) 182 | 183 | c.Validation.Required(password == verifyPassword). 184 | Message("Passwords do not match") 185 | 186 | if c.Validation.HasErrors() { 187 | c.Validation.Keep() 188 | c.FlashParams() 189 | return c.Redirect(routes.User.Password()) 190 | } 191 | 192 | err := bcrypt.CompareHashAndPassword(user.HashedPassword, []byte(currentPassword)) 193 | if err != nil { 194 | c.Flash.Error("Password provided doesn't match") 195 | return c.Redirect(routes.User.Password()) 196 | } 197 | 198 | user.HashedPassword, _ = bcrypt.GenerateFromPassword( 199 | []byte(password), bcrypt.DefaultCost) 200 | 201 | _, err = c.Txn.Update(user) 202 | if err != nil { 203 | c.Flash.Error("Error Updating user") 204 | return c.Redirect(routes.User.Password()) 205 | } 206 | 207 | c.Flash.Success("Password Changed") 208 | return c.Redirect(routes.App.Index()) 209 | } 210 | -------------------------------------------------------------------------------- /app/views/Admin/EditCertificate.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Edit Certificate"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |
7 |

Project: {{.project.Name}} Edit Certificate

8 | 9 | {{ $project := .project}} 10 | {{ $certificate := .certificate}} 11 | {{ $chain := index .chains 0 }} 12 | {{ $users_map := .users_map}} 13 | {{ $owner_map := .owner_map}} 14 | 15 | 16 |
17 |
Status:
{{ if .revoked}}REVOKED{{else}}VALID{{end}}
18 |
Country:
{{ index .cert.Subject.Country 0 }}
19 |
State:
{{ index .cert.Subject.Province 0 }}
20 |
City:
{{ index .cert.Subject.Locality 0 }}
21 |
Organization:
{{ index .cert.Subject.Organization 0 }}
22 |
Organizational Unit:
{{ index .cert.Subject.OrganizationalUnit 0 }}
23 |
Common Name:
{{ index .cert.Subject.CommonName }}
24 |
25 |
Signed By:
{{ index .cert.Issuer.CommonName }}
26 |
27 |
Key Uses:
28 |
29 | {{ $size := len .keyUses}}{{ range $i, $key := .keyUses }}{{if $i}}, {{end}}{{$key}}{{end}} 30 |
31 |
Extra Key Uses:
32 |
33 | {{ range $i, $key := .extKeyUses }}{{if $i}}, {{end}}{{$key}}{{end}} 34 |
35 |
36 |
CA:
37 |
CA Chain:
{{range $i, $cert := $chain}} 38 | {{if $i}}{{end}}{{$cert.Subject.CommonName}} 39 | {{end}}
40 | 41 | 42 |
43 | 44 |
45 | 46 | 47 | {{ if .revoked}} 48 | {{else}} 49 | {{if .canRevokeKey}} 50 | Revoke cert 51 | {{end}} 52 | 53 | {{end}} 54 | 55 | 56 |
57 | Download Public Certificate 58 | {{if or .canDownloadKey .canRevokeKey}} 59 | 60 | 61 | 80 | {{end}} 81 |
82 |
83 | 84 | 85 |
86 |
87 |
88 |
89 | Edit Ownership: 90 | 91 | 92 | 93 | 94 | {{with $field := field "certificateOwnership" .}} 95 |
96 | 97 |
98 | 103 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 104 |
105 |
106 | {{end}} 107 | 108 |
109 |
110 | 111 |
112 |
113 | 114 |
115 |
116 | 117 |
118 |
119 | 120 | 121 | 122 |
123 | 124 | 164 | 165 | {{template "footer.html" .}} 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /app/views/Admin/ManageProject.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Project"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 |

Project: {{.project.Name}}

7 | {{ $project := .project}} 8 | {{ $templates := .templates}} 9 | {{ $cert_map := .cert_map}} 10 | {{ $revokedMap := .revokedMap }} 11 |
12 | 13 |
14 |
15 |

Certificates

16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {{ range $i, $certificate := .certs }} 30 | 31 | 34 | 37 | 38 | 68 | 69 | 70 | 71 | {{else}} 72 | 73 | {{end}} 74 | 75 | 76 | 77 |
Common NameCertificate AuthorityActions
32 | {{if index $revokedMap $certificate.Id}}{{$certificate.CommonName}}{{else}}{{$certificate.CommonName}}{{end}} 33 | 35 | 36 | 39 |
40 | Download Certificate 41 | 42 | {{if index $cert_map $certificate.Id}} 43 | 44 | Toggle Dropdown 45 | 65 | {{end}} 66 |
67 |
No Certificates created
78 | 79 | 80 |
81 | Create new cert 82 | {{if $templates}} 83 | 84 | Toggle Dropdown 85 | 92 | {{end}} 93 |
94 | 95 |
96 | {{if .csrs}} 97 |
98 |

Signing Requests

99 | 106 |
107 | {{end}} 108 | 109 |
110 | 111 |
112 |
113 |
114 |

Templates

115 |
    116 | {{ range $i, $template := .templates }} 117 |
  • {{$template.Name}}
  • 118 | {{else}} 119 | No Certificate Templates created 120 | {{end}} 121 |
122 | 123 | Create new certificate template 124 |
125 |
126 |
127 |
128 |

Members

129 |
    130 | {{ range $i, $member := .project_members }} 131 |
  • {{$member.Name}}
  • 132 | {{end}} 133 |
134 | 135 | Manage Project Membership 136 | 137 |
138 | 139 |
140 |
141 |
142 | 143 |
144 |
145 | Edit project 146 | 147 | 148 | 149 | 150 |
151 | 152 | {{ range $i, $certificate := .certs }} 153 | 193 | {{end}} 194 | 195 | {{template "footer.html" .}} 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /app/views/Admin/EditTemplate.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Create Certificate Template"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | 8 | {{ $template := .template }} 9 | {{ $cas := .cas}} 10 |
11 |
12 |
13 |
14 | Create Certificate Template: 15 | 16 | 17 | 18 | {{with $field := field "template.Name" .}} 19 |
20 | 21 |
22 | 23 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 24 |
25 |
26 | {{end}} 27 | 28 | {{with $field := field "template.Country" .}} 29 | {{$selected := $template.Country}} {{if $field.Flash}}{{$selected := $field.Flash}}{{end}} 30 |
31 | 32 |
33 | 36 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 37 |
38 |
39 | {{end}} 40 | 41 | {{with $field := field "template.State" .}} 42 |
43 | 44 |
45 | 46 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 47 |
48 |
49 | {{end}} 50 | 51 | 52 | {{with $field := field "template.City" .}} 53 |
54 | 55 |
56 | 57 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 58 |
59 |
60 | {{end}} 61 | 62 | {{with $field := field "template.Organization" .}} 63 |
64 | 65 |
66 | 67 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 68 |
69 |
70 | {{end}} 71 | 72 | {{with $field := field "template.OrganizationUnit" .}} 73 |
74 | 75 |
76 | 77 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 78 |
79 |
80 | {{end}} 81 | 82 | 83 | {{with $field := field "template.Expires" .}} 84 |
85 | 86 |
87 | 88 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 89 |
90 |
91 | {{end}} 92 | 93 | 94 | {{with $field := field "template.PrivateKeyType" .}} 95 |
96 | 97 |
98 | 101 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 102 |
103 |
104 | {{end}} 105 | 106 | 107 | 120 | 121 | {{with $field := field "template.IsCA" .}} 122 |
123 | 124 |
125 | 126 |
127 |
128 | {{end}} 129 | 130 | {{with $field := field "template.KeyUses" .}} 131 |
132 | 133 |
134 | 137 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 138 |
139 |
140 | {{end}} 141 | 142 | {{with $field := field "template.ExtKeyUses" .}} 143 |
144 | 145 |
146 | 149 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 150 |
151 |
152 | {{end}} 153 | 154 | {{with $field := field "template.SignedBy" .}} 155 |
156 | 157 |
158 | 164 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 165 |
166 |
167 | {{end}} 168 | 169 | 170 |
171 |
172 | 173 |
174 |
175 |
176 |
177 |
178 | 179 |
180 | 181 | {{template "footer.html" .}} 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /app/views/Project/CreateCertFromTemplate.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Create Certificate"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | 8 | 9 | {{ $cas := .cas}} 10 | {{ $template := .template}} 11 |
12 |
13 |
14 |
15 | Create Certificate: 16 | 17 | {{with $field := field "certificate.Country" .}} 18 | {{$selected := $template.Country}} {{if $field.Flash}}{{$selected := $field.Flash}}{{end}} 19 |
20 | 21 |
22 | 25 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 26 |
27 |
28 | {{end}} 29 | 30 | {{with $field := field "certificate.State" .}} 31 |
32 | 33 |
34 | 35 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 36 |
37 |
38 | {{end}} 39 | 40 | 41 | {{with $field := field "certificate.City" .}} 42 |
43 | 44 |
45 | 46 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 47 |
48 |
49 | {{end}} 50 | 51 | {{with $field := field "certificate.Organization" .}} 52 |
53 | 54 |
55 | 56 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 57 |
58 |
59 | {{end}} 60 | 61 | {{with $field := field "certificate.OrganizationUnit" .}} 62 |
63 | 64 |
65 | 66 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 67 |
68 |
69 | {{end}} 70 | 71 | {{with $field := field "certificate.CommonName" .}} 72 |
73 | 74 |
75 | 76 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 77 |
78 |
79 | {{end}} 80 | 81 | 82 | {{with $field := field "certificate.Expires" .}} 83 |
84 | 85 |
86 | 87 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 88 |
89 |
90 | {{end}} 91 | 92 | 93 | {{with $field := field "certificate.PrivateKeyType" .}} 94 |
95 | 96 |
97 | 100 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 101 |
102 |
103 | {{end}} 104 | 105 | 106 | 119 | 120 | {{with $field := field "certificate.IsCA" .}} 121 |
122 | 123 |
124 | 125 |
126 |
127 | {{end}} 128 | 129 | {{with $field := field "certificate.KeyUses" .}} 130 |
131 | 132 |
133 | 136 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 137 |
138 |
139 | {{end}} 140 | 141 | {{with $field := field "certificate.ExtKeyUses" .}} 142 |
143 | 144 |
145 | 148 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 149 |
150 |
151 | {{end}} 152 | 153 | {{/* 154 | {{with $field := field "certificate.EncryptionKey" .}} 155 |
156 | 157 |
158 | 159 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 160 |
161 |
162 | {{end}} 163 | */}} 164 | 165 | {{with $field := field "certificate.SignedBy" .}} 166 |
167 | 168 |
169 | 175 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 176 |
177 |
178 | {{end}} 179 | 180 | {{/* 181 | {{with $field := field "certificate.CAEncryptionKey" .}} 182 |
183 | 184 |
185 | 186 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 187 |
188 |
189 | {{end}} 190 | */}} 191 | 192 | 193 |
194 |
195 | 196 |
197 |
198 |
199 |
200 |
201 | 202 |
203 | 204 | {{template "footer.html" .}} 205 | 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /app/views/Project/CreateCert.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Create Certificate"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | 8 | 9 | {{ $cas := .cas}} 10 |
11 |
12 |
13 |
14 | Create Certificate: 15 | 16 | {{with $field := field "certificate.Country" .}} 17 | {{$selected := "US"}} {{if $field.Flash}}{{$selected := $field.Flash}}{{end}} 18 |
19 | 20 |
21 | 24 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 25 |
26 |
27 | {{end}} 28 | 29 | {{with $field := field "certificate.State" .}} 30 |
31 | 32 |
33 | 34 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 35 |
36 |
37 | {{end}} 38 | 39 | 40 | {{with $field := field "certificate.City" .}} 41 |
42 | 43 |
44 | 45 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 46 |
47 |
48 | {{end}} 49 | 50 | {{with $field := field "certificate.Organization" .}} 51 |
52 | 53 |
54 | 55 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 56 |
57 |
58 | {{end}} 59 | 60 | {{with $field := field "certificate.OrganizationUnit" .}} 61 |
62 | 63 |
64 | 65 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 66 |
67 |
68 | {{end}} 69 | 70 | {{with $field := field "certificate.CommonName" .}} 71 |
72 | 73 |
74 | 75 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 76 |
77 |
78 | {{end}} 79 | 80 | 81 | {{with $field := field "certificate.Expires" .}} 82 |
83 | 84 |
85 | 86 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 87 |
88 |
89 | {{end}} 90 | 91 | 92 | {{with $field := field "certificate.PrivateKeyType" .}} 93 |
94 | 95 |
96 | 99 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 100 |
101 |
102 | {{end}} 103 | 104 | 105 | 118 | 119 | {{with $field := field "certificate.IsCA" .}} 120 |
121 | 122 |
123 | 124 |
125 |
126 | {{end}} 127 | 128 | {{with $field := field "certificate.KeyUses" .}} 129 |
130 | 131 |
132 | 143 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 144 |
145 |
146 | {{end}} 147 | 148 | {{with $field := field "certificate.ExtKeyUses" .}} 149 |
150 | 151 |
152 | 161 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 162 |
163 |
164 | {{end}} 165 | 166 | {{/* 167 | {{with $field := field "certificate.EncryptionKey" .}} 168 |
169 | 170 |
171 | 172 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 173 |
174 |
175 | {{end}} 176 | */}} 177 | 178 | {{with $field := field "certificate.SignedBy" .}} 179 |
180 | 181 |
182 | 188 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 189 |
190 |
191 | {{end}} 192 | 193 | {{/* 194 | {{with $field := field "certificate.CAEncryptionKey" .}} 195 |
196 | 197 |
198 | 199 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 200 |
201 |
202 | {{end}} 203 | */}} 204 | 205 | 206 | 207 | 208 |
209 |
210 | 211 |
212 |
213 |
214 |
215 |
216 | 217 |
218 | 219 | {{template "footer.html" .}} 220 | 221 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /app/models/certificate.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | //"github.com/robfig/revel" 6 | "github.com/coopernurse/gorp" 7 | ) 8 | 9 | // The Full data associated with a certificate 10 | type FullCertificate struct { 11 | Country, State, City string 12 | Organization, OrganizationUnit string 13 | CommonName string 14 | Expires string 15 | PrivateKeyType string 16 | IsCA bool 17 | KeyUses, ExtKeyUses []string 18 | SignedBy int 19 | 20 | EncryptionKey string 21 | CAEncryptionKey string 22 | } 23 | 24 | // Types of Key used 25 | type KeyType int 26 | 27 | const ( 28 | RSA KeyType = iota 29 | ECDSA 30 | ) 31 | 32 | // Certificate model as stored in the database 33 | type Certificate struct { 34 | // Unique ID 35 | Id int 36 | // PEM and Private key as can be written to file 37 | PEM, PrivateKey []byte 38 | // The x509 CN of the certificate 39 | CommonName string 40 | // The type of key used 41 | KeyType 42 | 43 | SerialNumber int 44 | 45 | // Whether the certificate represents a CA, and whether the key is encrypted 46 | CA, Encrypted bool 47 | 48 | // The ID of the project this cert is associated with 49 | ProjectId int 50 | 51 | // The Project this cert is associated with 52 | Project *Project 53 | } 54 | 55 | // Model for CAs to keep track of which serial number was last issued 56 | type CACount struct { 57 | // Unique ID 58 | Id int 59 | 60 | // The CA cert 61 | CertificateId int 62 | Certificate *Certificate 63 | 64 | // Last Serial number used 65 | SerialNumber int 66 | } 67 | 68 | // Certificate template model as stored in the database 69 | type CertificateTemplate struct { 70 | // Unique ID 71 | Id int 72 | Name string 73 | 74 | Country, State, City string 75 | Organization, OrganizationUnit string 76 | Expires string 77 | PrivateKeyType string 78 | IsCA bool 79 | KeyUses, ExtKeyUses string 80 | SignedBy int 81 | 82 | // The ID of the project this cert is associated with 83 | ProjectId int 84 | 85 | // The Project this cert is associated with 86 | Project *Project 87 | } 88 | 89 | // Certificate model as stored in the database 90 | type CertificateRequest struct { 91 | // Unique ID 92 | Id int 93 | // PEM and Private key as can be written to file 94 | CSR []byte 95 | 96 | // The ID of the Certificate requested to be signed by 97 | RequestedCAId int 98 | // The Certificate requested to be signed by 99 | RequestedCA *Certificate 100 | 101 | // The ID of the project this cert is associated with 102 | ProjectId int 103 | 104 | // The Project this cert is associated with 105 | Project *Project 106 | 107 | // The user that submitted the CSR 108 | UserId int 109 | User *User 110 | } 111 | 112 | // Join a certificate to the user that owns it 113 | type CertificateOwnership struct { 114 | Id int 115 | CertificateId, UserId int 116 | 117 | Certificate *Certificate 118 | User *User 119 | } 120 | 121 | // Represents a One Time link to download a key 122 | type CertificateOneTimeDownload struct { 123 | Id int 124 | CertificateId int 125 | Hash string 126 | 127 | Certificate *Certificate 128 | } 129 | 130 | // Represents a revoked certificate, used to generate 131 | type RevokedCertificate struct { 132 | Id int 133 | CertificateId int 134 | 135 | Certificate *Certificate 136 | } 137 | 138 | // Before inserting a cert into the db, make sure that ProjectId is set 139 | func (c *Certificate) PreInsert(_ gorp.SqlExecutor) error { 140 | if c.Project != nil { 141 | c.ProjectId = c.Project.Id 142 | } 143 | 144 | return nil 145 | } 146 | 147 | // After get a cert from the db, set the project based on the ProjectId 148 | func (c *Certificate) PostGet(exe gorp.SqlExecutor) error { 149 | var ( 150 | obj interface{} 151 | err error 152 | ) 153 | 154 | if c.ProjectId != 0 { 155 | obj, err = exe.Get(Project{}, c.ProjectId) 156 | if err != nil { 157 | return fmt.Errorf("Error loading a certificate's project (%d): %s", c.ProjectId, err) 158 | } 159 | c.Project = obj.(*Project) 160 | } 161 | 162 | return nil 163 | } 164 | 165 | // Before inserting a template into the db, make sure that ProjectId is set 166 | func (c *CertificateTemplate) PreInsert(_ gorp.SqlExecutor) error { 167 | if c.Project != nil { 168 | c.ProjectId = c.Project.Id 169 | } 170 | 171 | return nil 172 | } 173 | 174 | // After getting a template from the db, set the project based on the ProjectId 175 | func (c *CertificateTemplate) PostGet(exe gorp.SqlExecutor) error { 176 | var ( 177 | obj interface{} 178 | err error 179 | ) 180 | 181 | if c.ProjectId != 0 { 182 | obj, err = exe.Get(Project{}, c.ProjectId) 183 | if err != nil { 184 | return fmt.Errorf("Error loading a certificate's project (%d): %s", c.ProjectId, err) 185 | } 186 | c.Project = obj.(*Project) 187 | } 188 | 189 | return nil 190 | } 191 | 192 | // Before inserting a certificate request into the db, make sure that ProjectId and RequestedCAID are set 193 | func (c *CertificateRequest) PreInsert(_ gorp.SqlExecutor) error { 194 | if c.Project != nil { 195 | c.ProjectId = c.Project.Id 196 | } 197 | if c.RequestedCA != nil { 198 | c.RequestedCAId = c.RequestedCA.Id 199 | } 200 | if c.User != nil { 201 | c.UserId = c.User.Id 202 | } 203 | 204 | return nil 205 | } 206 | 207 | // After getting a template from the db, set the project based on the ProjectId and RequestedCAID 208 | func (c *CertificateRequest) PostGet(exe gorp.SqlExecutor) error { 209 | var ( 210 | obj interface{} 211 | err error 212 | ) 213 | 214 | if c.ProjectId != 0 { 215 | obj, err = exe.Get(Project{}, c.ProjectId) 216 | if err != nil { 217 | return fmt.Errorf("Error loading a certificate's project (%d): %s", c.ProjectId, err) 218 | } 219 | c.Project = obj.(*Project) 220 | } 221 | 222 | if c.RequestedCAId != 0 { 223 | obj, err = exe.Get(Certificate{}, c.RequestedCAId) 224 | if err != nil { 225 | return fmt.Errorf("Error loading a csr's requested CA (%d): %s", c.RequestedCAId, err) 226 | } 227 | c.RequestedCA = obj.(*Certificate) 228 | } 229 | 230 | if c.UserId != 0 { 231 | obj, err = exe.Get(User{}, c.UserId) 232 | if err != nil { 233 | return fmt.Errorf("Error loading a csr's requesting user (%d): %s", c.UserId, err) 234 | } 235 | c.User = obj.(*User) 236 | } 237 | 238 | return nil 239 | } 240 | 241 | // Before inserting a cert ownership into the db, make sure that userID and CertificateId are set 242 | func (c *CertificateOwnership) PreInsert(_ gorp.SqlExecutor) error { 243 | if c.User != nil { 244 | c.UserId = c.User.Id 245 | } 246 | 247 | if c.Certificate != nil { 248 | c.CertificateId = c.Certificate.Id 249 | } 250 | 251 | return nil 252 | } 253 | 254 | // After getting certificate ownership, set Certificate and User 255 | func (c *CertificateOwnership) PostGet(exe gorp.SqlExecutor) error { 256 | var ( 257 | obj interface{} 258 | err error 259 | ) 260 | 261 | if c.UserId != 0 { 262 | obj, err = exe.Get(User{}, c.UserId) 263 | if err != nil { 264 | return fmt.Errorf("Error loading a certificate's user (%d): %s", c.UserId, err) 265 | } 266 | c.User = obj.(*User) 267 | } 268 | 269 | if c.CertificateId != 0 { 270 | obj, err = exe.Get(Certificate{}, c.CertificateId) 271 | if err != nil { 272 | return fmt.Errorf("Error loading a certificate (%d): %s", c.CertificateId, err) 273 | } 274 | c.Certificate = obj.(*Certificate) 275 | } 276 | 277 | return nil 278 | } 279 | 280 | // Before inserting a certOneTimeDownload into the db, make sure that CertificateId is set 281 | func (c *CertificateOneTimeDownload) PreInsert(_ gorp.SqlExecutor) error { 282 | 283 | if c.Certificate != nil { 284 | c.CertificateId = c.Certificate.Id 285 | } 286 | 287 | return nil 288 | } 289 | 290 | // After getting CertOneTimeDownload from db, set Certificate 291 | func (c *CertificateOneTimeDownload) PostGet(exe gorp.SqlExecutor) error { 292 | var ( 293 | obj interface{} 294 | err error 295 | ) 296 | 297 | if c.CertificateId != 0 { 298 | obj, err = exe.Get(Certificate{}, c.CertificateId) 299 | if err != nil { 300 | return fmt.Errorf("Error loading a certificate (%d): %s", c.CertificateId, err) 301 | } 302 | c.Certificate = obj.(*Certificate) 303 | } 304 | 305 | return nil 306 | } 307 | 308 | // Before inserting a Revoked Certificate into the db, make sure that CertificateId is set 309 | func (c *RevokedCertificate) PreInsert(_ gorp.SqlExecutor) error { 310 | 311 | if c.Certificate != nil { 312 | c.CertificateId = c.Certificate.Id 313 | } 314 | 315 | return nil 316 | } 317 | 318 | // After getting a Revoked Certificate from db, set Certificate 319 | func (c *RevokedCertificate) PostGet(exe gorp.SqlExecutor) error { 320 | var ( 321 | obj interface{} 322 | err error 323 | ) 324 | 325 | if c.CertificateId != 0 { 326 | obj, err = exe.Get(Certificate{}, c.CertificateId) 327 | if err != nil { 328 | return fmt.Errorf("Error loading a certificate (%d): %s", c.CertificateId, err) 329 | } 330 | c.Certificate = obj.(*Certificate) 331 | } 332 | 333 | return nil 334 | } 335 | 336 | // Before inserting a Revoked Certificate into the db, make sure that CertificateId is set 337 | func (c *CACount) PreInsert(_ gorp.SqlExecutor) error { 338 | 339 | if c.Certificate != nil { 340 | c.CertificateId = c.Certificate.Id 341 | } 342 | 343 | return nil 344 | } 345 | 346 | // After getting a Revoked Certificate from db, set Certificate 347 | func (c *CACount) PostGet(exe gorp.SqlExecutor) error { 348 | var ( 349 | obj interface{} 350 | err error 351 | ) 352 | 353 | if c.CertificateId != 0 { 354 | obj, err = exe.Get(Certificate{}, c.CertificateId) 355 | if err != nil { 356 | return fmt.Errorf("Error loading a certificate (%d): %s", c.CertificateId, err) 357 | } 358 | c.Certificate = obj.(*Certificate) 359 | } 360 | 361 | return nil 362 | } 363 | -------------------------------------------------------------------------------- /app/views/Admin/SignCSR.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Sign Certificate Request"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | 8 | {{ $certificate := .certificate}} 9 | {{ $requestedCA := .requestedCA}} 10 | 11 | {{ $cas := .cas}} 12 |
13 |
14 |
15 |
16 | Create Certificate: 17 | 18 | {{with $field := field "certificate.Country" .}} 19 | {{$selected := "US"}} {{if $field.Flash}}{{$selected := $field.Flash}}{{else}}{{$selected := $certificate.Country}}{{end}} 20 |
21 | 22 |
23 | 26 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 27 |
28 |
29 | {{end}} 30 | 31 | {{with $field := field "certificate.State" .}} 32 |
33 | 34 |
35 | 36 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 37 |
38 |
39 | {{end}} 40 | 41 | 42 | {{with $field := field "certificate.City" .}} 43 |
44 | 45 |
46 | 47 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 48 |
49 |
50 | {{end}} 51 | 52 | {{with $field := field "certificate.Organization" .}} 53 |
54 | 55 |
56 | 57 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 58 |
59 |
60 | {{end}} 61 | 62 | {{with $field := field "certificate.OrganizationUnit" .}} 63 |
64 | 65 |
66 | 67 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 68 |
69 |
70 | {{end}} 71 | 72 | {{with $field := field "certificate.CommonName" .}} 73 |
74 | 75 |
76 | 77 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 78 |
79 |
80 | {{end}} 81 | 82 | 83 | {{with $field := field "certificate.Expires" .}} 84 |
85 | 86 |
87 | 88 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 89 |
90 |
91 | {{end}} 92 | 93 | 94 | {{with $field := field "certificate.PrivateKeyType" .}} 95 |
96 | 97 |
98 | 101 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 102 |
103 |
104 | {{end}} 105 | 106 | 107 | 120 | 121 | {{with $field := field "certificate.IsCA" .}} 122 |
123 | 124 |
125 | 126 |
127 |
128 | {{end}} 129 | 130 | {{with $field := field "certificate.KeyUses" .}} 131 |
132 | 133 |
134 | 145 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 146 |
147 |
148 | {{end}} 149 | 150 | {{with $field := field "certificate.ExtKeyUses" .}} 151 |
152 | 153 |
154 | 163 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 164 |
165 |
166 | {{end}} 167 | 168 | {{with $field := field "certificate.EncryptionKey" .}} 169 |
170 | 171 |
172 | 173 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 174 |
175 |
176 | {{end}} 177 | 178 | {{with $field := field "certificate.SignedBy" .}} 179 |
180 | 181 |
182 | 188 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 189 |
190 |
191 | {{end}} 192 | 193 | {{with $field := field "certificate.CAEncryptionKey" .}} 194 |
195 | 196 |
197 | 198 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 199 |
200 |
201 | {{end}} 202 | 203 | 204 | 205 | 206 |
207 |
208 | 209 |
210 |
211 |
212 |
213 |
214 | 215 |
216 | 217 | {{template "footer.html" .}} 218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /app/controllers/init.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "bytes" 5 | "github.com/robfig/revel" 6 | "html/template" 7 | "math/rand" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func init() { 13 | 14 | // Seed the random library 15 | rand.Seed(time.Now().UTC().UnixNano()) 16 | 17 | revel.OnAppStart(Init) 18 | revel.InterceptMethod((*GorpController).Begin, revel.BEFORE) 19 | //revel.InterceptMethod(Application.AddUser, revel.BEFORE) 20 | revel.InterceptMethod(App.AddUser, revel.BEFORE) 21 | revel.InterceptMethod(App.logEntry, revel.BEFORE) 22 | //revel.InterceptMethod(Hotels.checkUser, revel.BEFORE) 23 | revel.InterceptMethod((*GorpController).Commit, revel.AFTER) 24 | revel.InterceptMethod((*GorpController).Rollback, revel.FINALLY) 25 | 26 | revel.TemplateFuncs["countryOption"] = getCountryOptions 27 | revel.TemplateFuncs["keyOption"] = getKeyOptions 28 | revel.TemplateFuncs["keyUsageOption"] = getKeyUsageOptions 29 | revel.TemplateFuncs["extKeyUsageOption"] = getExtKeyUsageOptions 30 | } 31 | 32 | var countryList = []string{"AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "KH", "CM", "CA", "CV", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CD", "CK", "CR", "CI", "HR", "CU", "CY", "CZ", "DK", "DJ", "DM", "DO", "TP", "EC", "EG", "SV", "GQ", "ER", "EE", "ET", "FK", "FO", "FJ", "FI", "FR", "FX", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GN", "GW", "GY", "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IL", "IT", "JM", "JP", "JO", "KZ", "KE", "KI", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MK", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "AN", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP", "NO", "OM", "PK", "PW", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "KN", "LC", "VC", "WS", "SM", "ST", "SA", "SN", "SC", "SL", "SG", "SK", "SI", "SB", "SO", "ZA", "GS", "ES", "LK", "SH", "PM", "SD", "SR", "SJ", "SZ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "YU", "ZM", "ZW"} 33 | var countryMap = map[string]string{"AF": "Afghanistan", "AL": "Albania", "DZ": "Algeria", "AS": "American Samoa", "AD": "Andorra", "AO": "Angola", "AI": "Anguilla", "AQ": "Antarctica", "AG": "Antigua and Barbuda", "AR": "Argentina", "AM": "Armenia", "AW": "Aruba", "AU": "Australia", "AT": "Austria", "AZ": "Azerbaijan", "BS": "Bahamas", "BH": "Bahrain", "BD": "Bangladesh", "BB": "Barbados", "BY": "Belarus", "BE": "Belgium", "BZ": "Belize", "BJ": "Benin", "BM": "Bermuda", "BT": "Bhutan", "BO": "Bolivia", "BA": "Bosnia and Herzegowina", "BW": "Botswana", "BV": "Bouvet Island", "BR": "Brazil", "IO": "British Indian Ocean Territory", "BN": "Brunei Darussalam", "BG": "Bulgaria", "BF": "Burkina Faso", "BI": "Burundi", "KH": "Cambodia", "CM": "Cameroon", "CA": "Canada", "CV": "Cape Verde", "KY": "Cayman Islands", "CF": "Central African Republic", "TD": "Chad", "CL": "Chile", "CN": "China", "CX": "Christmas Island", "CC": "Cocos (Keeling) Islands", "CO": "Colombia", "KM": "Comoros", "CG": "Congo", "CD": "Congo, the Democratic Republic of the", "CK": "Cook Islands", "CR": "Costa Rica", "CI": "Cote d'Ivoire", "HR": "Croatia (Hrvatska)", "CU": "Cuba", "CY": "Cyprus", "CZ": "Czech Republic", "DK": "Denmark", "DJ": "Djibouti", "DM": "Dominica", "DO": "Dominican Republic", "TP": "East Timor", "EC": "Ecuador", "EG": "Egypt", "SV": "El Salvador", "GQ": "Equatorial Guinea", "ER": "Eritrea", "EE": "Estonia", "ET": "Ethiopia", "FK": "Falkland Islands (Malvinas)", "FO": "Faroe Islands", "FJ": "Fiji", "FI": "Finland", "FR": "France", "FX": "France, Metropolitan", "GF": "French Guiana", "PF": "French Polynesia", "TF": "French Southern Territories", "GA": "Gabon", "GM": "Gambia", "GE": "Georgia", "DE": "Germany", "GH": "Ghana", "GI": "Gibraltar", "GR": "Greece", "GL": "Greenland", "GD": "Grenada", "GP": "Guadeloupe", "GU": "Guam", "GT": "Guatemala", "GN": "Guinea", "GW": "Guinea-Bissau", "GY": "Guyana", "HT": "Haiti", "HM": "Heard and Mc Donald Islands", "VA": "Holy See (Vatican City State)", "HN": "Honduras", "HK": "Hong Kong", "HU": "Hungary", "IS": "Iceland", "IN": "India", "ID": "Indonesia", "IR": "Iran (Islamic Republic of)", "IQ": "Iraq", "IE": "Ireland", "IL": "Israel", "IT": "Italy", "JM": "Jamaica", "JP": "Japan", "JO": "Jordan", "KZ": "Kazakhstan", "KE": "Kenya", "KI": "Kiribati", "KP": "Korea, Democratic People's Republic of", "KR": "Korea, Republic of", "KW": "Kuwait", "KG": "Kyrgyzstan", "LA": "Lao People's Democratic Republic", "LV": "Latvia", "LB": "Lebanon", "LS": "Lesotho", "LR": "Liberia", "LY": "Libyan Arab Jamahiriya", "LI": "Liechtenstein", "LT": "Lithuania", "LU": "Luxembourg", "MO": "Macau", "MK": "Macedonia, The Former Yugoslav Republic of", "MG": "Madagascar", "MW": "Malawi", "MY": "Malaysia", "MV": "Maldives", "ML": "Mali", "MT": "Malta", "MH": "Marshall Islands", "MQ": "Martinique", "MR": "Mauritania", "MU": "Mauritius", "YT": "Mayotte", "MX": "Mexico", "FM": "Micronesia, Federated States of", "MD": "Moldova, Republic of", "MC": "Monaco", "MN": "Mongolia", "MS": "Montserrat", "MA": "Morocco", "MZ": "Mozambique", "MM": "Myanmar", "NA": "Namibia", "NR": "Nauru", "NP": "Nepal", "NL": "Netherlands", "AN": "Netherlands Antilles", "NC": "New Caledonia", "NZ": "New Zealand", "NI": "Nicaragua", "NE": "Niger", "NG": "Nigeria", "NU": "Niue", "NF": "Norfolk Island", "MP": "Northern Mariana Islands", "NO": "Norway", "OM": "Oman", "PK": "Pakistan", "PW": "Palau", "PA": "Panama", "PG": "Papua New Guinea", "PY": "Paraguay", "PE": "Peru", "PH": "Philippines", "PN": "Pitcairn", "PL": "Poland", "PT": "Portugal", "PR": "Puerto Rico", "QA": "Qatar", "RE": "Reunion", "RO": "Romania", "RU": "Russian Federation", "RW": "Rwanda", "KN": "Saint Kitts and Nevis", "LC": "Saint LUCIA", "VC": "Saint Vincent and the Grenadines", "WS": "Samoa", "SM": "San Marino", "ST": "Sao Tome and Principe", "SA": "Saudi Arabia", "SN": "Senegal", "SC": "Seychelles", "SL": "Sierra Leone", "SG": "Singapore", "SK": "Slovakia (Slovak Republic)", "SI": "Slovenia", "SB": "Solomon Islands", "SO": "Somalia", "ZA": "South Africa", "GS": "South Georgia and the South Sandwich Islands", "ES": "Spain", "LK": "Sri Lanka", "SH": "St. Helena", "PM": "St. Pierre and Miquelon", "SD": "Sudan", "SR": "Suriname", "SJ": "Svalbard and Jan Mayen Islands", "SZ": "Swaziland", "SE": "Sweden", "CH": "Switzerland", "SY": "Syrian Arab Republic", "TW": "Taiwan, Province of China", "TJ": "Tajikistan", "TZ": "Tanzania, United Republic of", "TH": "Thailand", "TG": "Togo", "TK": "Tokelau", "TO": "Tonga", "TT": "Trinidad and Tobago", "TN": "Tunisia", "TR": "Turkey", "TM": "Turkmenistan", "TC": "Turks and Caicos Islands", "TV": "Tuvalu", "UG": "Uganda", "UA": "Ukraine", "AE": "United Arab Emirates", "GB": "United Kingdom", "US": "United States", "UM": "United States Minor Outlying Islands", "UY": "Uruguay", "UZ": "Uzbekistan", "VU": "Vanuatu", "VE": "Venezuela", "VN": "Viet Nam", "VG": "Virgin Islands (British)", "VI": "Virgin Islands (U.S.)", "WF": "Wallis and Futuna Islands", "EH": "Western Sahara", "YE": "Yemen", "YU": "Yugoslavia", "ZM": "Zambia", "ZW": "Zimbabwe"} 34 | 35 | // Function available to templates to fill country dropdown 36 | func getCountryOptions(selectedCountry string) template.HTML { 37 | option_tmpl := "" 38 | t := template.Must(template.New("option").Parse(option_tmpl)) 39 | type countryOption struct { 40 | Abbreviation string 41 | Country string 42 | Selected string 43 | } 44 | output := "" 45 | for _, abbr := range countryList { 46 | selected := "" 47 | if selectedCountry == abbr { 48 | selected = "selected" 49 | } 50 | buff := bytes.NewBufferString("") 51 | err := t.Execute(buff, countryOption{Abbreviation: abbr, Country: countryMap[abbr], Selected: selected}) 52 | if err != nil { 53 | 54 | } 55 | output += buff.String() 56 | } 57 | return template.HTML(output) 58 | } 59 | 60 | var keyList = []string{"RSA 1024", "RSA 2048", "RSA 4096", "ECDSA 224", "ECDSA 256"} //, "ECDSA 384", "ECDSA 521" 61 | 62 | // Function available to templates to fill Certificate Key Type dropdown 63 | func getKeyOptions(selectedOption string) template.HTML { 64 | option_tmpl := "" 65 | t := template.Must(template.New("option").Parse(option_tmpl)) 66 | type keyOption struct { 67 | Place int 68 | Key string 69 | Selected string 70 | } 71 | output := "" 72 | for place, key := range keyList { 73 | selected := "" 74 | if selectedOption == key { 75 | selected = "selected" 76 | } 77 | buff := bytes.NewBufferString("") 78 | err := t.Execute(buff, keyOption{Place: place, Key: key, Selected: selected}) 79 | if err != nil { 80 | 81 | } 82 | output += buff.String() 83 | } 84 | return template.HTML(output) 85 | } 86 | 87 | var keyUsageList = []string{"1", "2", "4", "8", "16", "32", "64", "128", "256"} 88 | var keyUsageMap = map[string]string{"1": "Digital Signature", "2": "Content Commitment", "4": "Key Encipherment", "8": "Data Encipherment", "16": "Key Agreement", "32": "Cert Sign", "64": "CRL Sign", "128": "Encipher Only", "256": "Decipher Only"} 89 | 90 | // Function available to templats to fill Certificate Key Usage dropdown 91 | func getKeyUsageOptions(selectedKeys string) template.HTML { 92 | option_tmpl := "" 93 | t := template.Must(template.New("option").Parse(option_tmpl)) 94 | type keyUsageOption struct { 95 | Value string 96 | KeyUsage string 97 | Selected string 98 | } 99 | output := "" 100 | for _, value := range keyUsageList { 101 | selected := "" 102 | if strings.Contains(selectedKeys, value) { 103 | selected = "selected" 104 | } 105 | buff := bytes.NewBufferString("") 106 | err := t.Execute(buff, keyUsageOption{Value: value, KeyUsage: keyUsageMap[value], Selected: selected}) 107 | if err != nil { 108 | 109 | } 110 | output += buff.String() 111 | } 112 | return template.HTML(output) 113 | } 114 | 115 | var extKeyUsageList = []string{"0", "1", "2", "3", "4", "8", "9"} 116 | var extKeyUsageMap = map[string]string{"0": "Any", "1": "Server Authentication", "2": "Client Authentication", "3": "Code Signing", "4": "Email Protection", "8": "Time Stamping", "9": "OCSP Signing"} 117 | 118 | // Function available to templats to fill Certificate Extra Key Usage dropdown 119 | func getExtKeyUsageOptions(selectedKeys string) template.HTML { 120 | option_tmpl := "" 121 | t := template.Must(template.New("option").Parse(option_tmpl)) 122 | type keyUsageOption struct { 123 | Value string 124 | KeyUsage string 125 | Selected string 126 | } 127 | output := "" 128 | for _, value := range extKeyUsageList { 129 | selected := "" 130 | if strings.Contains(selectedKeys, value) { 131 | selected = "selected" 132 | } 133 | buff := bytes.NewBufferString("") 134 | err := t.Execute(buff, keyUsageOption{Value: value, KeyUsage: extKeyUsageMap[value], Selected: selected}) 135 | if err != nil { 136 | 137 | } 138 | output += buff.String() 139 | } 140 | return template.HTML(output) 141 | } 142 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /public/css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.0.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857142858em;text-align:center}.fa-ul{padding-left:0;margin-left:2.142857142857143em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;top:.14285714285714285em;text-align:center}.fa-li.fa-lg{left:-1.8571428571428572em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1);-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);-o-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1);-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"} -------------------------------------------------------------------------------- /app/views/Admin/NewTemplate.html: -------------------------------------------------------------------------------- 1 | {{set . "title" "Create Certificate Template"}} 2 | {{template "header.html" .}} 3 | 4 |
5 |
6 | 7 | 8 | 9 | {{ $cas := .cas}} 10 |
11 |
12 |
13 |
14 | Create Certificate Template: 15 | 16 | {{with $field := field "template.Name" .}} 17 |
18 | 19 |
20 | 21 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 22 |
23 |
24 | {{end}} 25 | 26 | {{with $field := field "template.Country" .}} 27 |
28 | 29 |
30 | 271 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 272 |
273 |
274 | {{end}} 275 | 276 | {{with $field := field "template.State" .}} 277 |
278 | 279 |
280 | 281 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 282 |
283 |
284 | {{end}} 285 | 286 | 287 | {{with $field := field "template.City" .}} 288 |
289 | 290 |
291 | 292 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 293 |
294 |
295 | {{end}} 296 | 297 | {{with $field := field "template.Organization" .}} 298 |
299 | 300 |
301 | 302 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 303 |
304 |
305 | {{end}} 306 | 307 | {{with $field := field "template.OrganizationUnit" .}} 308 |
309 | 310 |
311 | 312 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 313 |
314 |
315 | {{end}} 316 | 317 | 318 | {{with $field := field "template.Expires" .}} 319 |
320 | 321 |
322 | 323 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 324 |
325 |
326 | {{end}} 327 | 328 | 329 | {{with $field := field "template.PrivateKeyType" .}} 330 |
331 | 332 |
333 | 342 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 343 |
344 |
345 | {{end}} 346 | 347 | 348 | 361 | 362 | {{with $field := field "template.IsCA" .}} 363 |
364 | 365 |
366 | 367 |
368 |
369 | {{end}} 370 | 371 | {{with $field := field "template.KeyUses" .}} 372 |
373 | 374 |
375 | 386 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 387 |
388 |
389 | {{end}} 390 | 391 | {{with $field := field "template.ExtKeyUses" .}} 392 |
393 | 394 |
395 | 404 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 405 |
406 |
407 | {{end}} 408 | 409 | {{with $field := field "template.SignedBy" .}} 410 |
411 | 412 |
413 | 419 | {{ if $field.ErrorClass}}{{$field.Error}} {{end}} 420 |
421 |
422 | {{end}} 423 | 424 | 425 |
426 |
427 | 428 |
429 |
430 |
431 |
432 |
433 | 434 |
435 | 436 | {{template "footer.html" .}} 437 | 438 | 439 | 440 | 441 | --------------------------------------------------------------------------------