Example #1
0
// POST /users/password/reset
func HandlePasswordResetSend(context router.Context) error {

	// Find the user by email (if not found let them know)
	// Find the user by hex token in the db
	email := context.Param("email")
	user, err := users.First(users.Where("email=?", email))
	if err != nil {
		return router.Redirect(context, "/users/password/reset?message=invalid_email")
	}

	// Generate a random token and url for the email
	token := auth.BytesToHex(auth.RandomToken())

	// Generate the url to use in our email
	base := fmt.Sprintf("%s://%s", context.Request().URL.Scheme, context.Request().URL.Host)
	url := fmt.Sprintf("%s/users/password?token=%s", base, token)

	context.Logf("#info sending reset email:%s url:%s", email, url)

	// Update the user record with with this token
	p := map[string]string{"reset_token": token}
	user.Update(p)

	// Send a password reset email out
	//mail.Send("mymail")

	// Tell the user what we have done
	return router.Redirect(context, "/users/password/sent")
}
Example #2
0
// HandleHome displays a list of stories using gravity to order them
// used for the home page for gravity rank see votes.go
// responds to GET /
func HandleHome(context router.Context) error {

	// Build a query
	q := stories.Query().Limit(listLimit)

	// Select only above 0 points,  Order by rank, then points, then name
	q.Where("points > 0").Order("rank desc, points desc, id desc")

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	setStoriesMetadata(view, context.Request())
	view.AddKey("page", page)
	view.AddKey("stories", results)
	view.Template("stories/views/index.html.got")

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Example #3
0
// HandleDownload sends a single file
func HandleDownload(context router.Context) error {

	// Find the file
	file, err := files.Find(context.ParamInt("id"))
	if err != nil {
		return router.InternalError(err)
	}

	// Authorise access to this file - only the file owners can access their own files
	err = authorise.Resource(context, file)
	if err != nil {
		return router.NotAuthorizedError(err)
	}

	// If we are permitted, send the file to the user
	//w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	//http.DetectContentType(data []byte) string
	h := context.Header()
	h.Set("Content-Type", "application/pgp-encrypted")
	h.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", file.Name()))
	h.Set("Content-Transfer-Encoding", "binary")

	http.ServeFile(context, context.Request(), file.Path)
	return nil
}
Example #4
0
// AuthenticityTokenFilter sets the authenticity token on the context and on the cookie
func AuthenticityTokenFilter(c router.Context) error {
	token, err := auth.AuthenticityToken(c.Writer(), c.Request())
	if err != nil {
		return err
	}
	c.Set(auth.SessionTokenKey, token)
	return nil
}
// CreateAuthenticityToken returns an auth.AuthenticityToken and writes a secret to check it to the cookie
func CreateAuthenticityToken(context router.Context) string {
	token, err := auth.AuthenticityToken(context.Writer(), context.Request())
	if err != nil {
		context.Logf("#warn invalid authenticity token at %v", context)
		return "" // empty strings are invalid as tokens
	}

	return token
}
Example #6
0
func loginUser(context router.Context, user *users.User) error {
	// Now save the user details in a secure cookie, so that we remember the next request
	session, err := auth.Session(context, context.Request())
	if err != nil {
		return err
	}

	context.Logf("#info Login success for user: %d %s", user.Id, user.Email)
	session.Set(auth.SessionUserKey, fmt.Sprintf("%d", user.Id))
	session.Save(context)
	return nil
}
Example #7
0
// HandleIndex displays a list of stories at /stories
func HandleIndex(context router.Context) error {

	// Build a query
	q := stories.Query().Limit(listLimit)

	// Order by date by default
	q.Where("points > -6").Order("created_at desc")

	// Filter if necessary - this assumes name and summary cols
	filter := context.Param("q")
	if len(filter) > 0 {

		// Replace special characters with escaped sequence
		filter = strings.Replace(filter, "_", "\\_", -1)
		filter = strings.Replace(filter, "%", "\\%", -1)

		wildcard := "%" + filter + "%"

		// Perform a wildcard search for name or url
		q.Where("stories.name ILIKE ? OR stories.url ILIKE ?", wildcard, wildcard)

		// If filtering, order by rank, not by date
		q.Order("rank desc, points desc, id desc")
	}

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)

	setStoriesMetadata(view, context.Request())
	view.AddKey("page", page)
	view.AddKey("stories", results)

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Example #8
0
// AuthenticityToken checks the token in the current request
func AuthenticityToken(context router.Context) error {
	token := context.Param(auth.SessionTokenKey)
	err := auth.CheckAuthenticityToken(token, context.Request())
	if err != nil {
		// If the check fails, log out the user and completely clear the session
		context.Logf("#warn invalid authenticity token at %v", context)
		session, err := auth.SessionGet(context.Request())
		if err != nil {
			return err
		}
		session.Clear(context.Writer())
	}

	return err
}
Example #9
0
// storiesXMLPath returns the xml path for a given request to a stories link
func storiesXMLPath(context router.Context) string {

	request := context.Request()

	p := strings.Replace(request.URL.Path, ".xml", "", 1)
	if p == "/" {
		p = "/index"
	}

	q := request.URL.RawQuery
	if len(q) > 0 {
		q = "?" + q
	}

	return fmt.Sprintf("%s.xml%s", p, q)
}
Example #10
0
// HandleUpvoted displays a list of stories the user has upvoted in the past
func HandleUpvoted(context router.Context) error {

	// Build a query
	q := stories.Query().Limit(listLimit)

	// Select only above 0 points,  Order by rank, then points, then name
	q.Where("points > 0").Order("rank desc, points desc, id desc")

	// Select only stories which the user has upvoted
	user := authorise.CurrentUser(context)
	if !user.Anon() {
		// Can we use a join instead?
		v := query.New("votes", "story_id").Select("select story_id as id from votes").Where("user_id=? AND story_id IS NOT NULL AND points > 0", user.Id)

		storyIDs := v.ResultIDs()
		if len(storyIDs) > 0 {
			q.WhereIn("id", storyIDs)
		}
	}

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	setStoriesMetadata(view, context.Request())
	view.AddKey("page", page)
	view.AddKey("stories", results)
	view.Template("stories/views/index.html.got")

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Example #11
0
// HandleCreate handles the POST of the create form for users
func HandleCreate(context router.Context) error {

	// Authorise
	err := authorise.Resource(context, nil)
	if err != nil {
		return router.NotAuthorizedError(err)
	}

	// Setup context
	params, err := context.Params()
	if err != nil {
		return router.InternalError(err)
	}

	// Default to customer role etc - admins will have to promote afterwards
	params.Set("role", fmt.Sprintf("%d", users.RoleCustomer))
	params.Set("status", fmt.Sprintf("%d", status.Published))

	id, err := users.Create(params.Map())
	if err != nil {
		return err
	}

	// Log creation
	context.Logf("#info Created user id,%d", id)

	// Redirect to the new user
	user, err := users.Find(id)
	if err != nil {
		return router.InternalError(err)
	}

	// Save the details in a secure cookie
	session, err := auth.Session(context, context.Request())
	if err != nil {
		return router.InternalError(err)
	}

	context.Logf("#info CREATE for user: %d", user.Id)
	session.Set(auth.SessionUserKey, fmt.Sprintf("%d", user.Id))
	session.Save(context)

	// Send them to their user profile page
	return router.Redirect(context, user.URLShow())
}
Example #12
0
// Default file handler, used in development - in production serve with nginx
func serveFile(context router.Context) error {
	// Assuming we're running from the root of the website
	localPath := "./public" + path.Clean(context.Path())

	if _, err := os.Stat(localPath); err != nil {
		// If file not found return error
		if os.IsNotExist(err) {
			return router.NotFoundError(err)
		}

		// For other errors return not authorised
		return router.NotAuthorizedError(err)
	}

	// If the file exists and we can access it, serve it
	http.ServeFile(context, context.Request(), localPath)
	return nil
}
Example #13
0
// Handle serving assets in dev (if we can) - return true on success
func serveAsset(context router.Context) error {
	p := path.Clean(context.Path())

	// It must be under /assets, or we don't serve
	if !strings.HasPrefix(p, "/assets/") {
		return router.NotFoundError(nil)
	}

	// Try to find an asset in our list
	f := appAssets.File(path.Base(p))
	if f == nil {
		return router.NotFoundError(nil)
	}

	localPath := "./" + f.LocalPath()
	http.ServeFile(context, context.Request(), localPath)
	return nil
}
Example #14
0
// HandleLogin handles a post to /users/login
func HandleLogin(context router.Context) error {

	// Check we're not already logged in, if so redirect

	// Get the user details from the database
	params, err := context.Params()
	if err != nil {
		return router.NotFoundError(err)
	}

	// Need something neater than this - how best to do it?
	q := users.Where("email=?", params.Get("email"))
	user, err := users.First(q)
	if err != nil {
		context.Logf("#error Login failed for user no such user : %s %s", params.Get("email"), err)
		return router.Redirect(context, "/users/login?error=failed_email")

	}

	err = auth.CheckPassword(params.Get("password"), user.EncryptedPassword)

	if err != nil {
		context.Logf("#error Login failed for user : %s %s", params.Get("email"), err)
		return router.Redirect(context, "/users/login?error=failed_password")
	}

	// Now save the user details in a secure cookie, so that we remember the next request
	session, err := auth.Session(context, context.Request())
	if err != nil {
		context.Logf("#error problem retrieving session")

	}
	context.Logf("#info Login success for user: %d %s", user.Id, user.Email)
	session.Set(auth.SessionUserKey, fmt.Sprintf("%d", user.Id))
	session.Save(context)

	// Redirect to whatever page the user tried to visit before (if any)
	// For now send them to root
	return router.Redirect(context, "/")

}
Example #15
0
// HandleCode displays a list of stories linking to repos (github etc) using gravity to order them
// responds to GET /stories/code
func HandleCode(context router.Context) error {

	// Build a query
	q := stories.Query().Where("points > -6").Order("rank desc, points desc, id desc").Limit(listLimit)

	// Restrict to stories with have a url starting with github.com or bitbucket.org
	// other code repos can be added later
	q.Where("url ILIKE 'https://github.com%'").OrWhere("url ILIKE 'https://bitbucket.org'")

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	setStoriesMetadata(view, context.Request())
	view.AddKey("page", page)
	view.AddKey("stories", results)
	//	view.AddKey("tags", []string{"web", "mobile", "data", "email", "crypto", "data", "graphics", "ui", "security"})

	// TODO: remove these calls and put in a filter
	// - given it is not too expensive, we could just generate tokens on every request

	view.Template("stories/views/index.html.got")

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Example #16
0
// CurrentUser returns the saved user (or an empty anon user) for the current session cookie
// Strictly speaking this should be authenticate.User
func CurrentUser(context router.Context) *users.User {

	// First check if the user has already been set on context, if so return it
	if context.Get("current_user") != nil {
		return context.Get("current_user").(*users.User)
	}

	// Start with an anon user by default (role 0, id 0)
	user := &users.User{}

	// Build the session from the secure cookie, or create a new one
	session, err := auth.Session(context.Writer(), context.Request())
	if err != nil {
		context.Logf("#error problem retrieving session")
		return user
	}

	// Fetch the current user record if we have one recorded in the session
	var id int64
	ids := session.Get(auth.SessionUserKey)
	if len(ids) > 0 {
		id, err = strconv.ParseInt(ids, 10, 64)
		if err != nil {
			context.Logf("#error Error decoding session user key:%s\n", err)
			return user
		}
	}

	if id != 0 {
		u, err := users.Find(id)
		if err != nil {
			context.Logf("#info User not found from session id:%d\n", id)
			return user
		}
		user = u
	}

	return user
}
Example #17
0
// HandleCode displays a list of stories linking to repos (github etc) using gravity to order them
// responds to GET /stories/code
func HandleCode(context router.Context) error {

	// Build a query
	q := stories.Query().Where("points > -6").Order("rank desc, points desc, id desc").Limit(listLimit)

	// Restrict to stories with have a url starting with github.com or bitbucket.org
	// other code repos can be added later
	q.Where("url ILIKE 'https://github.com%'").OrWhere("url ILIKE 'https://bitbucket.org'")

	// Set the offset in pages if we have one
	page := int(context.ParamInt("page"))
	if page > 0 {
		q.Offset(listLimit * page)
	}

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		return router.InternalError(err)
	}

	// Render the template
	view := view.New(context)
	setStoriesMetadata(view, context.Request())
	view.AddKey("page", page)
	view.AddKey("stories", results)

	view.Template("stories/views/index.html.got")

	if context.Param("format") == ".xml" {
		view.Layout("")
		view.Template("stories/views/index.xml.got")
	}

	return view.Render()

}
Example #18
0
// HandleLogin handles a post to /users/login
func HandleLogin(context router.Context) error {

	params, err := context.Params()
	if err != nil {
		return router.NotFoundError(err)
	}

	// Check users against their username - we could also check against the email later?
	name := params.Get("name")
	q := users.Where("name=?", name)
	user, err := users.FindFirst(q)
	if err != nil {
		context.Logf("#error Login failed for user : %s %s", name, err)
		return router.Redirect(context, "/users/login?error=failed_name")
	}

	err = auth.CheckPassword(params.Get("password"), user.Password)
	if err != nil {
		context.Logf("#error Login failed for user : %s %s", name, err)
		return router.Redirect(context, "/users/login?error=failed_password")
	}

	// Save the details in a secure cookie
	session, err := auth.Session(context, context.Request())
	if err != nil {
		return router.InternalError(err)
	}

	context.Logf("#info LOGIN for user: %d", user.Id)
	session.Set(auth.SessionUserKey, fmt.Sprintf("%d", user.Id))
	session.Save(context)

	// Send them to their user profile page
	return router.Redirect(context, user.URLShow())

}
Example #19
0
// HandleSetup responds to a POST at /fragmenta/setup
// by creating our first user and page
func HandleSetup(context router.Context) error {

	// If we have pages or users already, do not proceed
	if !missingUsersAndPages() {
		return router.NotAuthorizedError(nil)
	}

	// Take the details given and create the first user
	params := map[string]string{
		"email":    context.Param("email"),
		"password": context.Param("password"),
		"name":     nameFromEmail(context.Param("email")),
		"status":   "100",
		"role":     "100",
		"title":    "Administrator",
	}

	uid, err := users.Create(params)
	if err != nil {
		return router.InternalError(err)
	}
	context.Logf("#info Created user #%d", uid)
	user, err := users.Find(uid)
	if err != nil {
		return router.InternalError(err)
	}
	// Login this user automatically - save cookie
	session, err := auth.Session(context, context.Request())
	if err != nil {
		return router.InternalError(err)
	}
	context.Logf("#info Automatic login for first user: %d %s", user.Id, user.Email)
	session.Set(auth.SessionUserKey, fmt.Sprintf("%d", user.Id))
	session.Save(context)

	// Load our welcomepage template html
	// and put it into the text field of a new page with id 1

	welcomeText, err := ioutil.ReadFile("src/pages/views/welcome.html.got")
	if err != nil {
		return router.InternalError(err)
	}

	params = map[string]string{
		"status": "100",
		"name":   "Fragmenta",
		"url":    "/",
		"text":   string(welcomeText),
	}
	_, err = pages.Create(params)
	if err != nil {
		return router.InternalError(err)
	}

	// Create another couple of simple pages as examples (about and privacy)
	params = map[string]string{
		"status": "100",
		"name":   "About Us",
		"url":    "/about",
		"text":   "<section class=\"narrow\"><h1>About us</h1><p>About us</p></section>",
	}
	_, err = pages.Create(params)
	if err != nil {
		return router.InternalError(err)
	}
	params = map[string]string{
		"status": "100",
		"name":   "Privacy Policy",
		"url":    "/privacy",
		"text":   "<section class=\"narrow\"><h1>Privacy Policy</h1><p>We respect your privacy.</p></section>",
	}
	_, err = pages.Create(params)
	if err != nil {
		return router.InternalError(err)
	}

	// Redirect back to the newly populated home page
	return router.Redirect(context, "/")
}