| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package v4 |
| |
| import ( |
| "context" |
| "encoding/json" |
| errorsEx "github.com/apache/servicecomb-service-center/pkg/errors" |
| "github.com/apache/servicecomb-service-center/pkg/log" |
| "github.com/apache/servicecomb-service-center/pkg/rbacframe" |
| "github.com/apache/servicecomb-service-center/pkg/rest" |
| "github.com/apache/servicecomb-service-center/pkg/util" |
| "github.com/apache/servicecomb-service-center/server/rest/controller" |
| "github.com/apache/servicecomb-service-center/server/scerror" |
| "github.com/apache/servicecomb-service-center/server/service" |
| "github.com/apache/servicecomb-service-center/server/service/rbac" |
| "github.com/apache/servicecomb-service-center/server/service/rbac/dao" |
| "github.com/go-chassis/go-chassis/security/authr" |
| "io/ioutil" |
| "net/http" |
| ) |
| |
| type AuthResource struct { |
| } |
| |
| //URLPatterns define htp pattern |
| func (r *AuthResource) URLPatterns() []rest.Route { |
| return []rest.Route{ |
| {Method: http.MethodPost, Path: "/v4/token", Func: r.Login}, |
| {Method: http.MethodPost, Path: "/v4/account", Func: r.CreateAccount}, |
| {Method: http.MethodGet, Path: "/v4/account", Func: r.ListAccount}, |
| {Method: http.MethodGet, Path: "/v4/account/:name", Func: r.GetAccount}, |
| {Method: http.MethodDelete, Path: "/v4/account/:name", Func: r.DeleteAccount}, |
| {Method: http.MethodPost, Path: "/v4/account/:name/password", Func: r.ChangePassword}, |
| } |
| } |
| func (r *AuthResource) CreateAccount(w http.ResponseWriter, req *http.Request) { |
| body, err := ioutil.ReadAll(req.Body) |
| if err != nil { |
| log.Error("read body err", err) |
| controller.WriteError(w, scerror.ErrInternal, err.Error()) |
| return |
| } |
| a := &rbacframe.Account{} |
| if err = json.Unmarshal(body, a); err != nil { |
| log.Error("json err", err) |
| controller.WriteError(w, scerror.ErrInvalidParams, errorsEx.MsgJSON) |
| return |
| } |
| err = service.ValidateCreateAccount(a) |
| if err != nil { |
| controller.WriteError(w, scerror.ErrInvalidParams, err.Error()) |
| return |
| } |
| err = dao.CreateAccount(context.TODO(), a) |
| if err != nil { |
| if err == dao.ErrDuplicated { |
| controller.WriteError(w, scerror.ErrConflictAccount, "") |
| return |
| } |
| log.Error(errorsEx.MsgOperateAccountFailed, err) |
| controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgOperateAccountFailed) |
| return |
| } |
| } |
| func (r *AuthResource) DeleteAccount(w http.ResponseWriter, req *http.Request) { |
| _, err := dao.DeleteAccount(context.TODO(), req.URL.Query().Get(":name")) |
| if err != nil { |
| log.Error(errorsEx.MsgOperateAccountFailed, err) |
| controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgOperateAccountFailed) |
| return |
| } |
| w.WriteHeader(http.StatusNoContent) |
| } |
| func (r *AuthResource) ListAccount(w http.ResponseWriter, req *http.Request) { |
| as, n, err := dao.ListAccount(context.TODO()) |
| if err != nil { |
| log.Error(errorsEx.MsgGetAccountFailed, err) |
| controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgGetAccountFailed) |
| return |
| } |
| resp := &rbacframe.AccountResponse{ |
| Total: n, |
| Accounts: as, |
| } |
| b, err := json.Marshal(resp) |
| if err != nil { |
| log.Error(errorsEx.MsgJSON, err) |
| controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgJSON) |
| return |
| } |
| controller.WriteJSON(w, b) |
| } |
| func (r *AuthResource) GetAccount(w http.ResponseWriter, req *http.Request) { |
| a, err := dao.GetAccount(context.TODO(), req.URL.Query().Get(":name")) |
| if err != nil { |
| log.Error(errorsEx.MsgGetAccountFailed, err) |
| controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgGetAccountFailed) |
| return |
| } |
| a.Password = "" |
| b, err := json.Marshal(a) |
| if err != nil { |
| log.Error(errorsEx.MsgJSON, err) |
| controller.WriteError(w, scerror.ErrInternal, errorsEx.MsgJSON) |
| return |
| } |
| controller.WriteJSON(w, b) |
| } |
| func (r *AuthResource) ChangePassword(w http.ResponseWriter, req *http.Request) { |
| ip := util.GetRealIP(req) |
| if rbac.IsBanned(ip) { |
| log.Warn("ip is banned:" + ip) |
| controller.WriteError(w, scerror.ErrForbidden, "") |
| return |
| } |
| body, err := ioutil.ReadAll(req.Body) |
| if err != nil { |
| log.Error("read body err", err) |
| controller.WriteError(w, scerror.ErrInternal, err.Error()) |
| return |
| } |
| a := &rbacframe.Account{} |
| if err = json.Unmarshal(body, a); err != nil { |
| log.Error("json err", err) |
| controller.WriteError(w, scerror.ErrInvalidParams, errorsEx.MsgJSON) |
| return |
| } |
| a.Name = req.URL.Query().Get(":name") |
| err = service.ValidateChangePWD(a) |
| if err != nil { |
| controller.WriteError(w, scerror.ErrInvalidParams, err.Error()) |
| return |
| } |
| changer, err := rbacframe.AccountFromContext(req.Context()) |
| if err != nil { |
| controller.WriteError(w, scerror.ErrInternal, "can not parse account info") |
| return |
| } |
| err = rbac.ChangePassword(context.TODO(), changer.Role, changer.Name, a) |
| if err != nil { |
| if err == rbac.ErrSamePassword || |
| err == rbac.ErrEmptyCurrentPassword || |
| err == rbac.ErrNoPermChangeAccount { |
| controller.WriteError(w, scerror.ErrInvalidParams, err.Error()) |
| return |
| } |
| if err == rbac.ErrWrongPassword { |
| rbac.CountFailure(ip) |
| controller.WriteError(w, scerror.ErrInvalidParams, err.Error()) |
| return |
| } |
| log.Error("change password failed", err) |
| controller.WriteError(w, scerror.ErrInternal, err.Error()) |
| return |
| } |
| } |
| |
| func (r *AuthResource) Login(w http.ResponseWriter, req *http.Request) { |
| ip := util.GetRealIP(req) |
| if rbac.IsBanned(ip) { |
| log.Warn("ip is banned:" + ip) |
| controller.WriteError(w, scerror.ErrForbidden, "") |
| return |
| } |
| body, err := ioutil.ReadAll(req.Body) |
| if err != nil { |
| log.Error("read body err", err) |
| controller.WriteError(w, scerror.ErrInternal, err.Error()) |
| return |
| } |
| a := &rbacframe.Account{} |
| if err = json.Unmarshal(body, a); err != nil { |
| log.Error("json err", err) |
| controller.WriteError(w, scerror.ErrInvalidParams, err.Error()) |
| return |
| } |
| err = service.ValidateAccountLogin(a) |
| if err != nil { |
| controller.WriteError(w, scerror.ErrInvalidParams, err.Error()) |
| return |
| } |
| if a.TokenExpirationTime == "" { |
| a.TokenExpirationTime = "30m" |
| } |
| t, err := authr.Login(context.TODO(), a.Name, a.Password, |
| authr.ExpireAfter(a.TokenExpirationTime)) |
| if err != nil { |
| if err == rbac.ErrUnauthorized { |
| log.Error("not authorized", err) |
| rbac.CountFailure(ip) |
| controller.WriteError(w, scerror.ErrUnauthorized, err.Error()) |
| return |
| } |
| log.Error("can not sign token", err) |
| controller.WriteError(w, scerror.ErrInternal, err.Error()) |
| return |
| } |
| to := &rbacframe.Token{TokenStr: t} |
| b, err := json.Marshal(to) |
| if err != nil { |
| log.Error("json err", err) |
| controller.WriteError(w, scerror.ErrInvalidParams, err.Error()) |
| return |
| } |
| controller.WriteJSON(w, b) |
| } |