// Both everyone and personal. func (v *V) TypeConfig() error { uni := v.uni typ := uni.Req.Form["type"][0] op, ok := jsonp.Get(uni.Opt, "Modules.content.types."+typ) if !ok { return fmt.Errorf("Can not find content type " + typ + " in options.") } uni.Dat["type"] = typ uni.Dat["type_options"], _ = json.MarshalIndent(op, "", " ") uni.Dat["op"] = op user_type_op, _ := jsonp.Get(uni.Dat["_user"], "content_options."+typ) uni.Dat["user_type_op"] = user_type_op return nil }
func AD(uni *context.Uni) error { defer adErr(uni) var err error if lev, k := jsonp.Get(uni.Dat, "_user.level"); k == false || lev.(int) < 300 { if admin_model.SiteHasAdmin(uni.Db) { uni.Dat["_points"] = []string{"admin/login"} } else { uni.Dat["_points"] = []string{"admin/regfirstadmin"} } return nil } m, cerr := routep.Comp("/admin/{modname}", uni.P) if cerr != nil { // It should be always nil anyway. return fmt.Errorf("Control is routed to Admin display, but it does not like the url structure.") } modname, _ := m["modname"] switch modname { case "": err = Index(uni) case "edit-config": err = EditConfig(uni) case "install": err = Install(uni) case "uninstall": err = Uninstall(uni) default: _, installed := jsonp.Get(uni.Opt, "Modules."+modname) if !installed { err = fmt.Errorf("There is no module named ", modname, " installed.") } var viewname string if len(uni.Paths) < 4 { viewname = "index" } else { viewname = uni.Paths[3] } uni.Caller.Call("views", modname, "AdminInit", nil) sanitized_viewname := Viewnameize(viewname) if !uni.Caller.Has("views", modname, sanitized_viewname) { err = fmt.Errorf("Module %v has no view named %v.", modname, sanitized_viewname) } ret_rec := func(e error) { err = e } uni.Dat["_points"] = []string{modname + "/" + viewname} uni.Caller.Call("views", modname, sanitized_viewname, ret_rec) } return err }
func (v *V) Index() error { uni := v.uni var search string if s, hass := uni.Req.Form["point-name"]; hass { search = s[0] } points, ok := jsonp.Get(uni.Opt, "Display-points") if !ok { // return fmt.Errorf("There is no \"Display-points\" field in the option document.") // Rather than freezing we easily recover here below. points = map[string]interface{}{} } // TODO: clean up here and make it more straightforward. points_m := points.(map[string]interface{}) has_points := false ps := []string{} if ok { for key, _ := range points_m { if search == "" || strings.Index(key, search) != -1 { ps = append(ps, key) } has_points = true } } uni.Dat["has_points"] = has_points sort.Strings(ps) uni.Dat["point_names"] = ps uni.Dat["search"] = search uni.Dat["_points"] = []string{"display_editor/index"} return nil }
// Return values: content type, general (fatal) error, puzzle error // Puzzle error is returned to support the decision of wether to put the comment into a moderation queue. func (a *A) allowsComment(op string) (string, error, error) { uni := a.uni inp := uni.Req.Form user_level := scut.Ulev(uni.Dat["_user"]) content_id := bson.ObjectIdHex(inp["content_id"][0]) typ, err := content_model.TypeOf(uni.Db, content_id) if err != nil { return "", err, nil } auth_opts, ignore := user.AuthOpts(uni, "content.types."+typ, op+"_comment") if ignore { return "", fmt.Errorf("Auth options should not be ignored."), nil } err, puzzle_err := user.AuthAction(uni, auth_opts) if err != nil { return "", err, nil } var user_id bson.ObjectId user_id_i, has := jsonp.Get(uni.Dat, "_user._id") // At this point the user will have a user id. TODO: except when the auth_opts is misconfigured. if !has { return "", fmt.Errorf("User has no id."), nil } if has { user_id = user_id_i.(bson.ObjectId) } if op != "insert" { err = content_model.CanModifyComment(uni.Db, inp, 300, user_id, user_level) // TODO: remove hard-coded value. } return typ, err, puzzle_err }
// Update content. // TODO: Consider separating the shared processes of Insert/Update (type and rule checking, extracting) func (a *A) update() error { uni := a.uni uid, typ, prep_err := a.allowsContent("insert") if prep_err != nil { return prep_err } rule, hasrule := jsonp.Get(uni.Opt, "Modules.content.types."+typ+".rules") if !hasrule { return fmt.Errorf("Can't find content type rules " + typ) } err := content_model.Update(uni.Db, uni.Ev, rule.(map[string]interface{}), uni.Req.Form, uid) if err != nil { return err } draft_id, has_draft_id := uni.Req.Form[content_model.Parent_draft_field] content_id := uni.Req.Form["id"][0] if has_draft_id && len(draft_id[0]) > 0 { // Coming from draft. // We must set redirect because it can come from draft edit too. uni.Dat["_cont"] = map[string]interface{}{ "!type": typ, "!id": content_id, } } else { uni.Dat["_cont"] = map[string]interface{}{ "!type": typ, } } return nil }
// Searches all {{load modname/filename.ext}} and replaces that with the proper requires then calls the require module on the file. func Load(opt_i interface{}, root string, file []byte, get func(string, string) ([]byte, error)) ([]byte, error) { r := regexp.MustCompile(beg + "([a-zA-Z_.:/-])*" + end) s := r.FindAllString(string(file), -1) cut_beg := len(beg) cut_end := len(end) for _, v := range s { replacement := []byte{} load_name := v[cut_beg : len(v)-cut_end] loads, has := jsonp.Get(opt_i, load_name) if has { req_paths := jsonp.ToStringSlice(loads.([]interface{})) for _, x := range req_paths { replacement = append(replacement, []byte(fmt.Sprintf("{{require %v}}", x))...) } } if len(replacement) > 0 { str, err := require.RMem(root, replacement, get) if err != nil { return nil, err } replacement = []byte(str) } file = r.ReplaceAll(file, replacement) } return file, nil }
func eval_rec(i interface{}, vars map[string]interface{}, nested bool) interface{} { switch val := i.(type) { case string: if string(val[0]) == Marker { if string(val[1]) == "&" { // Reference if nested { panic("Can't interpret reference in map or slice.") } else { return Ref{vars, string(val[2:])} } } else { // Variable val, _ := jsonp.Get(vars, val[1:]) return val } } else { return i } case map[string]interface{}: for i, v := range val { val[i] = eval_rec(v, vars, true) } case []interface{}: for i, v := range val { val[i] = eval_rec(v, vars, true) } default: return i } return i }
func (h *H) contentView(content_map map[string]string) (error, bool) { uni := h.uni types, ok := jsonp.Get(uni.Opt, "Modules.content.types") if !ok { return fmt.Errorf("No content types."), false } slug_keymap := map[string]struct{}{} for _, v := range types.(map[string]interface{}) { type_conf := v.(map[string]interface{}) if slugval, has := type_conf["accessed_by"]; has { slug_keymap[slugval.(string)] = struct{}{} } else { slug_keymap["slug"] = struct{}{} } } slug_keys := []string{} for i, _ := range slug_keymap { slug_keys = append(slug_keys, i) } content, found := content_model.FindContent(uni.Db, slug_keys, content_map["slug"]) if !found { return nil, false } dont_query := map[string]interface{}{"password": 0} resolver.ResolveOne(uni.Db, content, dont_query) uni.Dat["_points"] = []string{"content"} uni.Dat["content"] = content return nil, true }
func (r Ref) DereferStrict() interface{} { val, ok := jsonp.Get(r.vars, r.name) if !ok { panic(r.name + " is undefined.") } return val }
func (a *A) allowsContent(op string) (bson.ObjectId, string, error) { uni := a.uni var typ string if op == "insert" { typ = uni.Req.Form["type"][0] // See TODO below. } else { content_id := patterns.ToIdWithCare(uni.Req.Form["id"][0]) // TODO: Don't let it panic if id does not exists, return descriptive error message. _typ, err := content_model.TypeOf(uni.Db, content_id) if err != nil { return "", "", err } typ = _typ } auth_opts, ignore := user.AuthOpts(uni, "content.types."+typ, op) if ignore { return "", "", fmt.Errorf("Auth options should not be ignored.") } err, _ := user.AuthAction(uni, auth_opts) if err != nil { return "", "", err } uid_i, has_uid := jsonp.Get(uni.Dat, "_user._id") if !has_uid { return "", "", fmt.Errorf("Can't %v content, you have no id.", op) } uid := uid_i.(bson.ObjectId) user_level := scut.Ulev(uni.Dat["_user"]) allowed_err := content_model.CanModifyContent(uni.Db, uni.Req.Form, 300, uid, user_level) if allowed_err != nil { return "", "", allowed_err } return uid, typ, nil }
func musth(a string, b map[string]interface{}) error { _, has := jsonp.Get(b, a) if !has { return fmt.Errorf("Map has no key \"%v\". Terminating.", a) } return nil }
// Displays a display point. func D(uni *context.Uni) { points, points_exist := uni.Dat["_points"] var point string if points_exist { point = points.([]string)[0] } else { p := uni.Req.URL.Path if p == "/" { point = "index" } else { point = p } } queries, queries_exists := jsonp.Get(uni.Opt, "Display-points."+point+".queries") if queries_exists { qmap, ok := queries.(map[string]interface{}) if ok { runQueries(uni, qmap) } } BeforeDisplay(uni) // While it is not the cheapest solution to convert bson.ObjectIds to strings here, where we have to iterate trough all values, // it is still better than remembering (and forgetting) to convert it at every specific place. scut.IdsToStrings(uni.Dat) langs, _ := jsonp.Get(uni.Dat, "_user.languages") // _user always has language member langs_s := toStringSlice(langs) loc, _ := display_model.LoadLocStrings(uni.Dat, langs_s, uni.Root, scut.GetTPath(uni.Opt, uni.Req.Host), nil) // TODO: think about errors here. if loc != nil { uni.Dat["loc"] = loc } if _, isjson := uni.Req.Form["json"]; isjson { putJSON(uni) return } else { err := DisplayFile(uni, point) if err != nil { uni.Dat["missing_file"] = point err_404 := DisplayFile(uni, "404") if err_404 != nil { uni.Put("Cant find file: ", point) } } } }
// A very basic framework to provide an easy way to do action based authorization (currently checks user levels and puzzles). // Hopefully this will solve the common security problem of forgetting to check the user's rights in modules, // since everything is blacklisted by default (needs admin rights). // // Example: // "Modules.%v.actions.%v.auth" : { // "min_lev": 0, // Defaults to 300. 0 Means somebody who has a user level >= min_lev can do it. // "no_puzzles_lev": 2 // Defaults to 2. Means someone who has a user level >= no_puzzles_lev will not have to solve the spam protection puzzle. // "puzzles": ["timer"] // Defaults to defaultPuzzles(uni). // "hot_reg": 2 // More precisely: "reg, login, build". // // Defaults to 0. Specifies wether to register, login and build a guest user. // // 0 means don't register at all. 1 means register if he solved the puzzles. 2 register even if he failed the puzzles (useful for moderation). // } // // A value of false means proceed as passed. This is useful when the rights to an action can not be determined by only // from the module and action name. A good example is the content module. An action of "insert", or "comment_insert" can belong // to different types of content, thus requiring different levels. // We can solve this problem by assigning "Modules.content.actions.insert.auth" = false // and calling this function by hand as mod_name = "content.types.blog", action_name = "insert" => "Modules.content.types.blog.actions.insert.auth" (long, I know...). // // Better workaround must exists, but currently we go on with this in the content module. // First error is general error, not meant to be ignored, second is puzzle error, which can be ignored if one wants implement moderation. func OkayToDoAction(uni *context.Uni, mod_name, action_name string) (error, error) { if _, installed := jsonp.Get(uni.Opt, "Modules."+mod_name); !installed { return fmt.Errorf(cant_run_back, action_name, mod_name), nil } auth_options, explicit_ignore := AuthOpts(uni, mod_name, action_name) if explicit_ignore { return nil, nil } return AuthAction(uni, auth_options) }
// TODO: Ugly name. func (a *A) SavePersonalTypeConfig() error { uni := a.uni return fmt.Errorf(not_impl) // Temp. user_id_i, has := jsonp.Get(uni.Dat, "_user._id") if !has { return fmt.Errorf("Can't find user id.") } user_id := user_id_i.(bson.ObjectId) return content_model.SavePersonalTypeConfig(uni.Db, uni.Req.Form, user_id) }
// opt structure: // Modules.modulename func InstallB(db *mgo.Database, ev ifaces.Event, opt map[string]interface{}, modn, mode string) (bson.ObjectId, error) { var object_id bson.ObjectId if _, already := jsonp.Get(opt, "Modules."+modn); mode == "install" && already { return object_id, fmt.Errorf("Module " + modn + " is already installed.") } else if mode == "uninstall" && !already { return object_id, fmt.Errorf("Module " + modn + " is not installed.") } object_id = basic.CreateOptCopy(db) return object_id, nil }
func (v *V) Config() error { uni := v.uni op, _ := jsonp.Get(uni.Opt, "Modules.content") marsh, err := json.MarshalIndent(op, "", " ") if err != nil { return fmt.Errorf("Can't marshal content options.") } uni.Dat["content_options"] = string(marsh) return nil }
func (h *HighLev) ownLev() (int, bool) { verbL, ok := jsonp.Get(h.nouns, fmt.Sprintf("%v.verbs.%v.ownLevel", h.desc.Sentence.Noun, h.desc.Sentence.Verb)) if ok { return numcon.IntP(verbL), true } nounL, ok := jsonp.GetM(h.nouns, fmt.Sprintf("%v.ownLevel", h.desc.Sentence.Noun)) if ok { return numcon.IntP(nounL), true } return 0, false }
func (v *V) getSidebar() []string { uni := v.uni menu := []string{} types, has := jsonp.Get(uni.Opt, "Modules.content.types") if !has { panic("There are no content types.") } for i, _ := range types.(map[string]interface{}) { menu = append(menu, i) } return menu }
func (a *A) DeleteComment() error { uni := a.uni _, allow_err, _ := a.allowsComment("delete") if allow_err != nil { return allow_err } uid, has_uid := jsonp.Get(uni.Dat, "_user._id") if !has_uid { return fmt.Errorf("Can't delete comment, you have no id.") } return content_model.DeleteComment(uni.Db, uni.Ev, uni.Req.Form, uid.(bson.ObjectId)) }
func (r Ref) Set(a interface{}) { // TODO: this will not handle []s, set should be implemented in opesun/jsonp. sl := strings.Split(r.name, ".") l := len(sl) if l == 1 { r.vars[r.name] = a } else { // TODO: This is not generic yet! val, _ := jsonp.Get(r.vars, strings.Join(sl[:l-1], ".")) name := sl[l-1:][0] val.(map[string]interface{})[name] = val } }
// Retrieves the map which drives the given authorization from the option document. func AuthOpts(uni *context.Uni, mod_name, action_name string) (auth_opts map[string]interface{}, explicit_ignore bool) { val, has := jsonp.Get(uni.Opt, fmt.Sprintf("Modules.%v.actions.%v.auth", mod_name, action_name)) if !has { return authDefaults(uni, nil), false } boolval, isbool := val.(bool) if isbool && boolval == false { return nil, true } auth_opts, ok := val.(map[string]interface{}) if !ok { return authDefaults(uni, nil), false } return authDefaults(uni, auth_opts), false }
func get(dat map[string]interface{}, s ...string) interface{} { if len(s) > 0 { if len(s[0]) > 0 { if string(s[0][0]) == "$" { s[0] = s[0][1:] } } } access := strings.Join(s, ".") val, has := jsonp.Get(dat, access) if !has { return access } return val }
func (a *A) UpdateComment() error { uni := a.uni typ, allow_err, _ := a.allowsComment("update") if allow_err != nil { return allow_err } comment_rule, hasrule := jsonp.GetM(uni.Opt, "Modules.content.types."+typ+".comment_rules") if !hasrule { return fmt.Errorf("Can't find comment rules of content type " + typ) } uid, has_uid := jsonp.Get(uni.Dat, "_user._id") if !has_uid { return fmt.Errorf("Can't update comment, you have no id.") } inp := uni.Req.Form return content_model.UpdateComment(uni.Db, uni.Ev, comment_rule, inp, uid.(bson.ObjectId)) }
func (h *HighLev) Run(db iface.Db, usr iface.User, defminlev int) ([]interface{}, error) { desc := h.desc levi, ok := jsonp.Get(h.nouns, fmt.Sprintf("%v.verbs.%v.level", desc.Sentence.Noun, desc.Sentence.Verb)) if !ok { levi = defminlev } lev, _ := numcon.Int(levi) if usr.Level() < lev { return nil, fmt.Errorf("Not allowed.") } filterCreator := func(c string, input map[string]interface{}) (iface.Filter, error) { return db.NewFilter(c, input) } inp, data, err := desc.CreateInputs(filterCreator) if err != nil { return nil, err } if data != nil { if desc.Sentence.Noun != "options" { data, err = h.validate(desc.Sentence.Noun, desc.Sentence.Verb, data) if err != nil { return nil, err } } inp = append(inp, data) } module := h.hooks.Module(desc.VerbLocation) if !module.Exists() { return nil, fmt.Errorf("Unkown module.") } ins := module.Instance() var ret []interface{} ret_rec := func(i ...interface{}) { ret = i } err = ins.Method(desc.Sentence.Verb).Call(ret_rec, inp...) if err != nil { return nil, err } return ret, nil }
// Loads localization, template functions and executes the template. func prepareAndExec(uni *context.Uni, file string) { root := uni.Root host := uni.Req.Host dat := uni.Dat opt := uni.Opt w := uni.W langs, has := jsonp.Get(dat, "_user.languages") // _user should always has languages field if !has { langs = []string{"en"} } langs_s := toStringSlice(langs) if !has { langs = []string{"en"} } loc, _ := display_model.LoadLocTempl(file, langs_s, root, scut.GetTPath(opt, host), nil) // TODO: think about errors here. dat["loc"] = merge(dat["loc"], loc) funcMap := template.FuncMap(builtins(uni)) t, _ := template.New("tpl").Funcs(funcMap).Parse(string(file)) w.Header().Set("Content-Type", "text/html; charset=utf-8") t.Execute(w, dat) // TODO: watch for errors in execution. }
// Insert content. // TODO: Move Ins, Upd, Del to other package since they can be used with all modules similar to content. func (a *A) insert() error { uni := a.uni uid, typ, prep_err := a.allowsContent("insert") if prep_err != nil { return prep_err } rule, hasrule := jsonp.Get(uni.Opt, "Modules.content.types."+typ+".rules") if !hasrule { return fmt.Errorf("Can't find content type rules " + typ) } id, err := content_model.Insert(uni.Db, uni.Ev, rule.(map[string]interface{}), uni.Req.Form, uid) if err != nil { return err } // Handling redirect. uni.Dat["_cont"] = map[string]interface{}{ "!type": typ, "!id": id.Hex(), } return nil }
func (a *A) InsertComment() error { uni := a.uni typ, allow_err, puzzle_err := a.allowsComment("insert") if allow_err != nil { return allow_err } uid, has_uid := jsonp.Get(uni.Dat, "_user._id") if !has_uid { return fmt.Errorf("You must have user id to comment.") } user_id := uid.(bson.ObjectId) comment_rule, hasrule := jsonp.GetM(uni.Opt, "Modules.content.types."+typ+".comment_rules") if !hasrule { return fmt.Errorf("Can't find comment rules of content type " + typ) } mf, has := jsonp.GetB(uni.Opt, "Modules.content.types."+typ+".moderate_comment") var moderate_first bool if (has && mf) || puzzle_err != nil { moderate_first = true uni.Dat["_cont"] = map[string]interface{}{"awaits-moderation": true} } inp := uni.Req.Form return content_model.InsertComment(uni.Db, uni.Ev, comment_rule, inp, user_id, typ, moderate_first) }
func (h *HighLev) Run(db iface.Db, usr iface.User, defminlev int) ([]interface{}, error) { desc := h.desc // Authentication. levi, ok := jsonp.Get(h.nouns, fmt.Sprintf("%v.verbs.%v.level", desc.Sentence.Noun, desc.Sentence.Verb)) if !ok { levi = defminlev } lev, _ := numcon.Int(levi) if usr.Level() < lev { return nil, fmt.Errorf("Not allowed.") } err := h.userChecks(usr) if err != nil { return nil, err } filterCreator := func(c string, input map[string]interface{}) (iface.Filter, error) { return db.NewFilter(c, input) } inp, data, err := desc.CreateInputs(filterCreator) if err != nil { return nil, err } ownLev, own := h.ownLev() if len(inp) > 0 { if f, ok := inp[0].(iface.Filter); ok { // This hook allows you to modify a filter before a verb accesses it. h.hooks.Select("TopModFilter").Fire(f) h.hooks.Select(f.Subject() + "TopModFilter").Fire(f) } if own && lev <= ownLev { if f, ok := inp[0].(iface.Filter); ok { f.AddQuery(map[string]interface{}{ "createdBy": usr.Id(), }) } } } if data != nil { if desc.Sentence.Noun != "options" { data, err = h.validate(desc.Sentence.Noun, desc.Sentence.Verb, data) if err != nil { return nil, err } } inp = append(inp, data) } module := h.hooks.Module(desc.VerbLocation) if !module.Exists() { return nil, fmt.Errorf("Unkown module.") } ins := module.Instance() var ret []interface{} ret_rec := func(i ...interface{}) { ret = i } err = ins.Method(desc.Sentence.Verb).Call(ret_rec, inp...) if err != nil { return nil, err } return ret, nil }
func (t *Top) route() error { uni := t.uni paths := strings.Split(uni.Path, "/") if t.config.ServeFiles && strings.Index(paths[len(paths)-1], ".") != -1 { t.serveFile() return nil } t.buildUser() var ret []interface{} ret_rec := func(i ...interface{}) { ret = i } nouns, ok := uni.Opt["nouns"].(map[string]interface{}) if !ok { nouns = map[string]interface{}{ "options": opt_def, } } if _, ok := nouns["options"]; !ok { nouns["options"] = opt_def } uni.FilterCreator = func(c string, input map[string]interface{}) iface.Filter { return filterCreator(uni.Db, uni.Ev, nouns, input, c) } desc, err := glue.Identify(uni.Path, nouns, convert.Mapify(uni.Req.Form)) if err != nil { display.D(uni) return nil } default_level, _ := numcon.Int(uni.Opt["default_level"]) levi, ok := jsonp.Get(uni.Opt, fmt.Sprintf("nouns.%v.verbs.%v.level", desc.Sentence.Noun, desc.Sentence.Verb)) if !ok { levi = default_level } lev, _ := numcon.Int(levi) if scut.Ulev(uni.Dat["_user"]) < lev { return fmt.Errorf("Not allowed.") } inp, data, err := desc.CreateInputs(uni.FilterCreator) if err != nil { return err } if data != nil { if desc.Sentence.Noun != "options" { data, err = t.validate(desc.Sentence.Noun, desc.Sentence.Verb, data) if err != nil { return err } } inp = append(inp, data) } uni.Route = desc.Route uni.Sentence = desc.Sentence module := t.uni.NewModule(desc.VerbLocation) if !module.Exists() { return fmt.Errorf("Unkown module.") } ins := module.Instance() ins.Method(uni.Sentence.Verb).Call(ret_rec, inp...) if uni.Req.Method == "GET" { uni.Dat["main_noun"] = desc.Sentence.Noun uni.Dat["_points"] = []string{desc.Sentence.Noun + "/" + desc.Sentence.Verb, desc.VerbLocation + "/" + desc.Sentence.Verb} t.Get(ret) } else { t.Post(ret) } return nil }
func (n *NestedData) Get(s ...string) (interface{}, bool) { p := strings.Join(s, ".") return jsonp.Get(n.d, p) }