controllers

package
v0.0.0-...-c45cdb4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2020 License: MPL-2.0 Imports: 17 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var CreateForm = func(w http.ResponseWriter, r *http.Request) {

	rno := GetRollNo(w, r, true)
	if rno == "" {
		return
	}

	form := &models.Form{}
	err := json.NewDecoder(r.Body).Decode(form)
	if err != nil {
		log.Println(err)
		u.Respond(w, u.Message(false, err.Error()), 400)
		return
	}

	if len(form.Pages) == 0 {
		u.Respond(w, u.Message(false, "No Pages"), 400)
		return
	}

	assignUids(form)
	form.Name = form.Pages[0].Title
	responseToken := u.RandSeq(50)
	form.ResponseToken = responseToken
	collection := u.Collection(r.Context(), "forms")

	// Update or create new
	var id interface{}
	if r.Method == "PUT" {
		cid := mux.Vars(r)["id"]
		objID, _ := primitive.ObjectIDFromHex(cid)
		filt := bson.M{"$and": bson.A{
			bson.M{"_id": objID},
			bson.M{"creator": rno}}}

		var res *mongo.UpdateResult
		res, err = collection.ReplaceOne(r.Context(), filt, form)
		id = cid

		if res.MatchedCount == 0 {
			u.Respond(w, u.Message(false, "Not Found"), 404)
			return
		}
	} else {
		form.Creator = rno
		form.Timestamp = time.Now()
		var res *mongo.InsertOneResult
		res, err = collection.InsertOne(r.Context(), form)
		id = res.InsertedID
	}

	if err != nil {
		log.Println(err)
		u.Respond(w, u.Message(false, err.Error()), 400)
		return
	}

	log.Println(rno, ": new form", id)

	u.Respond(w, map[string]interface{}{"id": id, "token": responseToken}, 200)
}

CreateForm : API handler for POST-ing new forms

View Source
var CreateResponse = func(w http.ResponseWriter, r *http.Request) {
	formid := mux.Vars(r)["formid"]

	rno := GetRollNo(w, r, false)
	collection := u.Collection(r.Context(), "forms")
	objID, _ := primitive.ObjectIDFromHex(formid)
	filt := bson.M{"_id": objID}
	form := &models.Form{}
	collection.FindOne(r.Context(), filt).Decode(form)
	if form.RequireLogin && rno == "" {
		u.Respond(w, u.Message(false, "Not Found"), 401)
		return
	}

	year2000 := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
	if form.IsClosed || time.Now().UTC().After(form.CloseOn) && year2000.Before(form.CloseOn) {
		u.Respond(w, u.Message(false, "Form Closed"), 404)
		return
	}

	response := &models.FormResponse{}
	err := json.NewDecoder(r.Body).Decode(response)
	if err != nil {
		u.Respond(w, u.Message(false, err.Error()), 400)
		return
	}

	response.FormID = formid
	response.Timestamp = time.Now()
	response.Responses["timestamp"] = response.Timestamp
	if form.CollectEmail {
		response.Filler = rno
		response.Responses["filler"] = response.Filler
	}

	if form.SingleResponse && HasFilledAnon(r.Context(), formid, rno) {
		u.Respond(w, u.Message(false, "User has already filled this form"), 403)
		return
	}

	if form.RequireLogin {
		anonResponse := &models.FormAnonResponder{}
		anonResponse.Filler = rno
		anonResponse.FormID = formid

		collection = u.Collection(r.Context(), "filler")
		collection.InsertOne(r.Context(), anonResponse)
	}

	collection = u.Collection(r.Context(), "responses")
	cur, _ := collection.InsertOne(r.Context(), response)
	id := cur.InsertedID

	log.Println(rno, ": new response for form", formid)

	u.Respond(w, map[string]interface{}{"id": id}, 200)
}

CreateResponse : API handler for POST-ing new response

View Source
var DeleteForm = func(w http.ResponseWriter, r *http.Request) {
	cid := mux.Vars(r)["id"]

	rno := GetRollNo(w, r, false)

	form := &models.Form{}
	collection := u.Collection(r.Context(), "forms")
	objID, _ := primitive.ObjectIDFromHex(cid)
	err := collection.FindOne(r.Context(), bson.M{"_id": objID}).Decode(&form)
	if err != nil {
		u.Respond(w, u.Message(false, err.Error()), 400)
		return
	}

	if rno != form.Creator {
		u.Respond(w, u.Message(false, "Only form creator can delete form. Unauthorized access"), 403)
		return
	}

	_, err = collection.DeleteOne(r.Context(), bson.M{"_id": objID})
	if err != nil {
		log.Printf("remove fail %v\n", err)
	}

	_, err = u.Collection(r.Context(), "responses").DeleteMany(r.Context(), bson.M{"formid": cid})
	if err != nil {
		log.Printf("remove fail %v\n", err)
	}
	log.Println("Form", cid, "and its responses deleted")
	u.Respond(w, u.Message(false, "Form deleted"), 200)
}

DeleteForm : API handler for deleting a form

View Source
var GetAllForms = func(w http.ResponseWriter, r *http.Request) {

	rno := GetRollNo(w, r, false)

	// To extrach data out of collection.Find()
	type formDB struct {
		ID    primitive.ObjectID `bson:"_id"`
		Name  string             `bson:"name"`
		Token string             `bson:"responsetoken"`
	}

	// To send data to frontend
	type formDetails struct {
		ID    string
		Name  string
		Token string
	}

	var forms []formDetails

	collection := u.Collection(r.Context(), "forms")
	count, err := collection.CountDocuments(r.Context(), bson.M{"creator": rno})
	if count == 0 {
		u.Respond(w, make([]formDetails, 0), 200)
		return
	}

	// To Set which fields are required in the output
	type fields struct {
		ID    int `bson:"_id"`
		Name  int `bson:"name"`
		Token int `bson:"responsetoken"`
	}
	projection := fields{ID: 1, Name: 1, Token: 1}
	opt := &options.FindOptions{}
	opt.SetSort(bson.D{{"timestamp", -1}})
	opt.SetProjection(projection)

	values, err := collection.Find(r.Context(), bson.M{"creator": rno}, opt)
	if err != nil {
		u.Respond(w, u.Message(false, err.Error()), 400)
		return
	}

	for values.Next(context.TODO()) {
		var elem formDB
		err := values.Decode(&elem)
		if err != nil {
			log.Println(err)
		}
		forms = append(forms, formDetails{ID: (&elem).ID.Hex(), Name: (&elem).Name, Token: (&elem).Token})
	}
	log.Println("all forms created by", rno, "sent")
	u.Respond(w, forms, 200)
}

GetAllForms : API handler for getting all forms of the logged in user

View Source
var GetForm = func(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]

	form := &models.Form{}

	collection := u.Collection(r.Context(), "forms")
	objID, _ := primitive.ObjectIDFromHex(id)
	err := collection.FindOne(r.Context(), bson.M{"_id": objID}).Decode(&form)
	if err != nil {
		u.Respond(w, u.Message(false, err.Error()), 400)
		return
	}

	rno := GetRollNo(w, r, false)
	if rno != "" {
		form.CanEdit = rno == form.Creator
	}

	if !form.CanEdit && form.SingleResponse && HasFilledAnon(r.Context(), id, rno) {
		u.Respond(w, u.Message(false, "User has already filled this form"), 403)
		return
	}

	if form.RequireLogin && rno == "" {
		u.Respond(w, u.Message(false, "Unauthorized: Please login to continue"), 401)
		return
	}

	year2000 := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
	if time.Now().UTC().After(form.CloseOn) && year2000.Before(form.CloseOn) {
		form.IsClosed = true
	}

	u.Respond(w, form, 200)
}

GetForm : API handler for getting one form

View Source
var GetResponses = func(w http.ResponseWriter, r *http.Request) {

	rno := GetRollNo(w, r, true)
	if rno == "" {
		return
	}

	id := mux.Vars(r)["formid"]
	data := strings.Split(id, "-")
	formid := data[0]
	token := data[1]

	form := &models.Form{}
	collection := u.Collection(r.Context(), "forms")
	objID, _ := primitive.ObjectIDFromHex(formid)
	filt := bson.M{"$and": bson.A{
		bson.M{"_id": objID},
		bson.M{"responsetoken": token}}}
	err := collection.FindOne(r.Context(), filt).Decode(&form)
	if err != nil {
		u.Respond(w, u.Message(false, err.Error()), 400)
		return
	}

	responses := []*models.FormResponse{}
	collection = u.Collection(r.Context(), "responses")
	cur, err := collection.Find(r.Context(), bson.M{"formid": formid})
	if err != nil {
		u.Respond(w, u.Message(false, err.Error()), 400)
		return
	}

	for cur.Next(context.TODO()) {
		var elem models.FormResponse
		err := cur.Decode(&elem)
		if err != nil {
			log.Println(err)
		}
		responses = append(responses, &elem)
	}

	rq := &ResponsesRequest{}
	json.NewDecoder(r.Body).Decode(rq)
	if rq.Type == "array" {
		u.Respond(w, arrayResponse(form, responses), 200)
		return
	}

	u.Respond(w, responses, 200)
}

GetResponses : API handler for getting JSON responses (for CSV)

View Source
var GetRollNo = func(w http.ResponseWriter, r *http.Request, throw bool) string {
	c, err := r.Cookie("token")
	if err != nil {
		if err == http.ErrNoCookie {

			if throw {
				w.WriteHeader(http.StatusUnauthorized)
			}
			return ""
		}

		if throw {
			w.WriteHeader(http.StatusBadRequest)
		}
		return ""
	}

	tknStr := c.Value

	claims := &Claims{}

	tkn, err := jwt.ParseWithClaims(tknStr, claims, func(token *jwt.Token) (interface{}, error) {
		return jwtKey, nil
	})

	if !tkn.Valid {
		if throw {
			w.WriteHeader(http.StatusUnauthorized)
		}
		return ""
	}

	if err != nil {
		if err == jwt.ErrSignatureInvalid {
			if throw {
				w.WriteHeader(http.StatusUnauthorized)
			}
			return ""
		}
		if throw {
			w.WriteHeader(http.StatusBadRequest)
		}
		return ""
	}

	return claims.RollNumber
}

GetRollNo : helper function to get claimed roll number from JWT

View Source
var Login = func(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {

		rno := GetRollNo(w, r, true)
		if rno == "" {
			return
		}

		collection := u.Collection(r.Context(), "users")
		user := &ProfileResponse{}
		err := collection.FindOne(r.Context(), bson.M{"rollnumber": rno}).Decode(user)
		if err != nil {
			u.Respond(w, u.Message(false, err.Error()), 500)
			return
		}

		u.Respond(w, user, 200)
		return
	}

	code := &AuthCode{}
	err := json.NewDecoder(r.Body).Decode(code)
	if err != nil {
		u.Respond(w, u.Message(false, err.Error()), 400)
		return
	}

	url := os.Getenv("OAUTH_URL")
	authToken := os.Getenv("AUTH_TOKEN")
	authTemplate := "code=%s&redirect_uri=%s&grant_type=authorization_code"

	var jsonStr = []byte(fmt.Sprintf(authTemplate, code.Code, code.RedirectURI))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Set("Authorization", fmt.Sprintf("Basic %s", authToken))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
		u.Respond(w, u.Message(false, err.Error()), 401)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		u.Respond(w, u.Message(false, "Error"), resp.StatusCode)
		return
	}

	accessTokenResponse := &AccessTokenResponse{}
	err = json.NewDecoder(resp.Body).Decode(accessTokenResponse)

	if err != nil {
		log.Println(err)
		u.Respond(w, u.Message(false, err.Error()), 500)
		return
	}

	profileURL := os.Getenv("OAUTH_PROFILE")

	req, err = http.NewRequest("GET", profileURL, nil)
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessTokenResponse.AccessToken))
	resp, err = client.Do(req)
	if err != nil {
		log.Println(err)
		u.Respond(w, u.Message(false, err.Error()), 500)
		return
	}
	defer resp.Body.Close()

	profileResponse := &ProfileResponse{}
	err = json.NewDecoder(resp.Body).Decode(profileResponse)
	if err != nil {
		log.Println(err)
		u.Respond(w, u.Message(false, err.Error()), 500)
		return
	}

	rno := profileResponse.RollNumber
	collection := u.Collection(r.Context(), "users")

	// Allow creation
	var ropts options.ReplaceOptions
	ropts.SetUpsert(true)

	_, err = collection.ReplaceOne(
		r.Context(), bson.M{"rollnumber": rno}, profileResponse, &ropts)

	SetCookie(w, rno)

	u.Respond(w, profileResponse, 200)
}

Login : API handler for logging in with SSO auth code

View Source
var Logout = func(w http.ResponseWriter, r *http.Request) {

	http.SetCookie(w, &http.Cookie{
		Name:    "token",
		Value:   "",
		Expires: time.Unix(0, 0),
	})

	u.Respond(w, "", 204)
}

Logout : API handler for logging out

View Source
var SetCookie = func(w http.ResponseWriter, rno string) {

	expirationTime := time.Now().Add(24 * time.Hour)

	claims := &Claims{
		RollNumber: rno,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expirationTime.Unix(),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	tokenString, err := token.SignedString(jwtKey)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	http.SetCookie(w, &http.Cookie{
		Name:    "token",
		Value:   tokenString,
		Expires: expirationTime,
	})
}

SetCookie : helper function to set JWT cookie

Functions

func HasFilledAnon

func HasFilledAnon(ctx context.Context, formid string, filler string) bool

HasFilledAnon returns true if the person has already filled this form

Types

type AccessTokenResponse

type AccessTokenResponse struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
	Scope        string `json:"scope"`
}

AccessTokenResponse : access token received from SSO

type AuthCode

type AuthCode struct {
	Code        string `json:"code"`
	RedirectURI string `json:"redirect_uri"`
}

AuthCode : code and redirect uri to POST

type Claims

type Claims struct {
	RollNumber string `json:"roll_number"`
	jwt.StandardClaims
}

Claims : JWT claims stored on client side

type ProfileResponse

type ProfileResponse struct {
	ID             int    `json:"id"`
	FirstName      string `json:"first_name"`
	LastName       string `json:"last_name"`
	RollNumber     string `json:"roll_number"`
	ProfilePicture string `json:"profile_picture"`
	Email          string `json:"email"`
}

ProfileResponse : Profile as received from SSO

type ResponsesRequest

type ResponsesRequest struct {
	Type string `json:"type"`
}

ResponsesRequest : helper for post processing

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL