Skip to content

Commit d13f540

Browse files
Add Custom Authentication Endpoints (#3)
[WIP]: Add first end point - signup. - The endpoint is user/signup, which is equivalent to user/sign_up but returns json response. - This can't be under the `/api/v1/` route, AFAIK all endpoints under it require being authenticated. - For sake of demonstration, not done yet. - Server configuration is needed. - Rename sign up endpoint. Group kitspace endpoints under a kitspace route. - All custom endpoints are now under `/user/kitspace`. - The `api` suffix doesn't add a real value. Add `user/kitspace/sign_in` endpoint. - The build process is too slow so it hasn't been tested yet till we have other endpoints to bulk test them. Add `user/kitspace/forgot_password` endpoint. - The build process is too slow so it hasn't been tested yet till we have other endpoints to bulk test them. Move The TODO to the top level. Add `user/kitspace/reset_password` endpoint. - The build process is too slow so it hasn't been tested yet till we have other endpoints to bulk test them. Add `user/kitspace/activate` endpoint. - The build process is too slow so it hasn't been tested yet till we have other endpoints to bulk test them. First api test.[broken] - The sign up endpoint is working fine, assuming the part blocked by email is also fine. - The sign in is broken, it authenticate correctly yet redirect to the Gitea homepage as if the user signed in from the Gitea login page. A possible solution would be to just generate the token and send it to the frontend, let the frontend handle the reset of the sign in flow. - Both reset password and forgot password are completely broken. They sign in the user. Probably this something to do with context. - How context will be passed from frontend to the backend. Remove forgot_password, reset_password, and activate endpoints. - We agree these aren't necessary for now. Remove email conformation form the sign up endpoint. - Now the api respond with errors or the success case,in which it sends a json object with {"IsRegisteredSuccessfully": true} Add Success message to sign-in. Add Success message to sign-in. Add docs to Sign-up endpoint. Add docs to Sign-in endpoint. We are not handling custom locales for now, so remove it to reduce the clutter. Update docs to generate swagger. Re-enable email confirmation. Remove the docs TODO. Encode SignUp responses as proper json. Encode SignUp responses as proper json. Add request headers to `user/kitspace/`. - This modification is required to enable cors from frontend. Add `Access-Control-Allow-Origin` to the header. - Add `KitspaceAllowDomain` CORS option. - This is the same approach used by `routers/repo/http.go`. Add `Access-Control-Allow-Origin` to the header. - Add `KitspaceAllowDomain` CORS option. - This is the same approach used by `routers/repo/http.go`. Add Allow headers. - probably won't make a difference. Expose the the Access-Control headers. Remove `Access-Control-Allow-Headers`. Modify the mock response from signup form. Encode SingIn responses as proper json. Revert CORS handlers to the original state. Revert CORS handlers to the original state. Add sign out endpoint Add `reflex` to dependencies. - reflex [https://github.com/cespare/reflex] will enable hot-reload on modification. Use `handleSignInFull` directly. Replace the sing out response message with redirect. fixup! Replace the sing out response message with redirect. - Delegate the redirect to frontend. Drop `SignOut` endpoint. - See kitspace/kitspace-v2@b5f04da fixup! Replace the sing out response message with redirect. Split docker build process into separate steps. - Use a separate steps for frontend and backend that will enable caching the frontend build which takes the bulk of the build time. fixup! Split docker build process into separate steps. Clean the email activation routine. - Remove the unnecessary condition. - The same response is returned now whether mailing service is configured(production) or not(development). Revert "Add `reflex` to dependencies. - reflex [https://github.com/cespare/reflex] will enable hot-reload on modification." This reverts commit 1c9ed16 Restore the original Dockerfile. Fix formatting.
1 parent 50e16d6 commit d13f540

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed

routers/user/kitspace_auth.go

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package user
2+
3+
import (
4+
"net/http"
5+
6+
"code.gitea.io/gitea/models"
7+
"code.gitea.io/gitea/modules/context"
8+
auth "code.gitea.io/gitea/modules/forms"
9+
"code.gitea.io/gitea/modules/log"
10+
"code.gitea.io/gitea/modules/password"
11+
"code.gitea.io/gitea/modules/setting"
12+
"code.gitea.io/gitea/modules/timeutil"
13+
"code.gitea.io/gitea/modules/web"
14+
"code.gitea.io/gitea/services/mailer"
15+
)
16+
17+
// KitspaceSignUp custom sign-up compatible with Kitspace architecture
18+
func KitspaceSignUp(ctx *context.Context) {
19+
// swagger:operation POST /user/kitspace/sign_up
20+
// ---
21+
// summary: Create a user
22+
// consumes:
23+
// - application/json
24+
// produces:
25+
// - application/json
26+
// parameters:
27+
// - name: body
28+
// in: body
29+
// schema:
30+
// "$ref": "#/definitions/RegisterForm"
31+
// responses:
32+
// "201":
33+
// "$ref": "#/responses/User"
34+
// "400":
35+
// "$ref": "#/responses/error"
36+
// "409":
37+
// "$ref": "#/response/error
38+
// "422":
39+
// "$ref": "#/responses/validationError"
40+
response := make(map[string]string)
41+
form := web.GetForm(ctx).(*auth.RegisterForm)
42+
43+
if len(form.Password) < setting.MinPasswordLength {
44+
response["error"] = "UnprocessableEntity"
45+
response["message"] = "Password is too short."
46+
47+
ctx.JSON(http.StatusUnprocessableEntity, response)
48+
return
49+
}
50+
51+
if !password.IsComplexEnough(form.Password) {
52+
response["error"] = "UnprocessableEntity"
53+
response["message"] = "Password isn't complex enough."
54+
55+
ctx.JSON(http.StatusUnprocessableEntity, response)
56+
return
57+
}
58+
59+
u := &models.User{
60+
Name: form.UserName,
61+
Email: form.Email,
62+
Passwd: form.Password,
63+
IsActive: !setting.Service.RegisterEmailConfirm,
64+
}
65+
66+
if err := models.CreateUser(u); err != nil {
67+
switch {
68+
case models.IsErrUserAlreadyExist(err):
69+
response["error"] = "Conflict"
70+
response["message"] = "User already exists."
71+
72+
ctx.JSON(http.StatusConflict, response)
73+
case models.IsErrEmailAlreadyUsed(err):
74+
response["error"] = "Conflict"
75+
response["message"] = "Email is already used."
76+
77+
ctx.JSON(http.StatusConflict, response)
78+
case models.IsErrNameReserved(err):
79+
response["error"] = "Conflict"
80+
response["message"] = "Name is reserved."
81+
82+
ctx.JSON(http.StatusConflict, response)
83+
case models.IsErrNamePatternNotAllowed(err):
84+
response["error"] = "UnprocessableEntity"
85+
response["message"] = "This name pattern isn't allowed."
86+
87+
ctx.JSON(http.StatusUnprocessableEntity, response)
88+
default:
89+
ctx.ServerError("Signup", err)
90+
}
91+
return
92+
} else {
93+
log.Trace("Account created: %s", u.Name)
94+
}
95+
96+
// Send confirmation email
97+
// The mailing service works only in production during development no mails are sent
98+
if setting.Service.RegisterEmailConfirm && u.ID > 1 {
99+
mailer.SendActivateAccountMail(ctx.Locale, u)
100+
101+
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
102+
log.Error("Set cache(MailResendLimit) fail: %v", err)
103+
}
104+
}
105+
106+
// Return the success response with user details
107+
response["email"] = u.Email
108+
response["ActiveCodeLives"] = timeutil.MinutesToFriendly(
109+
setting.Service.ActiveCodeLives,
110+
ctx.Locale.Language(),
111+
)
112+
113+
ctx.JSON(http.StatusCreated, response)
114+
return
115+
}
116+
117+
// KitspaceSignIn custom sign-in compatible with Kitspace architecture
118+
func KitspaceSignIn(ctx *context.Context) {
119+
// swagger:operation POST /user/kitspace/sign_in
120+
// ---
121+
// summary: login a user
122+
// consumes:
123+
// - application/json
124+
// produces:
125+
// - application/json
126+
// parameters:
127+
// - name: body
128+
// in: body
129+
// schema:
130+
// "$ref": "#/definitions/SignInForm"
131+
// responses:
132+
// "200":
133+
// "$ref": "success"
134+
// "404":
135+
// "$ref": "#/response/forbidden"
136+
// "404":
137+
// "$ref": "#/responses/notFound"
138+
// "409":
139+
// "$ref": "#/response/error
140+
// "422":
141+
// "$ref": "#/responses/validationError"
142+
143+
form := web.GetForm(ctx).(*auth.SignInForm)
144+
u, err := models.UserSignIn(form.UserName, form.Password)
145+
146+
response := make(map[string]string)
147+
if err != nil {
148+
switch {
149+
case models.IsErrUserNotExist(err):
150+
response["error"] = "Not Found"
151+
response["message"] = "Wrong username or password."
152+
153+
ctx.JSON(http.StatusNotFound, response)
154+
log.Info("Failed authentication attempt for %s from %s", form.UserName, ctx.RemoteAddr())
155+
case models.IsErrEmailAlreadyUsed(err):
156+
response["error"] = "Conflict"
157+
response["message"] = "This email has already been used."
158+
159+
ctx.JSON(http.StatusConflict, response)
160+
log.Info("Failed authentication attempt for %s from %s", form.UserName, ctx.RemoteAddr())
161+
case models.IsErrUserProhibitLogin(err):
162+
response["error"] = "Prohibited"
163+
response["message"] = "Prohibited login."
164+
165+
ctx.JSON(http.StatusForbidden, response)
166+
log.Info("Failed authentication attempt for %s from %s", form.UserName, ctx.RemoteAddr())
167+
case models.IsErrUserInactive(err):
168+
if setting.Service.RegisterEmailConfirm {
169+
response["error"] = "ActivationRequired"
170+
response["message"] = "Activate your account."
171+
172+
ctx.JSON(http.StatusOK, response)
173+
} else {
174+
response["error"] = "Prohibited"
175+
response["message"] = "Prohibited login"
176+
177+
ctx.JSON(http.StatusForbidden, response)
178+
log.Info("Failed authentication attempt for %s from %s", form.UserName, ctx.RemoteAddr())
179+
}
180+
default:
181+
ctx.ServerError("KitspaceSignIn", err)
182+
}
183+
return
184+
}
185+
handleSignInFull(ctx, u, form.Remember, false)
186+
187+
response["LoggedInSuccessfully"] = u.Name
188+
ctx.JSON(http.StatusOK, response)
189+
}

routers/web/web.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,11 @@ func RegisterRoutes(m *web.Route) {
262262

263263
// ***** START: User *****
264264
m.Group("/user", func() {
265+
m.Group("/kitspace", func() {
266+
m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.KitspaceSignUp)
267+
m.Post("/sign_in", bindIgnErr(auth.SignInForm{}), user.KitspaceSignIn)
268+
})
269+
265270
m.Get("/login", user.SignIn)
266271
m.Post("/login", bindIgnErr(forms.SignInForm{}), user.SignInPost)
267272
m.Group("", func() {

0 commit comments

Comments
 (0)