基于用户发布的字段值的权限

时间:2019-04-20 19:49:37

标签: python django django-rest-framework

我的模型的简化视图:

# models.py 

class User(models.Model):
    first_name = models.CharField()
    last_name = models.CharField()
    team = models.ForeignKey('Team')
    ...

class Team(models.Model):
    name = models.CharField()

class ToDo(models.Model):
    task = models.CharField()
    description = models.TextField()
    owner = models.ForeignKey('User')

# serializers.py

class ToDoSerializer(serializers.ModelSerializer):
    id = serializers.ReadOnlyField()

    class Meta:
        model = ToDo
        fields = '__all__'

我要基于以下逻辑创建一个POST端点以添加一个新的ToDo对象:

  • 用户可以自己创建ToDo个项目
  • 用户可以为其团队中的其他人创建ToDo项目
  • 用户无法为不在团队中的其他人创建ToDo项目

问题:在哪里写这个逻辑

我通过使用Permission类尝试过此操作,但我不知道这是否是执行此操作的最佳位置

# views.py

class ToDoViewSet(viewsets.ModelViewSet):
    serializer_class = ToDoSerializer
    permission_classes = (CanAddToDo,)



# permissions.py

class CanAddToDo(BasePermission):
    def has_permission(self, request, view):
        owner_id = request.data.get('owner', None)

        # owner_id must be set
        if not owner_id:
            return False

        # User can create items if owner is themselves or someone in their team
        if User.objects.get(pk=owner_id).team == request.user.team:
            return True

        return False

    def has_object_permission(self, request, view, obj):
        """
        Checks if the user owns the todo to edit
        """
        return obj.owner == request.user

令我烦恼的是,我没有使用序列化的数据,而是从请求中获取原始所有者的ID,并在权限对象中进行查询以进行我的验证/许可

其他选择可能是在视图的def perform_create(self, serializer):函数或序列化程序自身中进行此验证。

1 个答案:

答案 0 :(得分:1)

还有另一种看待这个问题的方法;所有者验证

在您的ToDo序列化程序中,您可以为所有者字段编写验证

import java.awt.print.PrinterJob;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.print.DocFlavor;
import javax.print.PrintService;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaTray;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.woo.htmltopdf.HtmlToPdf;
import io.woo.htmltopdf.HtmlToPdfException;
import io.woo.htmltopdf.HtmlToPdfObject;
import io.woo.htmltopdf.PdfPageSize;

public class PDFServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private static final Logger myLog = LoggerFactory.getLogger(PDFServlet.class);
    private static final String PDF_CONTENT = "application/pdf";
    private static final String JSON_CONTENT = "application/json; charset=UTF-8";
    private static final String DOWNLOAD_FILE = "attachment;filename=%s.pdf";
    private static final String REQUEST_STATUS = "[{\"success\":%s}]";
    private static final String ERROR_MSG = "Error with %s request: ";

    private Map<String, File> myRenderMap = new HashMap<String, File>();
    private File myRenderDir;

    @Override
    public void init(ServletConfig config) throws ServletException {

        super.init(config);
        ServletContext servletContext = this.getServletContext();
        String appRoot = servletContext.getRealPath("/");
        myRenderDir = new File(appRoot + "render");
        if (myRenderDir.exists()) 
        {
            try 
            {
                FileUtils.cleanDirectory(myRenderDir);
            } 
            catch (IOException e) 
            {
                myLog.error("Unable to clean render directory.", e);
            }
        }
        else
        {
            myRenderDir.mkdirs();
        }
        myLog.debug("Initialized PDFServlet");

    }

    // downloads to client (or prints) a pre-rendered pdf
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException  {       

        try
        {
            String resourceId = request.getParameter("resourceId");
            String printer = request.getParameter("printer");
            String tray = request.getParameter("tray");
            String duplex = request.getParameter("duplex");
            String staple = request.getParameter("staple");

            if (resourceId == null || resourceId.equals("*") || resourceId.equals("resources"))
            {
                myLog.debug("Retrieving resources list for GET request: "
                        + request.getRequestURI()
                        + (request.getQueryString() != null ? "?" + request.getQueryString() : ""));

                response.setContentType(JSON_CONTENT);
                response.setStatus(200);
                try (PrintWriter out = response.getWriter())
                {
                    out.println(getResources());
                }
            }
            else if (resourceId.equalsIgnoreCase("printers"))
            {
                myLog.debug("Retrieving printers list for GET request: "
                        + request.getRequestURI()
                        + (request.getQueryString() != null ? "?" + request.getQueryString() : ""));

                response.setContentType(JSON_CONTENT);
                response.setStatus(200);
                try (PrintWriter out = response.getWriter())
                {
                    out.println(getPrinters());
                }
            }
            else if (myRenderMap.containsKey(resourceId))
            {
                myLog.debug("Retrieving pdf resource for GET request: "
                        + request.getRequestURI()
                        + (request.getQueryString() != null ? "?" + request.getQueryString() : ""));

                while (myRenderMap.containsKey(resourceId) && myRenderMap.get(resourceId) == null)
                {
                    Thread.sleep(100);
                }

                File file = myRenderMap.get(resourceId);

                // print
                if (printer != null)
                {
                    myLog.debug("Printing existing resource: " + resourceId);
                    byte[] document = Files.readAllBytes(file.toPath());
                    boolean printed = print(document, printer, resourceId, tray, duplex, staple);
                    response.setContentType(JSON_CONTENT);
                    response.setStatus(printed ? 200 : 500);
                    try (PrintWriter out = response.getWriter())
                    {
                        out.println(String.format(REQUEST_STATUS, String.valueOf(printed)));
                    }
                }

                // download
                else
                {
                    myLog.debug("Downloading existing resource: " + resourceId);
                    try (InputStream in = FileUtils.openInputStream(file)) 
                    {
                        response.setContentType(PDF_CONTENT);
                        response.setHeader("Content-Disposition", String.format(DOWNLOAD_FILE, resourceId));
                        ServletOutputStream out = response.getOutputStream();
                        IOUtils.copy(in, out);
                    } 
                    catch (HtmlToPdfException e) 
                    {
                        myLog.error("HTML to PDF conversion error.", e);
                    }
                }
            }
            else
            {
                myLog.debug("Resource does not exist for GET request: "
                        + request.getRequestURI()
                        + (request.getQueryString() != null ? "?" + request.getQueryString() : ""));

                response.setContentType(JSON_CONTENT);
                response.setStatus(500);
                try (PrintWriter out = response.getWriter())
                {
                    out.println(String.format(REQUEST_STATUS, "false"));
                }
            }
        }
        catch (Exception e)
        {
            String msg = String.format(ERROR_MSG, "GET") 
                    + request.getRequestURI() 
                    + (request.getQueryString() != null ? "?" + request.getQueryString() : "");

            myLog.error(msg, e);
            throw new ServletException(msg, e);
        }
    }

    // downloads to client (or prints) a pdf that is created from the body content
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException  {

        myLog.debug("Retrieving pdf resource for POST request: "
                + request.getRequestURI()
                + (request.getQueryString() != null ? "?" + request.getQueryString() : ""));

        try
        {
            String resourceId = request.getParameter("resourceId");
            String size = request.getParameter("size");
            String printer = request.getParameter("printer");
            String tray = request.getParameter("tray");
            String duplex = request.getParameter("duplex");
            String staple = request.getParameter("staple");

            if (resourceId == null)
            {
                resourceId = request.getContextPath().substring(1);
            }

            // resource is currently being rendered
            if (myRenderMap.containsKey(resourceId) && myRenderMap.get(resourceId) == null)
            {
                myLog.debug("Waiting for rendering: " + resourceId);
                while (myRenderMap.containsKey(resourceId) && myRenderMap.get(resourceId) == null)
                {
                    Thread.sleep(100);
                }
            }

            // resource is not already rendered
            if (!myRenderMap.containsKey(resourceId))
            {
                myLog.debug("Newly rendering resource: " + resourceId);
                String[] content = IOUtils.toString(request.getReader()).split("\\|", -1);   
                HtmlToPdf html = create(content);

                html.documentTitle(resourceId);

                if (size != null)
                {
                    html.pageSize(PdfPageSize.valueOf(size));
                }
                else
                {
                    html.pageSize(PdfPageSize.Letter);
                }

                // print
                if (printer != null)
                {
                    myLog.debug("Printing new resource: " + resourceId);
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    IOUtils.copy(html.convert(), baos);
                    byte[] document = baos.toByteArray();
                    boolean printed = print(document, printer, resourceId, tray, duplex, staple);
                    response.setContentType(JSON_CONTENT);
                    response.setStatus(printed ? 200 : 500);
                    try (PrintWriter out = response.getWriter())
                    {
                        out.println(String.format(REQUEST_STATUS, String.valueOf(printed)));
                    }
                }

                // download
                else
                {
                    myLog.debug("Downloading new resource: " + resourceId);
                    try (InputStream in = html.convert()) 
                    {
                        response.setContentType(PDF_CONTENT);
                        response.setHeader("Content-Disposition", String.format(DOWNLOAD_FILE, resourceId));
                        ServletOutputStream out = response.getOutputStream();
                        IOUtils.copy(in, out);
                    } 
                    catch (HtmlToPdfException e) 
                    {
                        myLog.error("HTML to PDF conversion error.", e);
                    }
                }
            }

            // resource is already rendered (use file)
            else
            {
                myLog.debug("Resource already exists: " + resourceId);
                File file = myRenderMap.get(resourceId);

                // print
                if (printer != null)
                {
                    myLog.debug("Printing existing resource: " + resourceId);
                    byte[] document = Files.readAllBytes(file.toPath());
                    boolean printed = print(document, printer, resourceId, tray, duplex, staple);
                    response.setContentType(JSON_CONTENT);
                    response.setStatus(printed ? 200 : 500);
                    try (PrintWriter out = response.getWriter())
                    {
                        out.println(String.format(REQUEST_STATUS, String.valueOf(printed)));
                    }
                }

                // download
                else
                {
                    myLog.debug("Downloading existing resource: " + resourceId);
                    try (InputStream in = FileUtils.openInputStream(file)) 
                    {
                        response.setContentType(PDF_CONTENT);
                        response.setHeader("Content-Disposition", String.format(DOWNLOAD_FILE, resourceId));
                        ServletOutputStream out = response.getOutputStream();
                        IOUtils.copy(in, out);
                    } 
                    catch (Exception e) 
                    {
                        myLog.error("Unable to download existing resource.", e);
                    }
                }
            }
        }
        catch (Exception e)
        {
            String msg = String.format(ERROR_MSG, "POST") 
                    + request.getRequestURI() 
                    + (request.getQueryString() != null ? "?" + request.getQueryString() : "");

            myLog.error(msg, e);
            throw new ServletException(msg, e);
        }
    }

    // pre-renders a pdf that is created from the body content
    @Override
    public void doPut(HttpServletRequest request, HttpServletResponse response)
            throws ServletException  {       

        myLog.debug("Rendering pdf resource for PUT request: "
                + request.getRequestURI()
                + (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
        try
        {
            String resourceId = request.getParameter("resourceId");
            String size = request.getParameter("size");
            boolean created = false;

            if (resourceId == null)
            {
                resourceId = request.getContextPath().substring(1);
            }

            if (!myRenderMap.containsKey(resourceId))
            {
                myRenderMap.put(resourceId, null);
                String filePath = myRenderDir.getAbsolutePath() + "/" + resourceId + ".pdf";
                String[] content = IOUtils.toString(request.getReader()).split("\\|", -1);  
                HtmlToPdf html = create(content);

                html.documentTitle(resourceId);

                if (size != null)
                {
                    html.pageSize(PdfPageSize.valueOf(size));
                }
                else
                {
                    html.pageSize(PdfPageSize.Letter);
                }
                created = html.convert(filePath);

                if (created)
                {
                    myRenderMap.put(resourceId, new File(filePath));
                }
                else
                {
                    myRenderMap.remove(resourceId);
                }
            }
            else
            {
                while (myRenderMap.get(resourceId) == null && myRenderMap.containsKey(resourceId))
                {
                    Thread.sleep(100);
                }
                created = myRenderMap.containsKey(resourceId);
            }

            response.setContentType(JSON_CONTENT);
            response.setStatus(created ? 200 : 500);
            try (PrintWriter out = response.getWriter())
            {
                out.println(String.format(REQUEST_STATUS, String.valueOf(created)));
            }
        }
        catch (Exception e)
        {
            String msg = String.format(ERROR_MSG, "PUT") 
                    + request.getRequestURI() 
                    + (request.getQueryString() != null ? "?" + request.getQueryString() : "");

            myLog.error(msg, e);
            throw new ServletException(msg, e);
        }
    }

    // removes pre-rendered pdf from server files
    @Override
    public void doDelete(HttpServletRequest request, HttpServletResponse response)
            throws ServletException  {    

        myLog.debug("Removing pdf resource for DELETE request: "
                + request.getRequestURI()
                + (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
        try
        {
            String resourceId = request.getParameter("resourceId");
            List<String> toRemove = new ArrayList<String>();
            long now = Calendar.getInstance().getTimeInMillis();
            for (String key : myRenderMap.keySet())
            {
                myLog.debug("Checking: " + key);
                File file = myRenderMap.get(key);
                if (file == null)
                {
                    Thread.sleep(10000); // wait 10 seconds to see if it renders
                }

                if (file == null || // the file doesn't actually exist
                    now - file.lastModified() > (1000 * 60 * 30) || // delete anything older than 30 minutes
                    key.equals(resourceId)) // requested to delete
                {
                    toRemove.add(key);
                }
            }

            myLog.debug("Removing: " + Arrays.toString(toRemove.toArray()));
            for (String key : toRemove)
            {
                myRenderMap.get(key).delete();
                myRenderMap.remove(key);
            }

            response.setContentType(JSON_CONTENT);
            response.setStatus(200);
            try (PrintWriter out = response.getWriter())
            {
                out.println(String.format(REQUEST_STATUS, "true"));
            }
        }
        catch (Exception e)
        {
            String msg = String.format(ERROR_MSG, "DELETE") 
                    + request.getRequestURI() 
                    + (request.getQueryString() != null ? "?" + request.getQueryString() : "");

            myLog.error(msg, e);
            throw new ServletException(msg, e);
        }
    }


    private HtmlToPdf create(String[] content)
    {
        HtmlToPdf html = HtmlToPdf.create();

        for (String document : content)
        {
            if (document.startsWith("<") && document.endsWith(">"))
            {
                html.object(HtmlToPdfObject.forHtml(document));
            }
            else
            {
                html.object(HtmlToPdfObject.forUrl(document));
            }
        }
        return html;
    }

    private boolean print(byte[] document, String printerIpAddress, String title, String tray, String duplex, String staple)
    {
        boolean success = false;
        List<byte[]> commands = new ArrayList<byte[]>();

        if (title != null)
        {
            commands.add(("@PJL SET JOBNAME=" + title + "\n").getBytes());
        }

        if (tray != null)
        {
            commands.add(("@PJL SET MEDIASOURCE=" + tray + "\n").getBytes());
        }

        if (staple != null)
        { 
            commands.add(("@PJL SET STAPLEOPTION=ONE\n").getBytes());
        }

        if (duplex != null)
        {
            commands.add(("@PJL SET DUPLEX=ON\n").getBytes());
            if (duplex.equalsIgnoreCase("short"))
            {
                commands.add(("@PJL SET BINDING=SHORTEDGE\n").getBytes());
            }
            else if (duplex.equalsIgnoreCase("long"))
            {
                commands.add(("@PJL SET BINDING=LONGEDGE\n").getBytes());
            }
        }

        try (Socket socket = new Socket(printerIpAddress, 9100))
        {
            DataOutputStream out = new DataOutputStream(socket.getOutputStream());

            out.write(27);
            out.write("%-12345X@PJL\n".getBytes());
            for (byte[] command : commands)
            {
                out.write(command);
            }
            out.write("@PJL ENTER LANGUAGE=PDF\n".getBytes());
            out.write(document);
            out.write(27);
            out.write("%-12345X".getBytes());
            out.flush();
            out.close();

            success = true;
        }
        catch (Exception e)
        {
            System.out.println(e);
        }
        return success;
    }

    private String getResources()
    {
        List<String> toRemove = new ArrayList<String>();
        StringBuilder json = new StringBuilder("[");
        long now = Calendar.getInstance().getTimeInMillis();
        for (String key : myRenderMap.keySet())
        {
            File file = myRenderMap.get(key);
            if (file != null)
            {
                if (now - file.lastModified() > (1000 * 60 * 30)) // delete anything older than 30 minutes
                {
                    toRemove.add(key);
                }
                else
                {
                    json.append("{\"resourceId\":\"");
                    json.append(key);
                    json.append("\"},");
                }
            }
        }

        if (json.length() > 1)
        {
            json.setLength(json.length() - 1);
        }
        json.append("]");

        for (String key : toRemove)
        {
            myRenderMap.get(key).delete();
            myRenderMap.remove(key);
        }

        return json.toString();
    }

    private String getPrinters()
    {
        StringBuilder json = new StringBuilder("[");
        PrintService[] services = PrinterJob.lookupPrintServices();
        DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;

        for (PrintService service : services)
        {
            json.append("{\"printer\":\"");
            json.append(service.getName());
            json.append("\",\"trays\":[");

            Object o = service.getSupportedAttributeValues(Media.class, flavor, null);
            if (o != null && o.getClass().isArray())
            {
                for (Media media : (Media[]) o)
                {
                    if (media instanceof MediaTray)
                    {
                        json.append("\"");
                        json.append(media.toString());
                        json.append("\",");
                    }
                }

                if (json.charAt(json.length() - 1) == ',')
                {
                    json.setLength(json.length() - 1);
                }
            }
            json.append("]},");
        }

        if (json.length() > 1)
        {
            json.setLength(json.length() - 1);
        }
        json.append("]");

        return json.toString();
    }
}

这很适合创建。但是,要进行更新或删除,您需要检查todo对象上的当前所有者。可以在权限类中完成。您可以使用请求网址中的class ToDoSerializer(serializers.ModelSerializer): id = serializers.ReadOnlyField() class Meta: model = ToDo fields = '__all__' def validate_owner(self, val): owner = val request = self.context.get('request', None) # it is possible you are using serializer outside an api view # in which case reqeust will not be present in the serializer context if request: if owner.team != request.user.team: raise serializers.ValidationError('you can only create todos for' 'yourself or your team members') return val 来获取权限类中的todo对象。