From 5d9b17542f18a4c87a2e1577498d8ffac1e868c5 Mon Sep 17 00:00:00 2001 From: Yaodong Yu <2814461814@qq.com> Date: Tue, 17 Jan 2023 22:57:05 +0800 Subject: [PATCH] feat: end-user log out (#1356) --- controllers/account.go | 61 ++++- controllers/application.go | 2 +- controllers/ldap.go | 10 +- controllers/token.go | 23 -- controllers/verification.go | 16 +- i18n/locales/de/data.json | 11 +- i18n/locales/en/data.json | 11 +- i18n/locales/es/data.json | 11 +- i18n/locales/fr/data.json | 11 +- i18n/locales/ja/data.json | 11 +- i18n/locales/ko/data.json | 11 +- i18n/locales/ru/data.json | 11 +- i18n/locales/zh/data.json | 11 +- object/oidc_discovery.go | 2 + object/session.go | 6 +- object/token.go | 24 +- routers/router.go | 2 - swagger/swagger.json | 463 ++++++++++++++++++++++++++++++++---- swagger/swagger.yml | 308 +++++++++++++++++++++--- util/time_test.go | 12 +- 20 files changed, 816 insertions(+), 201 deletions(-) diff --git a/controllers/account.go b/controllers/account.go index 9d08cd7a..c4898de0 100644 --- a/controllers/account.go +++ b/controllers/account.go @@ -17,6 +17,7 @@ package controllers import ( "encoding/json" "fmt" + "net/http" "strconv" "strings" @@ -238,21 +239,67 @@ func (c *ApiController) Signup() { // @Title Logout // @Tag Login API // @Description logout the current user +// @Param id_token_hint query string false "id_token_hint" +// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri" +// @Param state query string false "state" // @Success 200 {object} controllers.Response The Response object // @router /logout [get,post] func (c *ApiController) Logout() { user := c.GetSessionUsername() - object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID()) - util.LogInfo(c.Ctx, "API: [%s] logged out", user) - application := c.GetSessionApplication() - c.ClearUserSession() + // https://openid.net/specs/openid-connect-rpinitiated-1_0-final.html + accessToken := c.Input().Get("id_token_hint") + redirectUri := c.Input().Get("post_logout_redirect_uri") + state := c.Input().Get("state") - if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" { - c.ResponseOk(user) + if accessToken == "" && redirectUri == "" { + c.ClearUserSession() + object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID()) + util.LogInfo(c.Ctx, "API: [%s] logged out", user) + + application := c.GetSessionApplication() + if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" { + c.ResponseOk(user) + return + } + c.ResponseOk(user, application.HomepageUrl) return + } else { + if redirectUri == "" { + c.ResponseError(c.T("general:Missing parameter") + ": post_logout_redirect_uri") + return + } + if accessToken == "" { + c.ResponseError(c.T("general:Missing parameter") + ": id_token_hint") + return + } + + affected, application, token := object.ExpireTokenByAccessToken(accessToken) + if !affected { + c.ResponseError(c.T("token:Token not found, invalid accessToken")) + return + } + + if application == nil { + c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist")), token.Application) + return + } + + if application.IsRedirectUriValid(redirectUri) { + if user == "" { + user = util.GetId(token.Organization, token.User) + } + + c.ClearUserSession() + object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID()) + util.LogInfo(c.Ctx, "API: [%s] logged out", user) + + c.Ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?state=%s", strings.TrimRight(redirectUri, "/"), state)) + } else { + c.ResponseError(fmt.Sprintf(c.T("token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri)) + return + } } - c.ResponseOk(user, application.HomepageUrl) } // GetAccount diff --git a/controllers/application.go b/controllers/application.go index 8922f12a..e8ecb340 100644 --- a/controllers/application.go +++ b/controllers/application.go @@ -113,7 +113,7 @@ func (c *ApiController) GetOrganizationApplications() { sortOrder := c.Input().Get("sortOrder") if organization == "" { - c.ResponseError(c.T("application:Parameter organization is missing")) + c.ResponseError(c.T("general:Missing parameter") + ": organization") return } diff --git a/controllers/ldap.go b/controllers/ldap.go index fcf8ff61..d89b1ecb 100644 --- a/controllers/ldap.go +++ b/controllers/ldap.go @@ -52,7 +52,7 @@ func (c *ApiController) GetLdapUser() { ldapServer := LdapServer{} err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer) if err != nil || util.IsStrsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) { - c.ResponseError(c.T("ldap:Missing parameter")) + c.ResponseError(c.T("general:Missing parameter")) return } @@ -120,7 +120,7 @@ func (c *ApiController) GetLdap() { id := c.Input().Get("id") if util.IsStrsEmpty(id) { - c.ResponseError(c.T("ldap:Missing parameter")) + c.ResponseError(c.T("general:Missing parameter")) return } @@ -136,12 +136,12 @@ func (c *ApiController) AddLdap() { var ldap object.Ldap err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap) if err != nil { - c.ResponseError(c.T("ldap:Missing parameter")) + c.ResponseError(c.T("general:Missing parameter")) return } if util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) { - c.ResponseError(c.T("ldap:Missing parameter")) + c.ResponseError(c.T("general:Missing parameter")) return } @@ -171,7 +171,7 @@ func (c *ApiController) UpdateLdap() { var ldap object.Ldap err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap) if err != nil || util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) { - c.ResponseError(c.T("ldap:Missing parameter")) + c.ResponseError(c.T("general:Missing parameter")) return } diff --git a/controllers/token.go b/controllers/token.go index 265742a0..ae47f5f5 100644 --- a/controllers/token.go +++ b/controllers/token.go @@ -16,7 +16,6 @@ package controllers import ( "encoding/json" - "net/http" "github.com/beego/beego/utils/pagination" "github.com/casdoor/casdoor/object" @@ -247,28 +246,6 @@ func (c *ApiController) RefreshToken() { c.ServeJSON() } -// TokenLogout -// @Title TokenLogout -// @Tag Token API -// @Description delete token by AccessToken -// @Param id_token_hint query string true "id_token_hint" -// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri" -// @Param state query string true "state" -// @Success 200 {object} controllers.Response The Response object -// @router /login/oauth/logout [get] -func (c *ApiController) TokenLogout() { - token := c.Input().Get("id_token_hint") - flag, application := object.DeleteTokenByAccessToken(token) - redirectUri := c.Input().Get("post_logout_redirect_uri") - state := c.Input().Get("state") - if application != nil && application.IsRedirectUriValid(redirectUri) { - c.Ctx.Redirect(http.StatusFound, redirectUri+"?state="+state) - return - } - c.Data["json"] = wrapActionResponse(flag) - c.ServeJSON() -} - // IntrospectToken // @Title IntrospectToken // @Description The introspection endpoint is an OAuth 2.0 endpoint that takes a diff --git a/controllers/verification.go b/controllers/verification.go index db8e6bed..e985ac4e 100644 --- a/controllers/verification.go +++ b/controllers/verification.go @@ -51,15 +51,15 @@ func (c *ApiController) SendVerificationCode() { remoteAddr := util.GetIPFromRequest(c.Ctx.Request) if destType == "" { - c.ResponseError(c.T("verification:Missing parameter") + ": type.") + c.ResponseError(c.T("general:Missing parameter") + ": type.") return } if dest == "" { - c.ResponseError(c.T("verification:Missing parameter") + ": dest.") + c.ResponseError(c.T("general:Missing parameter") + ": dest.") return } if applicationId == "" { - c.ResponseError(c.T("verification:Missing parameter") + ": applicationId.") + c.ResponseError(c.T("general:Missing parameter") + ": applicationId.") return } if !strings.Contains(applicationId, "/") { @@ -67,7 +67,7 @@ func (c *ApiController) SendVerificationCode() { return } if checkType == "" { - c.ResponseError(c.T("verification:Missing parameter") + ": checkType.") + c.ResponseError(c.T("general:Missing parameter") + ": checkType.") return } @@ -75,7 +75,7 @@ func (c *ApiController) SendVerificationCode() { if captchaProvider != nil { if checkKey == "" { - c.ResponseError(c.T("verification:Missing parameter") + ": checkKey.") + c.ResponseError(c.T("general:Missing parameter") + ": checkKey.") return } isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId) @@ -170,7 +170,7 @@ func (c *ApiController) ResetEmailOrPhone() { dest := c.Ctx.Request.Form.Get("dest") code := c.Ctx.Request.Form.Get("code") if len(dest) == 0 || len(code) == 0 || len(destType) == 0 { - c.ResponseError(c.T("verification:Missing parameter")) + c.ResponseError(c.T("general:Missing parameter")) return } @@ -247,11 +247,11 @@ func (c *ApiController) VerifyCaptcha() { captchaToken := c.Ctx.Request.Form.Get("captchaToken") clientSecret := c.Ctx.Request.Form.Get("clientSecret") if captchaToken == "" { - c.ResponseError(c.T("verification:Missing parameter") + ": captchaToken.") + c.ResponseError(c.T("general:Missing parameter") + ": captchaToken.") return } if clientSecret == "" { - c.ResponseError(c.T("verification:Missing parameter") + ": clientSecret.") + c.ResponseError(c.T("general:Missing parameter") + ": clientSecret.") return } diff --git a/i18n/locales/de/data.json b/i18n/locales/de/data.json index 446bad33..7f77e4c2 100644 --- a/i18n/locales/de/data.json +++ b/i18n/locales/de/data.json @@ -8,9 +8,6 @@ "Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "The application does not allow to sign up new account" }, - "application": { - "Parameter organization is missing": "Parameter organization is missing" - }, "auth": { "%s No phone prefix": "%s No phone prefix", "Challenge method should be S256": "Challenge method should be S256", @@ -64,12 +61,12 @@ "unsupported password type: %s": "unsupported password type: %s" }, "general": { + "Missing parameter": "Missing parameter", "Please login first": "Please login first", "The user: %s doesn't exist": "The user: %s doesn't exist" }, "ldap": { - "Ldap server exist": "Ldap server exist", - "Missing parameter": "Missing parameter" + "Ldap server exist": "Ldap server exist" }, "link": { "Please link first": "Please link first", @@ -112,7 +109,8 @@ "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid client_id": "Invalid client_id", - "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" + "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list", + "Token not found, invalid accessToken": "Token not found, invalid accessToken" }, "user": { "Display name cannot be empty": "Display name cannot be empty", @@ -131,7 +129,6 @@ "Code has not been sent yet!": "Code has not been sent yet!", "Email is invalid": "Email is invalid", "Invalid captcha provider.": "Invalid captcha provider.", - "Missing parameter": "Missing parameter", "Organization does not exist": "Organization does not exist", "Phone number is invalid": "Phone number is invalid", "Turing test failed.": "Turing test failed.", diff --git a/i18n/locales/en/data.json b/i18n/locales/en/data.json index 446bad33..7f77e4c2 100644 --- a/i18n/locales/en/data.json +++ b/i18n/locales/en/data.json @@ -8,9 +8,6 @@ "Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "The application does not allow to sign up new account" }, - "application": { - "Parameter organization is missing": "Parameter organization is missing" - }, "auth": { "%s No phone prefix": "%s No phone prefix", "Challenge method should be S256": "Challenge method should be S256", @@ -64,12 +61,12 @@ "unsupported password type: %s": "unsupported password type: %s" }, "general": { + "Missing parameter": "Missing parameter", "Please login first": "Please login first", "The user: %s doesn't exist": "The user: %s doesn't exist" }, "ldap": { - "Ldap server exist": "Ldap server exist", - "Missing parameter": "Missing parameter" + "Ldap server exist": "Ldap server exist" }, "link": { "Please link first": "Please link first", @@ -112,7 +109,8 @@ "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid client_id": "Invalid client_id", - "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" + "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list", + "Token not found, invalid accessToken": "Token not found, invalid accessToken" }, "user": { "Display name cannot be empty": "Display name cannot be empty", @@ -131,7 +129,6 @@ "Code has not been sent yet!": "Code has not been sent yet!", "Email is invalid": "Email is invalid", "Invalid captcha provider.": "Invalid captcha provider.", - "Missing parameter": "Missing parameter", "Organization does not exist": "Organization does not exist", "Phone number is invalid": "Phone number is invalid", "Turing test failed.": "Turing test failed.", diff --git a/i18n/locales/es/data.json b/i18n/locales/es/data.json index 446bad33..7f77e4c2 100644 --- a/i18n/locales/es/data.json +++ b/i18n/locales/es/data.json @@ -8,9 +8,6 @@ "Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "The application does not allow to sign up new account" }, - "application": { - "Parameter organization is missing": "Parameter organization is missing" - }, "auth": { "%s No phone prefix": "%s No phone prefix", "Challenge method should be S256": "Challenge method should be S256", @@ -64,12 +61,12 @@ "unsupported password type: %s": "unsupported password type: %s" }, "general": { + "Missing parameter": "Missing parameter", "Please login first": "Please login first", "The user: %s doesn't exist": "The user: %s doesn't exist" }, "ldap": { - "Ldap server exist": "Ldap server exist", - "Missing parameter": "Missing parameter" + "Ldap server exist": "Ldap server exist" }, "link": { "Please link first": "Please link first", @@ -112,7 +109,8 @@ "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid client_id": "Invalid client_id", - "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" + "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list", + "Token not found, invalid accessToken": "Token not found, invalid accessToken" }, "user": { "Display name cannot be empty": "Display name cannot be empty", @@ -131,7 +129,6 @@ "Code has not been sent yet!": "Code has not been sent yet!", "Email is invalid": "Email is invalid", "Invalid captcha provider.": "Invalid captcha provider.", - "Missing parameter": "Missing parameter", "Organization does not exist": "Organization does not exist", "Phone number is invalid": "Phone number is invalid", "Turing test failed.": "Turing test failed.", diff --git a/i18n/locales/fr/data.json b/i18n/locales/fr/data.json index 446bad33..7f77e4c2 100644 --- a/i18n/locales/fr/data.json +++ b/i18n/locales/fr/data.json @@ -8,9 +8,6 @@ "Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "The application does not allow to sign up new account" }, - "application": { - "Parameter organization is missing": "Parameter organization is missing" - }, "auth": { "%s No phone prefix": "%s No phone prefix", "Challenge method should be S256": "Challenge method should be S256", @@ -64,12 +61,12 @@ "unsupported password type: %s": "unsupported password type: %s" }, "general": { + "Missing parameter": "Missing parameter", "Please login first": "Please login first", "The user: %s doesn't exist": "The user: %s doesn't exist" }, "ldap": { - "Ldap server exist": "Ldap server exist", - "Missing parameter": "Missing parameter" + "Ldap server exist": "Ldap server exist" }, "link": { "Please link first": "Please link first", @@ -112,7 +109,8 @@ "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid client_id": "Invalid client_id", - "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" + "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list", + "Token not found, invalid accessToken": "Token not found, invalid accessToken" }, "user": { "Display name cannot be empty": "Display name cannot be empty", @@ -131,7 +129,6 @@ "Code has not been sent yet!": "Code has not been sent yet!", "Email is invalid": "Email is invalid", "Invalid captcha provider.": "Invalid captcha provider.", - "Missing parameter": "Missing parameter", "Organization does not exist": "Organization does not exist", "Phone number is invalid": "Phone number is invalid", "Turing test failed.": "Turing test failed.", diff --git a/i18n/locales/ja/data.json b/i18n/locales/ja/data.json index 446bad33..7f77e4c2 100644 --- a/i18n/locales/ja/data.json +++ b/i18n/locales/ja/data.json @@ -8,9 +8,6 @@ "Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "The application does not allow to sign up new account" }, - "application": { - "Parameter organization is missing": "Parameter organization is missing" - }, "auth": { "%s No phone prefix": "%s No phone prefix", "Challenge method should be S256": "Challenge method should be S256", @@ -64,12 +61,12 @@ "unsupported password type: %s": "unsupported password type: %s" }, "general": { + "Missing parameter": "Missing parameter", "Please login first": "Please login first", "The user: %s doesn't exist": "The user: %s doesn't exist" }, "ldap": { - "Ldap server exist": "Ldap server exist", - "Missing parameter": "Missing parameter" + "Ldap server exist": "Ldap server exist" }, "link": { "Please link first": "Please link first", @@ -112,7 +109,8 @@ "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid client_id": "Invalid client_id", - "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" + "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list", + "Token not found, invalid accessToken": "Token not found, invalid accessToken" }, "user": { "Display name cannot be empty": "Display name cannot be empty", @@ -131,7 +129,6 @@ "Code has not been sent yet!": "Code has not been sent yet!", "Email is invalid": "Email is invalid", "Invalid captcha provider.": "Invalid captcha provider.", - "Missing parameter": "Missing parameter", "Organization does not exist": "Organization does not exist", "Phone number is invalid": "Phone number is invalid", "Turing test failed.": "Turing test failed.", diff --git a/i18n/locales/ko/data.json b/i18n/locales/ko/data.json index 446bad33..7f77e4c2 100644 --- a/i18n/locales/ko/data.json +++ b/i18n/locales/ko/data.json @@ -8,9 +8,6 @@ "Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "The application does not allow to sign up new account" }, - "application": { - "Parameter organization is missing": "Parameter organization is missing" - }, "auth": { "%s No phone prefix": "%s No phone prefix", "Challenge method should be S256": "Challenge method should be S256", @@ -64,12 +61,12 @@ "unsupported password type: %s": "unsupported password type: %s" }, "general": { + "Missing parameter": "Missing parameter", "Please login first": "Please login first", "The user: %s doesn't exist": "The user: %s doesn't exist" }, "ldap": { - "Ldap server exist": "Ldap server exist", - "Missing parameter": "Missing parameter" + "Ldap server exist": "Ldap server exist" }, "link": { "Please link first": "Please link first", @@ -112,7 +109,8 @@ "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid client_id": "Invalid client_id", - "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" + "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list", + "Token not found, invalid accessToken": "Token not found, invalid accessToken" }, "user": { "Display name cannot be empty": "Display name cannot be empty", @@ -131,7 +129,6 @@ "Code has not been sent yet!": "Code has not been sent yet!", "Email is invalid": "Email is invalid", "Invalid captcha provider.": "Invalid captcha provider.", - "Missing parameter": "Missing parameter", "Organization does not exist": "Organization does not exist", "Phone number is invalid": "Phone number is invalid", "Turing test failed.": "Turing test failed.", diff --git a/i18n/locales/ru/data.json b/i18n/locales/ru/data.json index 446bad33..7f77e4c2 100644 --- a/i18n/locales/ru/data.json +++ b/i18n/locales/ru/data.json @@ -8,9 +8,6 @@ "Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "The application does not allow to sign up new account" }, - "application": { - "Parameter organization is missing": "Parameter organization is missing" - }, "auth": { "%s No phone prefix": "%s No phone prefix", "Challenge method should be S256": "Challenge method should be S256", @@ -64,12 +61,12 @@ "unsupported password type: %s": "unsupported password type: %s" }, "general": { + "Missing parameter": "Missing parameter", "Please login first": "Please login first", "The user: %s doesn't exist": "The user: %s doesn't exist" }, "ldap": { - "Ldap server exist": "Ldap server exist", - "Missing parameter": "Missing parameter" + "Ldap server exist": "Ldap server exist" }, "link": { "Please link first": "Please link first", @@ -112,7 +109,8 @@ "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid client_id": "Invalid client_id", - "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" + "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list", + "Token not found, invalid accessToken": "Token not found, invalid accessToken" }, "user": { "Display name cannot be empty": "Display name cannot be empty", @@ -131,7 +129,6 @@ "Code has not been sent yet!": "Code has not been sent yet!", "Email is invalid": "Email is invalid", "Invalid captcha provider.": "Invalid captcha provider.", - "Missing parameter": "Missing parameter", "Organization does not exist": "Organization does not exist", "Phone number is invalid": "Phone number is invalid", "Turing test failed.": "Turing test failed.", diff --git a/i18n/locales/zh/data.json b/i18n/locales/zh/data.json index 607c9f1a..4cb3e2c8 100644 --- a/i18n/locales/zh/data.json +++ b/i18n/locales/zh/data.json @@ -8,9 +8,6 @@ "Please sign out first before signing up": "请在注册前先退出登录", "The application does not allow to sign up new account": "该应用不允许注册新用户" }, - "application": { - "Parameter organization is missing": "缺少organization参数" - }, "auth": { "%s No phone prefix": "%s 无此手机号前缀", "Challenge method should be S256": "Challenge 方法应该为 S256", @@ -64,12 +61,12 @@ "unsupported password type: %s": "不支持的密码类型: %s" }, "general": { + "Missing parameter": "缺少参数", "Please login first": "请先登录", "The user: %s doesn't exist": "用户: %s 不存在" }, "ldap": { - "Ldap server exist": "LDAP服务器已存在", - "Missing parameter": "LDAP缺少参数" + "Ldap server exist": "LDAP服务器已存在" }, "link": { "Please link first": "请先绑定", @@ -112,7 +109,8 @@ "Grant_type: %s is not supported in this application": "该应用不支持Grant_type: %s", "Invalid application or wrong clientSecret": "无效应用或错误的clientSecret", "Invalid client_id": "无效的ClientId", - "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "重定向 URI:%s 在许可跳转列表中未找到" + "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "重定向 URI:%s 在许可跳转列表中未找到", + "Token not found, invalid accessToken": "未查询到对应token, accessToken无效" }, "user": { "Display name cannot be empty": "显示名称不可为空", @@ -131,7 +129,6 @@ "Code has not been sent yet!": "验证码还未发送", "Email is invalid": "非法的邮箱", "Invalid captcha provider.": "非法的验证码提供商", - "Missing parameter": "缺少参数", "Organization does not exist": "组织不存在", "Phone number is invalid": "非法的手机号码", "Turing test failed.": "验证码还未发送", diff --git a/object/oidc_discovery.go b/object/oidc_discovery.go index 8c6bb51d..a6f6775e 100644 --- a/object/oidc_discovery.go +++ b/object/oidc_discovery.go @@ -40,6 +40,7 @@ type OidcDiscovery struct { ClaimsSupported []string `json:"claims_supported"` RequestParameterSupported bool `json:"request_parameter_supported"` RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"` + EndSessionEndpoint string `json:"end_session_endpoint"` } func getOriginFromHost(host string) (string, string) { @@ -84,6 +85,7 @@ func GetOidcDiscovery(host string) OidcDiscovery { ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isGlobalAdmin", "isForbidden", "signupApplication", "ldap"}, RequestParameterSupported: true, RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"}, + EndSessionEndpoint: fmt.Sprintf("%s/api/logout", originBackend), } return oidcDiscovery diff --git a/object/session.go b/object/session.go index 62d82571..1a56ca17 100644 --- a/object/session.go +++ b/object/session.go @@ -15,8 +15,6 @@ package object import ( - "time" - "github.com/beego/beego" "github.com/casdoor/casdoor/util" "xorm.io/core" @@ -42,7 +40,7 @@ func SetSession(id string, sessionId string) { if get { _, err = adapter.Engine.ID(core.PK{owner, name}).Update(session) } else { - session.CreatedTime = time.Now().Format(time.RFC3339) + session.CreatedTime = util.GetCurrentTime() _, err = adapter.Engine.Insert(session) } if err != nil { @@ -66,7 +64,7 @@ func DeleteSession(id string) bool { } func DeleteSessionId(id string, sessionId string) bool { - owner, name := util.GetOwnerAndNameFromIdNoCheck(id) + owner, name := util.GetOwnerAndNameFromId(id) session := &Session{Owner: owner, Name: name} _, err := adapter.Engine.ID(core.PK{owner, name}).Get(session) diff --git a/object/token.go b/object/token.go index dc8c3e93..bee14d4b 100644 --- a/object/token.go +++ b/object/token.go @@ -27,7 +27,7 @@ import ( ) const ( - hourSeconds = 3600 + hourMinutes = 60 InvalidRequest = "invalid_request" InvalidClient = "invalid_client" InvalidGrant = "invalid_grant" @@ -204,7 +204,7 @@ func DeleteToken(token *Token) bool { return affected != 0 } -func DeleteTokenByAccessToken(accessToken string) (bool, *Application) { +func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token) { token := Token{AccessToken: accessToken} existed, err := adapter.Engine.Get(&token) if err != nil { @@ -212,15 +212,17 @@ func DeleteTokenByAccessToken(accessToken string) (bool, *Application) { } if !existed { - return false, nil + return false, nil, nil } - application := getApplication(token.Owner, token.Application) - affected, err := adapter.Engine.Where("access_token=?", accessToken).Delete(&Token{}) + + token.ExpiresIn = 0 + affected, err := adapter.Engine.ID(core.PK{token.Owner, token.Name}).Cols("expires_in").Update(&token) if err != nil { panic(err) } - return affected != 0, application + application := getApplication(token.Owner, token.Application) + return affected != 0, application, &token } func GetTokenByAccessToken(accessToken string) *Token { @@ -304,7 +306,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU Code: util.GenerateClientId(), AccessToken: accessToken, RefreshToken: refreshToken, - ExpiresIn: application.ExpireInHours * hourSeconds, + ExpiresIn: application.ExpireInHours * hourMinutes, Scope: scope, TokenType: "Bearer", CodeChallenge: challenge, @@ -438,7 +440,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId Code: util.GenerateClientId(), AccessToken: newAccessToken, RefreshToken: newRefreshToken, - ExpiresIn: application.ExpireInHours * hourSeconds, + ExpiresIn: application.ExpireInHours * hourMinutes, Scope: scope, TokenType: "Bearer", } @@ -588,7 +590,7 @@ func GetPasswordToken(application *Application, username string, password string Code: util.GenerateClientId(), AccessToken: accessToken, RefreshToken: refreshToken, - ExpiresIn: application.ExpireInHours * hourSeconds, + ExpiresIn: application.ExpireInHours * hourMinutes, Scope: scope, TokenType: "Bearer", CodeIsUsed: true, @@ -628,7 +630,7 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc User: nullUser.Name, Code: util.GenerateClientId(), AccessToken: accessToken, - ExpiresIn: application.ExpireInHours * hourSeconds, + ExpiresIn: application.ExpireInHours * hourMinutes, Scope: scope, TokenType: "Bearer", CodeIsUsed: true, @@ -655,7 +657,7 @@ func GetTokenByUser(application *Application, user *User, scope string, host str Code: util.GenerateClientId(), AccessToken: accessToken, RefreshToken: refreshToken, - ExpiresIn: application.ExpireInHours * hourSeconds, + ExpiresIn: application.ExpireInHours * hourMinutes, Scope: scope, TokenType: "Bearer", CodeIsUsed: true, diff --git a/routers/router.go b/routers/router.go index 70b9cc83..6f54a9c4 100644 --- a/routers/router.go +++ b/routers/router.go @@ -158,8 +158,6 @@ func initAPI() { beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken") beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken") beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken") - beego.Router("/api/login/oauth/logout", &controllers.ApiController{}, "GET:TokenLogout") - beego.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords") beego.Router("/api/get-records-filter", &controllers.ApiController{}, "POST:GetRecordsByFilter") beego.Router("/api/add-record", &controllers.ApiController{}, "POST:AddRecord") diff --git a/swagger/swagger.json b/swagger/swagger.json index b8cf5f17..77677717 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -275,6 +275,34 @@ } } }, + "/api/add-record": { + "post": { + "tags": [ + "Record API" + ], + "description": "add a record", + "operationId": "ApiController.AddRecord", + "parameters": [ + { + "in": "body", + "name": "body", + "description": "The details of the record", + "required": true, + "schema": { + "$ref": "#/definitions/object.Record" + } + } + ], + "responses": { + "200": { + "description": "The Response object", + "schema": { + "$ref": "#/definitions/controllers.Response" + } + } + } + } + }, "/api/add-resource": { "post": { "tags": [ @@ -431,6 +459,14 @@ "operationId": "ApiController.GetCaptcha" } }, + "/api/api/get-webhook-event": { + "get": { + "tags": [ + "GetWebhookEventType API" + ], + "operationId": "ApiController.GetWebhookEventType" + } + }, "/api/api/reset-email-or-phone": { "post": { "tags": [ @@ -523,6 +559,14 @@ } } }, + "/api/api/webhook": { + "post": { + "tags": [ + "HandleOfficialAccountEvent API" + ], + "operationId": "ApiController.HandleOfficialAccountEvent" + } + }, "/api/buy-product": { "post": { "tags": [ @@ -840,6 +884,35 @@ } } }, + "/api/delete-session": { + "post": { + "tags": [ + "Session API" + ], + "description": "Delete session by userId", + "operationId": "ApiController.DeleteSession", + "parameters": [ + { + "in": "query", + "name": "ID", + "description": "The ID(owner/name) of user.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The Response object", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, "/api/delete-syncer": { "post": { "tags": [ @@ -1133,6 +1206,32 @@ } } }, + "/api/get-default-application": { + "get": { + "tags": [ + "Organization API" + ], + "description": "get default application", + "operationId": "ApiController.GetDefaultApplication", + "parameters": [ + { + "in": "query", + "name": "id", + "description": "organization id", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The Response object", + "schema": { + "$ref": "#/definitions/Response" + } + } + } + } + }, "/api/get-email-and-phone": { "post": { "tags": [ @@ -1166,6 +1265,26 @@ } } }, + "/api/get-global-providers": { + "get": { + "tags": [ + "Provider API" + ], + "description": "get Global providers", + "operationId": "ApiController.GetGlobalProviders", + "responses": { + "200": { + "description": "The Response object", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/object.Provider" + } + } + } + } + } + }, "/api/get-global-users": { "get": { "tags": [ @@ -1459,6 +1578,55 @@ } } }, + "/api/get-permissions-by-role": { + "get": { + "tags": [ + "Permission API" + ], + "description": "get permissions by role", + "operationId": "ApiController.GetPermissionsByRole", + "parameters": [ + { + "in": "query", + "name": "id", + "description": "The id of the role", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The Response object", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/object.Permission" + } + } + } + } + } + }, + "/api/get-permissions-by-submitter": { + "get": { + "tags": [ + "Permission API" + ], + "description": "get permissions by submitter", + "operationId": "ApiController.GetPermissionsBySubmitter", + "responses": { + "200": { + "description": "The Response object", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/object.Permission" + } + } + } + } + } + }, "/api/get-product": { "get": { "tags": [ @@ -1631,6 +1799,20 @@ } } }, + "/api/get-release": { + "get": { + "tags": [ + "System API" + ], + "description": "get local github repo's latest release version info", + "operationId": "ApiController.GitRepoVersion", + "responses": { + "200": { + "description": "{string} local latest version hash of casdoor" + } + } + } + }, "/api/get-resource": { "get": { "tags": [ @@ -1702,6 +1884,35 @@ } } }, + "/api/get-sessions": { + "get": { + "tags": [ + "Session API" + ], + "description": "Get organization user sessions", + "operationId": "ApiController.GetSessions", + "parameters": [ + { + "in": "query", + "name": "owner", + "description": "The organization name", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The Response object", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, "/api/get-sorted-users": { "get": { "tags": [ @@ -1799,6 +2010,32 @@ } } }, + "/api/get-system-info": { + "get": { + "tags": [ + "System API" + ], + "description": "get user's system info", + "operationId": "ApiController.GetSystemInfo", + "parameters": [ + { + "in": "query", + "name": "id", + "description": "The id of the user", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The Response object", + "schema": { + "$ref": "#/definitions/object.SystemInfo" + } + } + } + } + }, "/api/get-token": { "get": { "tags": [ @@ -2360,45 +2597,6 @@ } } }, - "/api/login/oauth/logout": { - "get": { - "tags": [ - "Token API" - ], - "description": "delete token by AccessToken", - "operationId": "ApiController.TokenLogout", - "parameters": [ - { - "in": "query", - "name": "id_token_hint", - "description": "id_token_hint", - "required": true, - "type": "string" - }, - { - "in": "query", - "name": "post_logout_redirect_uri", - "description": "post_logout_redirect_uri", - "type": "string" - }, - { - "in": "query", - "name": "state", - "description": "state", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "$ref": "#/definitions/controllers.Response" - } - } - } - } - }, "/api/login/oauth/refresh_token": { "post": { "tags": [ @@ -2471,6 +2669,26 @@ ], "description": "logout the current user", "operationId": "ApiController.Logout", + "parameters": [ + { + "in": "query", + "name": "id_token_hint", + "description": "id_token_hint", + "type": "string" + }, + { + "in": "query", + "name": "post_logout_redirect_uri", + "description": "post_logout_redirect_uri", + "type": "string" + }, + { + "in": "query", + "name": "state", + "description": "state", + "type": "string" + } + ], "responses": { "200": { "description": "The Response object", @@ -2486,6 +2704,26 @@ ], "description": "logout the current user", "operationId": "ApiController.Logout", + "parameters": [ + { + "in": "query", + "name": "id_token_hint", + "description": "id_token_hint", + "type": "string" + }, + { + "in": "query", + "name": "post_logout_redirect_uri", + "description": "post_logout_redirect_uri", + "type": "string" + }, + { + "in": "query", + "name": "state", + "description": "state", + "type": "string" + } + ], "responses": { "200": { "description": "The Response object", @@ -3267,11 +3505,11 @@ } }, "definitions": { - "2200.0xc0003f8480.false": { + "2268.0xc0000f9650.false": { "title": "false", "type": "object" }, - "2235.0xc0003f84b0.false": { + "2302.0xc0000f9680.false": { "title": "false", "type": "object" }, @@ -3316,6 +3554,15 @@ "autoSignin": { "type": "boolean" }, + "captchaToken": { + "type": "string" + }, + "captchaType": { + "type": "string" + }, + "clientSecret": { + "type": "string" + }, "code": { "type": "string" }, @@ -3389,10 +3636,10 @@ "type": "object", "properties": { "data": { - "$ref": "#/definitions/2200.0xc0003f8480.false" + "$ref": "#/definitions/2268.0xc0000f9650.false" }, "data2": { - "$ref": "#/definitions/2235.0xc0003f84b0.false" + "$ref": "#/definitions/2302.0xc0000f9680.false" }, "msg": { "type": "string" @@ -3491,9 +3738,15 @@ "displayName": { "type": "string" }, + "enableAutoSignin": { + "type": "boolean" + }, "enableCodeSignin": { "type": "boolean" }, + "enableLinkWithEmail": { + "type": "boolean" + }, "enablePassword": { "type": "boolean" }, @@ -3516,6 +3769,19 @@ "forgetUrl": { "type": "string" }, + "formBackgroundUrl": { + "type": "string" + }, + "formCss": { + "type": "string" + }, + "formOffset": { + "type": "integer", + "format": "int64" + }, + "formSideHtml": { + "type": "string" + }, "grantTypes": { "type": "array", "items": { @@ -3556,6 +3822,9 @@ "type": "integer", "format": "int64" }, + "samlReplyUrl": { + "type": "string" + }, "signinHtml": { "type": "string" }, @@ -3689,6 +3958,24 @@ } } }, + "object.ManagedAccount": { + "title": "ManagedAccount", + "type": "object", + "properties": { + "application": { + "type": "string" + }, + "password": { + "type": "string" + }, + "signinUrl": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "object.Model": { "title": "Model", "type": "object", @@ -3726,6 +4013,9 @@ "type": "string" } }, + "end_session_endpoint": { + "type": "string" + }, "grant_types_supported": { "type": "array", "items": { @@ -3801,6 +4091,9 @@ "createdTime": { "type": "string" }, + "defaultApplication": { + "type": "string" + }, "defaultAvatar": { "type": "string" }, @@ -3813,9 +4106,19 @@ "favicon": { "type": "string" }, + "initScore": { + "type": "integer", + "format": "int64" + }, "isProfilePublic": { "type": "boolean" }, + "languages": { + "type": "array", + "items": { + "type": "string" + } + }, "masterPassword": { "type": "string" }, @@ -3943,12 +4246,27 @@ "type": "string" } }, + "adapter": { + "type": "string" + }, + "approveTime": { + "type": "string" + }, + "approver": { + "type": "string" + }, "createdTime": { "type": "string" }, "displayName": { "type": "string" }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + }, "effect": { "type": "string" }, @@ -3979,6 +4297,12 @@ "type": "string" } }, + "state": { + "type": "string" + }, + "submitter": { + "type": "string" + }, "users": { "type": "array", "items": { @@ -3997,6 +4321,9 @@ "currency": { "type": "string" }, + "description": { + "type": "string" + }, "detail": { "type": "string" }, @@ -4016,6 +4343,12 @@ "type": "number", "format": "double" }, + "providerObjs": { + "type": "array", + "items": { + "$ref": "#/definitions/object.Provider" + } + }, "providers": { "type": "array", "items": { @@ -4090,6 +4423,9 @@ "customUserInfoUrl": { "type": "string" }, + "disableSsl": { + "type": "boolean" + }, "displayName": { "type": "string" }, @@ -4126,6 +4462,9 @@ "owner": { "type": "string" }, + "pathPrefix": { + "type": "string" + }, "port": { "type": "integer", "format": "int64" @@ -4133,6 +4472,9 @@ "providerUrl": { "type": "string" }, + "receiver": { + "type": "string" + }, "regionId": { "type": "string" }, @@ -4172,11 +4514,17 @@ "name": { "type": "string" }, + "owner": { + "type": "string" + }, "prompted": { "type": "boolean" }, "provider": { "$ref": "#/definitions/object.Provider" + }, + "rule": { + "type": "string" } } }, @@ -4233,6 +4581,12 @@ "displayName": { "type": "string" }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + }, "isEnabled": { "type": "boolean" }, @@ -4345,6 +4699,10 @@ } } }, + "object.SystemInfo": { + "title": "SystemInfo", + "type": "object" + }, "object.TableColumn": { "title": "TableColumn", "type": "object", @@ -4605,15 +4963,27 @@ "lastSigninTime": { "type": "string" }, + "lastSigninWrongTime": { + "type": "string" + }, "ldap": { "type": "string" }, + "line": { + "type": "string" + }, "linkedin": { "type": "string" }, "location": { "type": "string" }, + "managedAccounts": { + "type": "array", + "items": { + "$ref": "#/definitions/object.ManagedAccount" + } + }, "name": { "type": "string" }, @@ -4669,6 +5039,10 @@ "type": "integer", "format": "int64" }, + "signinWrongTimes": { + "type": "integer", + "format": "int64" + }, "signupApplication": { "type": "string" }, @@ -4687,9 +5061,6 @@ "type": { "type": "string" }, - "unionId": { - "type": "string" - }, "updatedTime": { "type": "string" }, diff --git a/swagger/swagger.yml b/swagger/swagger.yml index c08695ca..4fc996b0 100644 --- a/swagger/swagger.yml +++ b/swagger/swagger.yml @@ -177,6 +177,24 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' + /api/add-record: + post: + tags: + - Record API + description: add a record + operationId: ApiController.AddRecord + parameters: + - in: body + name: body + description: The details of the record + required: true + schema: + $ref: '#/definitions/object.Record' + responses: + "200": + description: The Response object + schema: + $ref: '#/definitions/controllers.Response' /api/add-resource: post: tags: @@ -277,6 +295,11 @@ paths: tags: - Login API operationId: ApiController.GetCaptcha + /api/api/get-webhook-event: + get: + tags: + - GetWebhookEventType API + operationId: ApiController.GetWebhookEventType /api/api/reset-email-or-phone: post: tags: @@ -338,6 +361,11 @@ paths: description: object schema: $ref: '#/definitions/Response' + /api/api/webhook: + post: + tags: + - HandleOfficialAccountEvent API + operationId: ApiController.HandleOfficialAccountEvent /api/buy-product: post: tags: @@ -542,6 +570,25 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' + /api/delete-session: + post: + tags: + - Session API + description: Delete session by userId + operationId: ApiController.DeleteSession + parameters: + - in: query + name: ID + description: The ID(owner/name) of user. + required: true + type: string + responses: + "200": + description: The Response object + schema: + type: array + items: + type: string /api/delete-syncer: post: tags: @@ -734,6 +781,23 @@ paths: type: array items: $ref: '#/definitions/object.Cert' + /api/get-default-application: + get: + tags: + - Organization API + description: get default application + operationId: ApiController.GetDefaultApplication + parameters: + - in: query + name: id + description: organization id + required: true + type: string + responses: + "200": + description: The Response object + schema: + $ref: '#/definitions/Response' /api/get-email-and-phone: post: tags: @@ -756,6 +820,19 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' + /api/get-global-providers: + get: + tags: + - Provider API + description: get Global providers + operationId: ApiController.GetGlobalProviders + responses: + "200": + description: The Response object + schema: + type: array + items: + $ref: '#/definitions/object.Provider' /api/get-global-users: get: tags: @@ -947,6 +1024,38 @@ paths: type: array items: $ref: '#/definitions/object.Permission' + /api/get-permissions-by-role: + get: + tags: + - Permission API + description: get permissions by role + operationId: ApiController.GetPermissionsByRole + parameters: + - in: query + name: id + description: The id of the role + required: true + type: string + responses: + "200": + description: The Response object + schema: + type: array + items: + $ref: '#/definitions/object.Permission' + /api/get-permissions-by-submitter: + get: + tags: + - Permission API + description: get permissions by submitter + operationId: ApiController.GetPermissionsBySubmitter + responses: + "200": + description: The Response object + schema: + type: array + items: + $ref: '#/definitions/object.Permission' /api/get-product: get: tags: @@ -1060,6 +1169,15 @@ paths: description: The Response object schema: $ref: '#/definitions/object.Record' + /api/get-release: + get: + tags: + - System API + description: get local github repo's latest release version info + operationId: ApiController.GitRepoVersion + responses: + "200": + description: '{string} local latest version hash of casdoor' /api/get-resource: get: tags: @@ -1106,6 +1224,25 @@ paths: type: array items: $ref: '#/definitions/object.Role' + /api/get-sessions: + get: + tags: + - Session API + description: Get organization user sessions + operationId: ApiController.GetSessions + parameters: + - in: query + name: owner + description: The organization name + required: true + type: string + responses: + "200": + description: The Response object + schema: + type: array + items: + type: string /api/get-sorted-users: get: tags: @@ -1170,6 +1307,23 @@ paths: type: array items: $ref: '#/definitions/object.Syncer' + /api/get-system-info: + get: + tags: + - System API + description: get user's system info + operationId: ApiController.GetSystemInfo + parameters: + - in: query + name: id + description: The id of the user + required: true + type: string + responses: + "200": + description: The Response object + schema: + $ref: '#/definitions/object.SystemInfo' /api/get-token: get: tags: @@ -1544,32 +1698,6 @@ paths: description: The Response object schema: $ref: '#/definitions/object.TokenError' - /api/login/oauth/logout: - get: - tags: - - Token API - description: delete token by AccessToken - operationId: ApiController.TokenLogout - parameters: - - in: query - name: id_token_hint - description: id_token_hint - required: true - type: string - - in: query - name: post_logout_redirect_uri - description: post_logout_redirect_uri - type: string - - in: query - name: state - description: state - required: true - type: string - responses: - "200": - description: The Response object - schema: - $ref: '#/definitions/controllers.Response' /api/login/oauth/refresh_token: post: tags: @@ -1620,6 +1748,19 @@ paths: - Login API description: logout the current user operationId: ApiController.Logout + parameters: + - in: query + name: id_token_hint + description: id_token_hint + type: string + - in: query + name: post_logout_redirect_uri + description: post_logout_redirect_uri + type: string + - in: query + name: state + description: state + type: string responses: "200": description: The Response object @@ -1630,6 +1771,19 @@ paths: - Login API description: logout the current user operationId: ApiController.Logout + parameters: + - in: query + name: id_token_hint + description: id_token_hint + type: string + - in: query + name: post_logout_redirect_uri + description: post_logout_redirect_uri + type: string + - in: query + name: state + description: state + type: string responses: "200": description: The Response object @@ -2139,10 +2293,10 @@ paths: schema: $ref: '#/definitions/Response' definitions: - 2200.0xc0003f8480.false: + 2268.0xc0000f9650.false: title: "false" type: object - 2235.0xc0003f84b0.false: + 2302.0xc0000f9680.false: title: "false" type: object Response: @@ -2174,6 +2328,12 @@ definitions: type: string autoSignin: type: boolean + captchaToken: + type: string + captchaType: + type: string + clientSecret: + type: string code: type: string email: @@ -2223,9 +2383,9 @@ definitions: type: object properties: data: - $ref: '#/definitions/2200.0xc0003f8480.false' + $ref: '#/definitions/2268.0xc0000f9650.false' data2: - $ref: '#/definitions/2235.0xc0003f84b0.false' + $ref: '#/definitions/2302.0xc0000f9680.false' msg: type: string name: @@ -2291,8 +2451,12 @@ definitions: type: string displayName: type: string + enableAutoSignin: + type: boolean enableCodeSignin: type: boolean + enableLinkWithEmail: + type: boolean enablePassword: type: boolean enableSamlCompress: @@ -2308,6 +2472,15 @@ definitions: format: int64 forgetUrl: type: string + formBackgroundUrl: + type: string + formCss: + type: string + formOffset: + type: integer + format: int64 + formSideHtml: + type: string grantTypes: type: array items: @@ -2335,6 +2508,8 @@ definitions: refreshExpireInHours: type: integer format: int64 + samlReplyUrl: + type: string signinHtml: type: string signinUrl: @@ -2424,6 +2599,18 @@ definitions: type: string username: type: string + object.ManagedAccount: + title: ManagedAccount + type: object + properties: + application: + type: string + password: + type: string + signinUrl: + type: string + username: + type: string object.Model: title: Model type: object @@ -2450,6 +2637,8 @@ definitions: type: array items: type: string + end_session_endpoint: + type: string grant_types_supported: type: array items: @@ -2500,6 +2689,8 @@ definitions: $ref: '#/definitions/object.AccountItem' createdTime: type: string + defaultApplication: + type: string defaultAvatar: type: string displayName: @@ -2508,8 +2699,15 @@ definitions: type: boolean favicon: type: string + initScore: + type: integer + format: int64 isProfilePublic: type: boolean + languages: + type: array + items: + type: string masterPassword: type: string name: @@ -2595,10 +2793,20 @@ definitions: type: array items: type: string + adapter: + type: string + approveTime: + type: string + approver: + type: string createdTime: type: string displayName: type: string + domains: + type: array + items: + type: string effect: type: string isEnabled: @@ -2619,6 +2827,10 @@ definitions: type: array items: type: string + state: + type: string + submitter: + type: string users: type: array items: @@ -2631,6 +2843,8 @@ definitions: type: string currency: type: string + description: + type: string detail: type: string displayName: @@ -2644,6 +2858,10 @@ definitions: price: type: number format: double + providerObjs: + type: array + items: + $ref: '#/definitions/object.Provider' providers: type: array items: @@ -2694,6 +2912,8 @@ definitions: type: string customUserInfoUrl: type: string + disableSsl: + type: boolean displayName: type: string domain: @@ -2718,11 +2938,15 @@ definitions: type: string owner: type: string + pathPrefix: + type: string port: type: integer format: int64 providerUrl: type: string + receiver: + type: string regionId: type: string signName: @@ -2749,10 +2973,14 @@ definitions: type: boolean name: type: string + owner: + type: string prompted: type: boolean provider: $ref: '#/definitions/object.Provider' + rule: + type: string object.Record: title: Record type: object @@ -2790,6 +3018,10 @@ definitions: type: string displayName: type: string + domains: + type: array + items: + type: string isEnabled: type: boolean name: @@ -2864,6 +3096,9 @@ definitions: type: string user: type: string + object.SystemInfo: + title: SystemInfo + type: object object.TableColumn: title: TableColumn type: object @@ -3040,12 +3275,20 @@ definitions: type: string lastSigninTime: type: string + lastSigninWrongTime: + type: string ldap: type: string + line: + type: string linkedin: type: string location: type: string + managedAccounts: + type: array + items: + $ref: '#/definitions/object.ManagedAccount' name: type: string okta: @@ -3083,6 +3326,9 @@ definitions: score: type: integer format: int64 + signinWrongTimes: + type: integer + format: int64 signupApplication: type: string slack: @@ -3095,8 +3341,6 @@ definitions: type: string type: type: string - unionId: - type: string updatedTime: type: string webauthnCredentials: diff --git a/util/time_test.go b/util/time_test.go index 801b60d7..d611950c 100644 --- a/util/time_test.go +++ b/util/time_test.go @@ -53,7 +53,7 @@ func Test_IsTokenExpired(t *testing.T) { for _, scenario := range []testCases{ { - description: "Token emited now is valid for 60 minutes", + description: "Token emitted now is valid for 60 minutes", input: input{ createdTime: time.Now().Format(time.RFC3339), expiresIn: 60, @@ -61,7 +61,7 @@ func Test_IsTokenExpired(t *testing.T) { expected: false, }, { - description: "Token emited 60 minutes before now is valid for 60 minutes", + description: "Token emitted 60 minutes before now is valid for 60 minutes", input: input{ createdTime: time.Now().Add(-time.Minute * 60).Format(time.RFC3339), expiresIn: 61, @@ -69,7 +69,7 @@ func Test_IsTokenExpired(t *testing.T) { expected: false, }, { - description: "Token emited 2 hours before now is Expired after 60 minutes", + description: "Token emitted 2 hours before now is Expired after 60 minutes", input: input{ createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339), expiresIn: 60, @@ -77,7 +77,7 @@ func Test_IsTokenExpired(t *testing.T) { expected: true, }, { - description: "Token emited 61 minutes before now is Expired after 60 minutes", + description: "Token emitted 61 minutes before now is Expired after 60 minutes", input: input{ createdTime: time.Now().Add(-time.Minute * 61).Format(time.RFC3339), expiresIn: 60, @@ -85,7 +85,7 @@ func Test_IsTokenExpired(t *testing.T) { expected: true, }, { - description: "Token emited 2 hours before now is velid for 120 minutes", + description: "Token emitted 2 hours before now is valid for 120 minutes", input: input{ createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339), expiresIn: 121, @@ -93,7 +93,7 @@ func Test_IsTokenExpired(t *testing.T) { expected: false, }, { - description: "Token emited 159 minutes before now is Expired after 60 minutes", + description: "Token emitted 159 minutes before now is Expired after 60 minutes", input: input{ createdTime: time.Now().Add(-time.Minute * 159).Format(time.RFC3339), expiresIn: 120,