github.com/greenpau/go-authcrunch@v1.1.4/pkg/authn/handle_http_apps.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package authn 16 17 import ( 18 "context" 19 "fmt" 20 "io" 21 "net/http" 22 "strings" 23 24 "github.com/greenpau/go-authcrunch/pkg/authn/enums/role" 25 "github.com/greenpau/go-authcrunch/pkg/authn/ui" 26 "github.com/greenpau/go-authcrunch/pkg/requests" 27 "github.com/greenpau/go-authcrunch/pkg/user" 28 "go.uber.org/zap" 29 ) 30 31 func (p *Portal) handleHTTPApps(ctx context.Context, w http.ResponseWriter, r *http.Request, rr *requests.Request, usr *user.User, appName string) error { 32 p.disableClientCache(w) 33 p.injectRedirectURL(ctx, w, r, rr) 34 if usr == nil && !strings.HasSuffix(r.URL.Path, appName+"/manifest.json") { 35 p.logger.Debug("app asset download is unauthorized", zap.String("app_name", appName), zap.String("app_file_url_path", r.URL.Path)) 36 return p.handleHTTPError(ctx, w, r, rr, http.StatusUnauthorized) 37 } 38 39 if err := p.authorizedRole(usr, []role.Kind{role.Admin, role.User}, rr.Response.Authenticated); err != nil { 40 if !strings.HasSuffix(r.URL.Path, appName+"/manifest.json") { 41 p.logger.Debug("app asset download is forbidden", zap.String("app_name", appName), zap.String("app_file_url_path", r.URL.Path)) 42 return p.handleHTTPError(ctx, w, r, rr, http.StatusForbidden) 43 } 44 } 45 46 var assetPath string 47 switch { 48 case appName == "profile": 49 assetPath = strings.TrimPrefix(r.URL.Path, rr.Upstream.BasePath) 50 if !strings.HasPrefix(assetPath, "profile/") { 51 assetPath = appName + "/" + assetPath 52 } 53 default: 54 p.logger.Debug("asset download for unsupported app", zap.String("app_name", appName), zap.String("app_file_url_path", r.URL.Path)) 55 return p.handleHTTPRenderError(ctx, w, r, rr, fmt.Errorf("file not found")) 56 } 57 58 p.logRequest(appName+" app assets", r, rr) 59 asset, err := ui.AppAssets.GetAsset(assetPath) 60 if err != nil { 61 if strings.HasSuffix(assetPath, "/") || strings.Count(assetPath, "/") >= 3 || strings.HasSuffix(assetPath, "/new") { 62 asset, err = ui.AppAssets.GetAsset(appName + "/") 63 if err != nil { 64 p.logger.Debug("app asset download not found", zap.String("app_name", appName), zap.String("app_file_url_path", r.URL.Path), zap.String("asset_path", assetPath)) 65 return p.handleHTTPError(ctx, w, r, rr, http.StatusNotFound) 66 } 67 } else { 68 p.logger.Debug("app asset download not found", zap.String("app_name", appName), zap.String("app_file_url_path", r.URL.Path), zap.String("asset_path", assetPath)) 69 return p.handleHTTPError(ctx, w, r, rr, http.StatusNotFound) 70 } 71 } 72 73 w.Header().Set("Content-Type", asset.ContentType) 74 w.Header().Set("Etag", asset.Checksum) 75 w.Header().Set("Cache-Control", "max-age=7200") 76 if match := r.Header.Get("If-None-Match"); match != "" { 77 if strings.Contains(match, asset.Checksum) { 78 w.WriteHeader(http.StatusNotModified) 79 return nil 80 } 81 } 82 w.WriteHeader(http.StatusOK) 83 io.WriteString(w, asset.Content) 84 return nil 85 }